loads of bug fixes
diff --git a/COPYING.md b/COPYING.md
index ceffdf6..dfe4882 100644
--- a/COPYING.md
+++ b/COPYING.md
@@ -1,5 +1,13 @@
 # Nucleus by Clicks
 
+
+## Contents:
+
+- [In short](in-short)
+- [How to](how-to)
+- [The legal bit](the-legal-bit)
+
+
 ## In Short:
 
 You **may**:
@@ -31,6 +39,7 @@
     "verifySecret": "if making a verify command, this is the value that checks if requests are from the website",
     "mongoUrl": "mongodb://your-mongo-ip-and-port",
     "baseUrl": "your website url, e.g. https://clicks.codes",
+    "pastebinApiKey": "your-pastebin-api-key"
 }
 ```
 
diff --git a/package.json b/package.json
index 9a9338b..0320d64 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
   "dependencies": {
     "@discordjs/builders": "^0.12.0",
     "@ungap/structured-clone": "^1.0.1",
+    "agenda": "^4.3.0",
     "body-parser": "^1.20.0",
     "discord.js": "13.8.1",
     "express": "^4.18.1",
@@ -11,9 +12,10 @@
     "json-diff": "^0.7.1",
     "mongodb": "^4.7.0",
     "node-tesseract": "^0.2.7",
+    "node-tesseract-ocr": "^2.2.1",
     "opencv.js": "^1.2.1",
+    "pastebin-api": "^5.1.1",
     "structured-clone": "^0.2.2",
-    "tesseract.js": "^2.1.5",
     "typescript": "^4.5.5",
     "unscan": "^1.1.2",
     "uuid": "^8.3.2"
diff --git a/src/api/index.ts b/src/api/index.ts
index b2edc66..c429452 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -90,7 +90,7 @@
         const data = req.body.data;
         if (secret === client.config.verifySecret) {
             console.table(data)
-            let guild = await client.guilds.fetch(client.roleMenu[code].guild); // TODO: do checks here to like max roles because people are fucking annoying and will edit the source :)
+            let guild = await client.guilds.fetch(client.roleMenu[code].guild);
             if (!guild) { return res.status(404) }
             let member = await guild.members.fetch(client.roleMenu[code].user);
             if (!member) { return res.status(404) }
diff --git a/src/automations/createModActionTicket.ts b/src/automations/createModActionTicket.ts
deleted file mode 100644
index 2ab9a46..0000000
--- a/src/automations/createModActionTicket.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import Discord, { MessageActionRow, MessageButton } from 'discord.js';
-import EmojiEmbed from '../utils/generateEmojiEmbed.js';
-import getEmojiByName from "../utils/getEmojiByName.js";
-import client from "../utils/client.js";
-
-export async function create(guild: Discord.Guild, member: Discord.User, createdBy: Discord.User, reason: string) {
-    let config = await client.database.guilds.read(guild.id);
-    // @ts-ignore
-    const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger
-    let overwrites = [{
-        id: member,
-        allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
-        type: "member"
-    }] as Discord.OverwriteResolvable[];
-    overwrites.push({
-        id: guild.roles.everyone,
-        deny: ["VIEW_CHANNEL"],
-        type: "role"
-    })
-    if (config.tickets.supportRole != null) {
-        overwrites.push({
-            id: guild.roles.cache.get(config.tickets.supportRole),
-            allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
-            type: "role"
-        })
-    }
-
-    let c;
-    try {
-        c = await guild.channels.create(member.username, {
-            type: "GUILD_TEXT",
-            topic: `${member.id} Active`,
-            parent: config.tickets.category,
-            nsfw: false,
-            permissionOverwrites: (overwrites as Discord.OverwriteResolvable[]),
-            reason: "Creating ticket"
-        })
-    } catch (e) {
-        return null
-    }
-    try {
-        await c.send(
-            {
-                content: (`<@${member.id}>` + (config.tickets.supportRole != null ? ` • <@&${config.tickets.supportRole}>` : "")),
-                allowedMentions: {
-                    users: [member.id],
-                    roles: (config.tickets.supportRole != null ? [config.tickets.supportRole] : [])
-                }
-            }
-        )
-        await c.send({ embeds: [new EmojiEmbed()
-            .setTitle("New Ticket")
-            .setDescription(
-                `Ticket created by a Moderator\n` +
-                `**Support type:** Appeal submission\n` + (reason != null ? `**Reason:**\n> ${reason}\n` : "") +
-                `**Ticket ID:** \`${c.id}\`\n` +
-                `Type \`/ticket close\` to close this ticket.`,
-            )
-            .setStatus("Success")
-            .setEmoji("GUILD.TICKET.OPEN")
-        ], components: [new MessageActionRow().addComponents([new MessageButton()
-            .setLabel("Close")
-            .setStyle("DANGER")
-            .setCustomId("closeticket")
-            .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-        ])]})
-        let data = {
-            meta:{
-                type: 'ticketCreate',
-                displayName: 'Ticket Created',
-                calculateType: true,
-                color: NucleusColors.green,
-                emoji: 'GUILD.TICKET.OPEN',
-                timestamp: new Date().getTime()
-            },
-            list: {
-                ticketFor: entry(member.id, renderUser(member)),
-                createdBy: entry(createdBy.id, renderUser(createdBy)),
-                created: entry(new Date().getTime(), renderDelta(new Date().getTime())),
-                ticketChannel: entry(c.id, renderChannel(c)),
-            },
-            hidden: {
-                guild: guild.id
-            }
-        }
-        log(data);
-    } catch (e) { console.log(e); return null }
-    return c.id
-}
-
-export async function areTicketsEnabled(guild: string) {
-    let config = await client.database.guilds.read(guild);
-    return config.tickets.enabled;
-}
\ No newline at end of file
diff --git a/src/automations/guide.ts b/src/automations/guide.ts
deleted file mode 100644
index 655d781..0000000
--- a/src/automations/guide.ts
+++ /dev/null
@@ -1,240 +0,0 @@
-import { SelectMenuOption } from '@discordjs/builders';
-import Discord, { MessageActionRow, MessageButton } from "discord.js";
-import EmojiEmbed from "../utils/generateEmojiEmbed.js";
-import getEmojiByName from "../utils/getEmojiByName.js";
-import createPageIndicator from "../utils/createPageIndicator.js";
-
-class Embed {
-    embed: Discord.MessageEmbed;
-    title: string;
-    description: string = "";
-    pageId: number = 0;
-    setEmbed(embed: Discord.MessageEmbed) { this.embed = embed; return this; }
-    setTitle(title: string) { this.title = title; return this; }
-    setDescription(description: string) { this.description = description; return this; }
-    setPageId(pageId: number) { this.pageId = pageId; return this; }
-}
-
-export default async (guild, interaction?) => {
-    let c = guild.publicUpdatesChannel ? guild.publicUpdatesChannel : guild.systemChannel;
-    c = c ? c : guild.channels.cache.find(ch => ch.type === "GUILD_TEXT" && ch.permissionsFor(guild.roles.everyone).has("SEND_MESSAGES") && ch.permissionsFor(guild.me).has("EMBED_LINKS"));
-    let pages = [
-        new Embed()
-            .setEmbed(new EmojiEmbed()
-                .setTitle("Welcome to Nucleus")
-                .setDescription(
-                    "Thanks for adding Nucleus to your server\n\n" +
-                    "On the next few pages you can find instructions on getting started, and commands you may want to set up\n\n" +
-                    "If you need support, have questions or want features, you can let us know in [Clicks](https://discord.gg/bPaNnxe)"
-                )
-                .setEmoji("NUCLEUS.LOGO")
-                .setStatus("Danger")
-            ).setTitle("Welcome").setDescription("About Nucleus").setPageId(0),
-        new Embed()
-            .setEmbed(new EmojiEmbed()
-                .setTitle("Logging")
-                .setDescription(
-                    "Nucleus can log server events and keep you informed with what content is being posted to your server.\n" +
-                    "We have 2 different types of logs, which each can be configured to send to a channel of your choice:\n" +
-                    "**General Logs:** These are events like kicks and channel changes etc.\n" +
-                    "**Warning Logs:** Warnings like NSFW avatars and spam etc. that may require action by a server staff member. " +
-                    "These go to to a separate staff notifications channel.\n\n" +
-                    "A general log channel can be set with `/settings log`\n" +
-                    "A warning log channel can be set with `/settings warnings channel`"
-                )
-                .setEmoji("NUCLEUS.LOGO")
-                .setStatus("Danger")
-            ).setTitle("Logging").setDescription("Logging, staff warning logs etc.").setPageId(1),
-        new Embed()
-            .setEmbed(new EmojiEmbed()
-                .setTitle("Moderation")
-                .setDescription(
-                    "Nucleus has a number of commands that can be used to moderate your server.\n" +
-                    "These commands are all found under `/mod`, and they include:\n" +
-                    `**${getEmojiByName("PUNISH.WARN.YELLOW")} Warn:** The user is warned (via DM) that they violated server rules.\n` +
-                    `**${getEmojiByName("PUNISH.CLEARHISTORY")} Clear:** Some messages from a user are deleted in a channel.\n` +
-                    `**${getEmojiByName("PUNISH.MUTE.YELLOW")} Mute:** The user is unable to send messages or join voice chats.\n` +
-                    `**${getEmojiByName("PUNISH.MUTE.GREEN")} Unmute:** The user is able to send messages in the server.\n` +
-                    `**${getEmojiByName("PUNISH.KICK.RED")} Kick:** The user is removed from the server.\n` +
-                    `**${getEmojiByName("PUNISH.SOFTBAN")} Softban:** Kicks the user, deleting their messages from every channel.\n` +
-                    `**${getEmojiByName("PUNISH.BAN.RED")} Ban:** The user is removed from the server, and they are unable to rejoin.\n` +
-                    `**${getEmojiByName("PUNISH.BAN.GREEN")} Unban:** The user is able to rejoin the server.`
-                )
-            .setEmoji("NUCLEUS.LOGO")
-            .setStatus("Danger")
-            ).setTitle("Moderation").setDescription("Basic moderation commands").setPageId(2),
-        new Embed()
-            .setEmbed(new EmojiEmbed()
-                .setTitle("Verify")
-                .setDescription(
-                    "Nucleus has a verification system that allows users to prove they aren't bots.\n" +
-                    "This is done by running `/verify` which sends a message only the user can see, giving them a link to a CAPTCHA to verify.\n" +
-                    "After the user complete's the CAPTCHA, they are given a role and can use the permissions accordingly.\n" +
-                    "You can set the role given with `/settings verify`"
-                )
-                .setEmoji("NUCLEUS.LOGO")
-                .setStatus("Danger")
-            ).setTitle("Verify").setDescription("Captcha verification system").setPageId(3),
-        new Embed()
-            .setEmbed(new EmojiEmbed()
-                .setTitle("Content Scanning")
-                .setDescription(
-                    "Nucleus has a content scanning system that automatically scans links and images sent by users.\n" +
-                    "Nucleus can detect, delete, and punish users for sending NSFW content, or links to scam or adult sites.\n" +
-                    "You can set the threshold for this in `/settings automation`" // TODO
-                )
-                .setEmoji("NUCLEUS.LOGO")
-                .setStatus("Danger")
-            ).setTitle("Content Scanning").setDescription("Content (NSFW, malware, scams) scanning").setPageId(4),
-        new Embed()
-            .setEmbed(new EmojiEmbed()
-                .setTitle("Tickets")
-                .setDescription(
-                    "Nucleus has a ticket system that allows users to create tickets and have a support team respond to them.\n" +
-                    "Tickets can be created with `/ticket create` and a channel is created, pinging the user and support role.\n" +
-                    "When the ticket is resolved, anyone can run `/ticket close` (or click the button) to close it.\n" +
-                    "Running `/ticket close` again will delete the ticket."
-                )
-                .setEmoji("NUCLEUS.LOGO")
-                .setStatus("Danger")
-            ).setTitle("Tickets").setDescription("Ticket system").setPageId(5),
-        new Embed()
-            .setEmbed(new EmojiEmbed()
-                .setTitle("Tags")
-                .setDescription(
-                    "Add a tag system to your server with the `/tag` and `/tags` commands.\n" +
-                    "To create a tag, type `/tags create <tag name> <tag content>`.\n" +
-                    "Tag names and content can be edited with `/tags edit`.\n" +
-                    "To delete a tag, type `/tags delete <tag name>`.\n" +
-                    "To view all tags, type `/tags list`.\n"
-                )
-                .setEmoji("NUCLEUS.LOGO")
-                .setStatus("Danger")
-            ).setTitle("Tags").setDescription("Tag system").setPageId(6)
-    ]
-    let m;
-    if (interaction) {
-        m = await interaction.reply({embeds: [
-            new EmojiEmbed()
-                .setTitle("Welcome")
-                .setDescription(`One moment...`)
-                .setStatus("Danger")
-                .setEmoji("NUCLEUS.LOADING")
-        ], fetchReply: true, ephemeral: true});
-    } else {
-        m = await c.send({embeds: [
-            new EmojiEmbed()
-                .setTitle("Welcome")
-                .setDescription(`One moment...`)
-                .setStatus("Danger")
-                .setEmoji("NUCLEUS.LOADING")
-        ], fetchReply: true });
-    }
-    let page = 0;
-
-    let f = async (component) => {
-        return (component.member as Discord.GuildMember).permissions.has("MANAGE_GUILD");
-    }
-
-    let selectPaneOpen = false;
-
-    while (true) {
-        let selectPane = []
-
-        if (selectPaneOpen) {
-            let options = [];
-            pages.forEach(embed => {
-                options.push(new SelectMenuOption({
-                    label: embed.title,
-                    value: embed.pageId.toString(),
-                    description: embed.description || "",
-                }))
-            })
-            selectPane = [new MessageActionRow().addComponents([
-                new Discord.MessageSelectMenu()
-                    .addOptions(options)
-                    .setCustomId("page")
-                    .setMaxValues(1)
-                    .setPlaceholder("Choose a page...")
-            ])]
-        }
-        let components = selectPane.concat([new MessageActionRow().addComponents([
-            new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(page === 0),
-            new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle(selectPaneOpen ? "PRIMARY" : "SECONDARY").setDisabled(false),
-            new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(page === pages.length - 1),
-            new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER")
-        ])])
-        if (interaction) {
-            let em = new Discord.MessageEmbed(pages[page].embed)
-            em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
-            await interaction.editReply({
-                embeds: [em],
-                components: components
-            });
-        } else {
-            let em = new Discord.MessageEmbed(pages[page].embed)
-            em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
-            await m.edit({
-                embeds: [em],
-                components: components,
-                fetchReply: true
-            });
-        }
-        let i
-        try {
-            i = await m.awaitMessageComponent({filter: interaction ? () => { return true } : f, time: 300000});
-        } catch(e) { break }
-        i.deferUpdate()
-        if (i.component.customId == "left") {
-            if (page > 0) page--;
-            selectPaneOpen = false;
-        } else if (i.component.customId == "right") {
-            if (page < pages.length - 1) page++;
-            selectPaneOpen = false;
-        } else if (i.component.customId == "select") {
-            selectPaneOpen = !selectPaneOpen;
-        } else if (i.component.customId == "page") {
-            page = parseInt(i.values[0]);
-            selectPaneOpen = false;
-        } else {
-            if (interaction) {
-                let em = new Discord.MessageEmbed(pages[page].embed)
-                em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page) + " | Message closed");
-                interaction.editReply({embeds: [em], components: [new MessageActionRow().addComponents([
-                    new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(true),
-                    new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle(selectPaneOpen ? "PRIMARY" : "SECONDARY").setDisabled(true),
-                    new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(true),
-                    new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER").setDisabled(true)
-                ])], fetchReply: true});
-            } else {
-                m.delete();
-            }
-            return;
-        }
-    }
-    if (interaction) {
-        let em = new Discord.MessageEmbed(pages[page])
-        em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page) + " | Message timed out");
-        await interaction.editReply({
-            embeds: [em],
-            components: [new MessageActionRow().addComponents([
-                new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(true),
-                new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle("SECONDARY").setDisabled(true),
-                new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(true),
-                new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER").setDisabled(true)
-            ])]
-        });
-    } else {
-        let em = new Discord.MessageEmbed(pages[page])
-        em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page) + " | Message timed out");
-        await m.edit({
-            embeds: [em],
-            components: [new MessageActionRow().addComponents([
-                new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setStyle("SECONDARY").setDisabled(true),
-                new MessageButton().setCustomId("select").setEmoji(getEmojiByName("CONTROL.MENU", "id")).setStyle("SECONDARY").setDisabled(true),
-                new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setStyle("SECONDARY").setDisabled(true),
-                new MessageButton().setCustomId("close").setEmoji(getEmojiByName("CONTROL.CROSS", "id")).setStyle("DANGER").setDisabled(true)
-            ])]
-        });
-    }
-}
diff --git a/src/automations/roleMenu.ts b/src/automations/roleMenu.ts
deleted file mode 100644
index 318a0dd..0000000
--- a/src/automations/roleMenu.ts
+++ /dev/null
@@ -1,152 +0,0 @@
-import { Message, MessageButton } from "discord.js";
-import EmojiEmbed from '../utils/generateEmojiEmbed.js'
-import { MessageActionRow, MessageSelectMenu } from 'discord.js';
-import getEmojiByName from "../utils/getEmojiByName.js";
-import client from "../utils/client.js";
-
-export async function callback(interaction) {
-    let config = await client.database.guilds.read(interaction.guild.id);
-    if (!config.roleMenu.enabled) return await interaction.reply({embeds: [new EmojiEmbed()
-        .setTitle("Roles")
-        .setDescription("Self roles are currently disabled. Please contact a staff member or try again later.")
-        .setStatus("Danger")
-        .setEmoji("CONTROL.BLOCKCROSS")
-    ], ephemeral: true})
-    if (config.roleMenu.options.length === 0) return await interaction.reply({embeds: [new EmojiEmbed()
-        .setTitle("Roles")
-        .setDescription("There are no roles available. Please contact a staff member or try again later.")
-        .setStatus("Danger")
-        .setEmoji("CONTROL.BLOCKCROSS")
-    ], ephemeral: true})
-    await interaction.reply({embeds: [new EmojiEmbed()
-        .setTitle("Roles")
-        .setDescription("Loading...")
-        .setStatus("Success")
-        .setEmoji("NUCLEUS.LOADING")
-    ], ephemeral: true})
-    let m;
-    if (config.roleMenu.allowWebUI) {
-        let code = ""
-        let length = 5
-        let itt = 0
-        const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-        while (true) {
-            itt += 1
-            code = ""
-            for (let i = 0; i < length; i++) { code += chars.charAt(Math.floor(Math.random() * chars.length)); }
-            if (code in client.roleMenu) continue;
-            if (itt > 1000) {
-                itt = 0
-                length += 1
-                continue;
-            }
-            break;
-        }
-        client.roleMenu[code] = {
-            guild: interaction.guild.id,
-            guildName: interaction.guild.name,
-            guildIcon: interaction.guild.iconURL({format: "png"}),
-            user: interaction.member.user.id,
-            username: interaction.member.user.username,
-            data: config.roleMenu.options,
-            interaction: interaction
-        };
-        m = await interaction.editReply({
-            embeds: [new EmojiEmbed()
-                .setTitle("Roles")
-                .setDescription("Select how to choose your roles")
-                .setStatus("Success")
-                .setEmoji("GUILD.GREEN")
-            ], components: [new MessageActionRow().addComponents([
-                new MessageButton()
-                    .setLabel("Online")
-                    .setStyle("LINK")
-                    .setDisabled(false) // TODO check if the server is up
-                    .setURL(`${client.config.baseUrl}/nucleus/rolemenu?code=${code}`),
-                new MessageButton()
-                    .setLabel("Manual")
-                    .setStyle("PRIMARY")
-                    .setCustomId("manual")
-            ])]
-        })
-    }
-    let component;
-    try { component = await (m as Message).awaitMessageComponent({time: 300000});
-    } catch (e) { return }
-    component.deferUpdate()
-    let rolesToAdd = []
-    for (let i = 0; i < config.roleMenu.options.length; i++) {
-        let object = config.roleMenu.options[i];
-        let m = await interaction.editReply({
-            embeds: [
-                new EmojiEmbed()
-                    .setTitle("Roles")
-                    .setEmoji("GUILD.GREEN")
-                    .setDescription(`**${object.name}**` + (object.description ? `\n${object.description}` : ``) +
-                        `\n\nSelect ${object.min}` + (object.min != object.max ? ` to ${object.max}` : ``) + ` role${object.max == 1 ? '' : 's'} to add.`)
-                    .setStatus("Success")
-                    .setFooter({text: `Step ${i + 1}/${config.roleMenu.options.length}`})
-            ],
-            components: [
-                new MessageActionRow().addComponents([
-                    new MessageButton()
-                        .setLabel("Cancel")
-                        .setStyle("DANGER")
-                        .setCustomId("cancel")
-                        .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-                    ].concat(object.min == 0 ? [
-                        new MessageButton()
-                            .setLabel("Skip")
-                            .setStyle("SECONDARY")
-                            .setCustomId("skip")
-                            .setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
-                        ] : []))
-            ].concat([new MessageActionRow().addComponents([new MessageSelectMenu()
-                .setPlaceholder(`${object.name}`)
-                .setCustomId("rolemenu")
-                .setMinValues(object.min)
-                .setMaxValues(object.max)
-                .setOptions(object.options.map(o => { return {label: o.name, description: o.description, value: o.role} }))
-            ])])
-        });
-        let component;
-        try {
-            component = await (m as Message).awaitMessageComponent({time: 300000});
-        } catch (e) {
-            return
-        }
-        component.deferUpdate()
-        if (component.customId == "rolemenu") {
-            rolesToAdd = rolesToAdd.concat(component.values)
-        } else if (component.customId == "cancel") {
-            return await interaction.editReply({embeds: [new EmojiEmbed()
-                .setTitle("Roles")
-                .setDescription("Cancelled. No changes were made.")
-                .setStatus("Danger")
-                .setEmoji("GUILD.RED")
-            ], components: []})
-        }
-    }
-    let rolesToRemove = config.roleMenu.options.map(o => o.options.map(o => o.role)).flat()
-    let memberRoles = interaction.member.roles.cache.map(r => r.id)
-    rolesToRemove = rolesToRemove.filter(r => memberRoles.includes(r)).filter(r => !rolesToAdd.includes(r))
-    rolesToAdd = rolesToAdd.filter(r => !memberRoles.includes(r))
-    try {
-        await interaction.member.roles.remove(rolesToRemove)
-        await interaction.member.roles.add(rolesToAdd)
-    } catch (e) {
-        return await interaction.reply({embeds: [new EmojiEmbed()
-            .setTitle("Roles")
-            .setDescription("Something went wrong and your roles were not added. Please contact a staff member or try again later.")
-            .setStatus("Danger")
-            .setEmoji("GUILD.RED")
-        ], components: []})
-    }
-    await interaction.editReply({embeds: [new EmojiEmbed()
-        .setTitle("Roles")
-        .setDescription("Roles have been added. You may close this message.")
-        .setStatus("Success")
-        .setEmoji("GUILD.GREEN")
-    ], components: []})
-    return
-}
diff --git a/src/automations/statsChannelAdd.ts b/src/automations/statsChannelAdd.ts
deleted file mode 100644
index 32de0ff..0000000
--- a/src/automations/statsChannelAdd.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
-import singleNotify from '../utils/singleNotify.js';
-import client from '../utils/client.js';
-
-export async function callback(_, member) {
-    let config = await client.database.guilds.read(member.guild.id);
-
-    config.stats.forEach(async element => {
-        if (element.enabled) {
-            let string = element.text
-            if (!string) return
-            string = await convertCurlyBracketString(string, member.id, member.displayName, member.guild.name, member.guild.members)
-            let channel;
-            try {
-                channel = await member.client.channels.fetch(element.channel)
-            } catch (error) { channel = null }
-            if (!channel) {
-                return singleNotify(
-                    "statsChannelDeleted",
-                    member.guild.id,
-                    "One or more of your stats channels have been deleted. Please open the settings menu to change this.",
-                    "Critical"
-                )
-            }
-            if (channel.guild.id !== member.guild.id) return
-            try {
-                await channel.edit({ name: string })
-            } catch (err) {
-                console.error(err)
-            }
-        }
-    });
-}
diff --git a/src/automations/statsChannelRemove.ts b/src/automations/statsChannelRemove.ts
deleted file mode 100644
index c6d4e65..0000000
--- a/src/automations/statsChannelRemove.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import client from '../utils/client.js';
-import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
-import singleNotify from '../utils/singleNotify.js';
-
-export async function callback(_, member) {
-    let config = await client.database.guilds.read(member.guild.id);
-
-    config.stats.forEach(async element => {
-        if (element.enabled) {
-            let string = element.text
-            if (!string) return
-            string = await convertCurlyBracketString(string, member.id, member.displayName, member.guild.name, member.guild.members)
-            let channel;
-            try {
-                channel = await member.client.channels.fetch(element.channel)
-            } catch { channel = null }
-            if (!channel) return singleNotify(
-                "statsChannelDeleted",
-                member.guild.id,
-                "One or more of your stats channels have been deleted. Please open the settings menu to change this.",
-                "Critical"
-            )
-            try {
-                await channel.edit({ name: string })
-            } catch (err) {
-                console.error(err)
-            }
-        }
-    });
-}
\ No newline at end of file
diff --git a/src/automations/tickets/create.ts b/src/automations/tickets/create.ts
deleted file mode 100644
index 8d58493..0000000
--- a/src/automations/tickets/create.ts
+++ /dev/null
@@ -1,206 +0,0 @@
-import Discord, { MessageActionRow, MessageButton } from "discord.js";
-import { tickets, toHexArray } from "../../utils/calculate.js";
-import client from "../../utils/client.js";
-import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
-import getEmojiByName from "../../utils/getEmojiByName.js";
-
-function capitalize(s: string) {
-    s = s.replace(/([A-Z])/g, ' $1');
-    return s.length < 3 ? s.toUpperCase() : s[0].toUpperCase() + s.slice(1).toLowerCase();
-}
-
-export default async function (interaction) {
-    const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger
-
-    let config = await client.database.guilds.read(interaction.guild.id);
-    if (!config.tickets.enabled || !config.tickets.category) {
-        return await interaction.reply({embeds: [new EmojiEmbed()
-            .setTitle("Tickets are disabled")
-            .setDescription("Please enable tickets in the configuration to use this command.")
-            .setStatus("Danger")
-            .setEmoji("CONTROL.BLOCKCROSS")
-        ], ephemeral: true});
-    }
-    let category = interaction.guild.channels.cache.get(config.tickets.category) as Discord.CategoryChannel;
-    let count = 0;
-    category.children.forEach(element => {
-        if (!(element.type == "GUILD_TEXT")) return;
-        if ((element as Discord.TextChannel).topic.includes(`${interaction.member.user.id}`)) {
-            if ((element as Discord.TextChannel).topic.endsWith("Active")) {
-                count++;
-            }
-        }
-    });
-    if (count >= config.tickets.maxTickets) {
-        return await interaction.reply({embeds: [new EmojiEmbed()
-            .setTitle("Create Ticket")
-            .setDescription(`You have reached the maximum amount of tickets (${config.tickets.maxTickets}). Please close one of your active tickets before creating a new one.`)
-            .setStatus("Danger")
-            .setEmoji("CONTROL.BLOCKCROSS")
-        ], ephemeral: true});
-    }
-    let ticketTypes;
-    let custom = false
-    if (config.tickets.customTypes && config.tickets.useCustom) { ticketTypes = config.tickets.customTypes; custom = true }
-    else if (config.tickets.types) ticketTypes = toHexArray(config.tickets.types, tickets);
-    else ticketTypes = [];
-    let chosenType;
-    let splitFormattedTicketTypes = [];
-    if (ticketTypes.length > 0) {
-        let formattedTicketTypes = [];
-        formattedTicketTypes = ticketTypes.map(type => {
-            if (custom) {
-                return new MessageButton()
-                    .setLabel(type)
-                    .setStyle("PRIMARY")
-                    .setCustomId(type)
-            } else {
-                return new MessageButton()
-                    .setLabel(capitalize(type))
-                    .setStyle("PRIMARY")
-                    .setCustomId(type)
-                    .setEmoji(getEmojiByName(("TICKETS." + type.toString().toUpperCase()), "id"));
-            }
-        });
-        for (let i = 0; i < formattedTicketTypes.length; i += 5) {
-            splitFormattedTicketTypes.push(new MessageActionRow().addComponents(formattedTicketTypes.slice(i, i + 5)));
-        }
-        let m = await interaction.reply({embeds: [new EmojiEmbed()
-            .setTitle("Create Ticket")
-            .setDescription("Select a ticket type")
-            .setStatus("Success")
-            .setEmoji("GUILD.TICKET.OPEN")
-        ], ephemeral: true, fetchReply: true, components: splitFormattedTicketTypes});
-        let component;
-        try {
-            component = await (m as Discord.Message).awaitMessageComponent({time: 300000});
-        } catch (e) {
-            return;
-        }
-        chosenType = component.customId;
-        splitFormattedTicketTypes = [];
-        formattedTicketTypes = [];
-        formattedTicketTypes = ticketTypes.map(type => {
-            if (custom) {
-                return new MessageButton()
-                    .setLabel(type)
-                    .setStyle(chosenType == type ? "SUCCESS" : "SECONDARY")
-                    .setCustomId(type)
-                    .setDisabled(true)
-                } else {
-                    return new MessageButton()
-                    .setLabel(capitalize(type))
-                    .setStyle(chosenType == type ? "SUCCESS" : "SECONDARY")
-                    .setCustomId(type)
-                    .setEmoji(getEmojiByName(("TICKETS." + type.toString().toUpperCase()), "id"))
-                    .setDisabled(true)
-            }
-        });
-        for (let i = 0; i < formattedTicketTypes.length; i += 5) {
-            splitFormattedTicketTypes.push(new MessageActionRow().addComponents(formattedTicketTypes.slice(i, i + 5)));
-        }
-        component.update({embeds: [new EmojiEmbed()
-            .setTitle("Create Ticket")
-            .setDescription("Select a ticket type")
-            .setStatus("Success")
-            .setEmoji("GUILD.TICKET.OPEN")
-        ], components: splitFormattedTicketTypes});
-    } else {
-        chosenType = null
-        await interaction.reply({embeds: [new EmojiEmbed()
-            .setTitle("Create Ticket")
-            .setEmoji("GUILD.TICKET.OPEN")
-        ], ephemeral: true, components: splitFormattedTicketTypes})
-    }
-    let overwrites = [{
-        id: interaction.member,
-        allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
-        type: "member"
-    }] as Discord.OverwriteResolvable[];
-    overwrites.push({
-        id: interaction.guild.roles.everyone,
-        deny: ["VIEW_CHANNEL"],
-        type: "role"
-    })
-    if (config.tickets.supportRole != null) {
-        overwrites.push({
-            id: interaction.guild.roles.cache.get(config.tickets.supportRole),
-            allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
-            type: "role"
-        })
-    }
-
-    let c;
-    try {
-        c = await interaction.guild.channels.create(interaction.member.user.username, {
-            type: "GUILD_TEXT",
-            topic: `${interaction.member.user.id} Active`,
-            parent: config.tickets.category,
-            nsfw: false,
-            permissionOverwrites: (overwrites as Discord.OverwriteResolvable[]),
-            reason: "Creating ticket"
-        })
-    } catch (e) {
-        return await interaction.editReply({embeds: [new EmojiEmbed()
-            .setTitle("Create Ticket")
-            .setDescription("Failed to create ticket")
-            .setStatus("Danger")
-            .setEmoji("CONTROL.BLOCKCROSS")
-        ]});
-    }
-    try {
-        await c.send(
-            {
-                content: (`<@${interaction.member.user.id}>` + (config.tickets.supportRole != null ? ` • <@&${config.tickets.supportRole}>` : "")),
-                allowedMentions: {
-                    users: [(interaction.member as Discord.GuildMember).id],
-                    roles: (config.tickets.supportRole != null ? [config.tickets.supportRole] : [])
-                }
-            }
-        )
-        let content = interaction.options ? interaction.options.getString("message") || "" : "";
-        if (content) content = `**Message:**\n> ${content}\n`;
-        let emoji = custom ? "" : getEmojiByName("TICKETS." + chosenType.toUpperCase());
-        await c.send({ embeds: [new EmojiEmbed()
-            .setTitle("New Ticket")
-            .setDescription(
-                `Ticket created by <@${interaction.member.user.id}>\n` +
-                `**Support type:** ${chosenType != null ? (emoji) + " " + capitalize(chosenType) : "General"}\n` +
-                `**Ticket ID:** \`${c.id}\`\n${content}\n` +
-                `Type \`/ticket close\` to close this ticket.`,
-            )
-            .setStatus("Success")
-            .setEmoji("GUILD.TICKET.OPEN")
-        ], components: [new MessageActionRow().addComponents([new MessageButton()
-            .setLabel("Close")
-            .setStyle("DANGER")
-            .setCustomId("closeticket")
-            .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-        ])]})
-        let data = {
-            meta:{
-                type: 'ticketCreate',
-                displayName: 'Ticket Created',
-                calculateType: true,
-                color: NucleusColors.green,
-                emoji: 'GUILD.TICKET.OPEN',
-                timestamp: new Date().getTime()
-            },
-            list: {
-                ticketFor: entry(interaction.member.user.id, renderUser(interaction.member.user)),
-                created: entry(new Date().getTime(), renderDelta(new Date().getTime())),
-                ticketChannel: entry(c.id, renderChannel(c)),
-            },
-            hidden: {
-                guild: interaction.guild.id
-            }
-        }
-        log(data);
-    } catch (e) { console.log(e)}
-    await interaction.editReply({embeds: [new EmojiEmbed()
-        .setTitle("Create Ticket")
-        .setDescription(`Ticket created. You can view it here: <#${c.id}>`)
-        .setStatus("Success")
-        .setEmoji("GUILD.TICKET.OPEN")
-    ], components: splitFormattedTicketTypes});
-}
\ No newline at end of file
diff --git a/src/automations/tickets/delete.ts b/src/automations/tickets/delete.ts
deleted file mode 100644
index 3df284b..0000000
--- a/src/automations/tickets/delete.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import Discord, { MessageButton, MessageActionRow } from "discord.js";
-import client from "../../utils/client.js";
-import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
-import getEmojiByName from "../../utils/getEmojiByName.js";
-
-export default async function (interaction) {
-    // @ts-ignore
-    const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger
-
-    let config = await client.database.guilds.read(interaction.guild.id);
-    let thread = false; let threadChannel
-    if (interaction.channel instanceof Discord.ThreadChannel) thread = true; threadChannel = interaction.channel as Discord.ThreadChannel
-    let channel = (interaction.channel as Discord.TextChannel)
-    if (!channel.parent || config.tickets.category != channel.parent.id || (thread ? (threadChannel.parent.parent.id != config.tickets.category) : false)) {
-        return interaction.reply({embeds: [new EmojiEmbed()
-            .setTitle("Deleting Ticket...")
-            .setDescription("This ticket is not in your tickets category, so cannot be deleted. You cannot run close in a thread.")
-            .setStatus("Danger")
-            .setEmoji("CONTROL.BLOCKCROSS")
-        ], ephemeral: true});
-    }
-    let status = channel.topic.split(" ")[1];
-    if (status == "Archived") {
-        await interaction.reply({embeds: [new EmojiEmbed()
-            .setTitle("Delete Ticket")
-            .setDescription("Your ticket is being deleted...")
-            .setStatus("Danger")
-            .setEmoji("GUILD.TICKET.CLOSE")
-        ]});
-        let data = {
-            meta:{
-                type: 'ticketDeleted',
-                displayName: 'Ticket Deleted',
-                calculateType: true,
-                color: NucleusColors.red,
-                emoji: 'GUILD.TICKET.CLOSE',
-                timestamp: new Date().getTime()
-            },
-            list: {
-                ticketFor: entry(channel.topic.split(" ")[0], renderUser((await interaction.guild.members.fetch(channel.topic.split(" ")[0])).user)),
-                deletedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)),
-                closed: entry(new Date().getTime(), renderDelta(new Date().getTime()))
-            },
-            hidden: {
-                guild: interaction.guild.id
-            }
-        }
-        log(data);
-        interaction.channel.delete();
-        return;
-    } else if (status == "Active") {
-        await interaction.reply({embeds: [new EmojiEmbed()
-            .setTitle("Close Ticket")
-            .setDescription("Your ticket is being closed...")
-            .setStatus("Warning")
-            .setEmoji("GUILD.TICKET.ARCHIVED")
-        ]});
-        let overwrites = [
-            {
-                id: channel.topic.split(" ")[0],
-                deny: ["VIEW_CHANNEL"],
-                type: "member"
-            },
-            {
-                id: interaction.guild.id,
-                deny: ["VIEW_CHANNEL"],
-                type: "role"
-            }
-        ] as Discord.OverwriteResolvable[];
-        if (config.tickets.supportRole != null) {
-            overwrites.push({
-                id: interaction.guild.roles.cache.get(config.tickets.supportRole),
-                allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
-                type: "role"
-            })
-        }
-        channel.edit({permissionOverwrites: overwrites})
-        channel.setTopic(`${channel.topic.split(" ")[0]} Archived`);
-        let data = {
-            meta:{
-                type: 'ticketClosed',
-                displayName: 'Ticket Closed',
-                calculateType: true,
-                color: NucleusColors.yellow,
-                emoji: 'GUILD.TICKET.ARCHIVED',
-                timestamp: new Date().getTime()
-            },
-            list: {
-                ticketFor: entry(channel.topic.split(" ")[0], renderUser((await interaction.guild.members.fetch(channel.topic.split(" ")[0])).user)),
-                closedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)),
-                closed: entry(new Date().getTime(), renderDelta(new Date().getTime())),
-                ticketChannel: entry(channel.id, renderChannel(channel)),
-            },
-            hidden: {
-                guild: interaction.guild.id
-            }
-        }
-        log(data);
-        await interaction.editReply({embeds: [new EmojiEmbed()
-            .setTitle("Close Ticket")
-            .setDescription("This ticket has been closed.\nType `/ticket close` again to delete it.")
-            .setStatus("Warning")
-            .setEmoji("GUILD.TICKET.ARCHIVED") // TODO:[Premium] Add a transcript option  ||\----/|| <- the bridge we will cross when we come to it
-        ], components: [new MessageActionRow().addComponents([new MessageButton()
-            .setLabel("Delete")
-            .setStyle("DANGER")
-            .setCustomId("closeticket")
-            .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-        ])]});
-        return;
-    }
-}
-
-async function purgeByUser(member, guild) {
-    let config = await client.database.guilds.read(guild.id);
-    if (!config.tickets.category) return;
-    let tickets = guild.channels.cache.get(config.tickets.category);
-    if (!tickets) return;
-    let ticketChannels = tickets.children;
-    let deleted = 0
-    ticketChannels.forEach(element => {
-        if (element.type != "GUILD_TEXT") return;
-        if (element.topic.split(" ")[0] == member) {
-            element.delete();
-            deleted++
-        }
-    });
-    if (deleted) {
-        try {
-            const { log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger
-            let data = {
-                meta:{
-                    type: 'ticketPurge',
-                    displayName: 'Tickets Purged',
-                    calculateType: true,
-                    color: NucleusColors.red,
-                    emoji: 'GUILD.TICKET.DELETE',
-                    timestamp: new Date().getTime()
-                },
-                list: {
-                    ticketFor: entry(member, renderUser(member)),
-                    deletedBy: entry(null, "Member left server"),
-                    deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
-                    ticketsDeleted: deleted,
-                },
-                hidden: {
-                    guild: guild.id
-                }
-            }
-            log(data);
-        } catch {}
-    }
-}
-
-export { purgeByUser }
\ No newline at end of file
diff --git a/src/automations/unscan.ts b/src/automations/unscan.ts
deleted file mode 100644
index d1fa7d4..0000000
--- a/src/automations/unscan.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as scan from '../utils/scanners.js'
-
-export async function LinkCheck(message): Promise<boolean> {
-    let links = message.content.match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi) ?? []
-    let detections = []
-    const promises = links.map(async element => {
-        try {
-            if (element.match(/https?:\/\/[a-zA-Z]+\.?discord(app)?\.(com|net)\/?/)) return // Also matches discord.net, not enough of a bug
-            console.log(1.1)
-            element = await scan.testLink(element)
-            console.log(1.2)
-        } catch {}
-        detections.push({tags: element.tags || [], safe: element.safe})
-    });
-    await Promise.all(promises);
-    let types = [
-        "PHISHING",  "DATING",            "TRACKERS",    "ADVERTISEMENTS", "FACEBOOK",
-        "AMP",       "FACEBOOK TRACKERS", "IP GRABBERS", "PORN",
-        "GAMBLING",  "MALWARE",           "PIRACY",      "RANSOMWARE",
-        "REDIRECTS", "SCAMS",             "TORRENT",     "HATE",           "JUNK"
-    ]
-    let detectionsTypes = detections.map(element => {
-        let type = types.find(type => element.tags.includes(type))
-        if (type) return type
-        // if (!element.safe) return "UNSAFE"
-        return undefined
-    }).filter(element => element !== undefined)
-    return detectionsTypes.length > 0
-}
-
-export async function NSFWCheck(element): Promise<boolean> {
-    try {
-        let test = (await scan.testNSFW(element))
-        //@ts-ignore
-        return test.nsfw
-    } catch {
-        return false
-    }
-}
-
-export async function SizeCheck(element): Promise<boolean> {
-    if (element.height == undefined || element.width == undefined) return true
-    if (element.height < 20 || element.width < 20) return false
-    return true
-}
-
-export async function MalwareCheck(element): Promise<boolean> {
-    try {
-        //@ts-ignore
-        return (await scan.testMalware(element)).safe
-    } catch {
-        return true
-    }
-}
-
-export function TestString(string, soft, strict): object | null {
-    for(let word of strict || []) {
-        if (string.toLowerCase().includes(word)) {
-            return {word: word, type: "strict"}
-        }
-    }
-    for(let word of soft) {
-        for(let word2 of string.match(/[a-z]+/gi) || []) {
-            if (word2 == word) {
-                return {word: word, type: "strict"}
-            }
-        }
-    }
-    return null
-}
-
-export async function TestImage(element): Promise<string | null> {
-    return null;
-}
diff --git a/src/automations/verify.ts b/src/automations/verify.ts
deleted file mode 100644
index 710439a..0000000
--- a/src/automations/verify.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-import Discord, { GuildMember } from "discord.js";
-import EmojiEmbed from "../utils/generateEmojiEmbed.js";
-import fetch from "node-fetch";
-import { TestString, NSFWCheck } from "../automations/unscan.js";
-import createPageIndicator from "../utils/createPageIndicator.js";
-import client from "../utils/client.js";
-
-function step(i) {
-    return "\n\n" + createPageIndicator(5, i);
-}
-
-export default async function(interaction) {
-    let verify = client.verify
-    await interaction.reply({embeds: [new EmojiEmbed()
-        .setTitle("Loading")
-        .setDescription(step(-1))
-        .setStatus("Danger")
-        .setEmoji("NUCLEUS.LOADING")
-    ], ephemeral: true, fetchReply: true});
-    let config = await client.database.guilds.read(interaction.guild.id);
-    if ((!config.verify.enabled ) || (!config.verify.role)) return interaction.editReply({embeds: [new EmojiEmbed()
-        .setTitle("Verify")
-        .setDescription(`Verify is not enabled on this server`)
-        .setStatus("Danger")
-        .setEmoji("CONTROL.BLOCKCROSS")
-    ], ephemeral: true, fetchReply: true});
-    if ((interaction.member as GuildMember).roles.cache.has(config.verify.role)) {
-        return await interaction.editReply({embeds: [new EmojiEmbed()
-            .setTitle("Verify")
-            .setDescription(`You already have the <@&${config.verify.role}> role` + step(0))
-            .setStatus("Danger")
-            .setEmoji("CONTROL.BLOCKCROSS")
-        ]});
-    }
-    await interaction.editReply({embeds: [new EmojiEmbed()
-        .setTitle("Verify")
-        .setDescription(`Checking our servers are up` + step(0))
-        .setStatus("Warning")
-        .setEmoji("NUCLEUS.LOADING")
-    ]});
-    try {
-        let status = await fetch(client.config.baseUrl).then(res => res.status);
-        if (status != 200) {
-            return await interaction.editReply({embeds: [new EmojiEmbed()
-                .setTitle("Verify")
-                .setDescription(`Our servers appear to be down, please try again later` + step(0))
-                .setStatus("Danger")
-                .setEmoji("CONTROL.BLOCKCROSS")
-            ]});
-        }
-    } catch {
-        return await interaction.editReply({embeds: [new EmojiEmbed()
-            .setTitle("Verify")
-            .setDescription(`Our servers appear to be down, please try again later` + step(0))
-            .setStatus("Danger")
-            .setEmoji("CONTROL.BLOCKCROSS")
-        ], components: [new Discord.MessageActionRow().addComponents([
-            new Discord.MessageButton()
-                .setLabel("Check webpage")
-                .setStyle("LINK")
-                .setURL(client.config.baseUrl),
-            new Discord.MessageButton()
-                .setLabel("Support")
-                .setStyle("LINK")
-                .setURL("https://discord.gg/bPaNnxe")
-        ])]});
-    }
-    if (config.filters.images.NSFW) {
-        await interaction.editReply({embeds: [new EmojiEmbed()
-            .setTitle("Verify")
-            .setDescription(`Checking your avatar is safe for work` + step(1))
-            .setStatus("Warning")
-            .setEmoji("NUCLEUS.LOADING")
-        ]});
-        if (await NSFWCheck((interaction.member as GuildMember).user.avatarURL({format: "png"}))) {
-            return await interaction.editReply({embeds: [new EmojiEmbed()
-                .setTitle("Verify")
-                .setDescription(`Your avatar was detected as NSFW, which we do not allow in this server.\nPlease contact one of our staff members if you believe this is a mistake` + step(1))
-                .setStatus("Danger")
-                .setEmoji("CONTROL.BLOCKCROSS")
-            ]});
-        }
-    }
-    if (config.filters.wordFilter) {
-        await interaction.editReply({embeds: [new EmojiEmbed()
-            .setTitle("Verify")
-            .setDescription(`Checking your name is allowed` + step(2))
-            .setStatus("Warning")
-            .setEmoji("NUCLEUS.LOADING")
-        ]});
-        if (TestString((interaction.member as Discord.GuildMember).displayName, config.filters.wordFilter.words.loose, config.filters.wordFilter.words.strict) !== null) {
-            return await interaction.editReply({embeds: [new EmojiEmbed()
-                .setTitle("Verify")
-                .setDescription(`Your name contained a word we do not allow in this server.\nPlease contact one of our staff members if you believe this is a mistake` + step(2))
-                .setStatus("Danger")
-                .setEmoji("CONTROL.BLOCKCROSS")
-            ]});
-        }
-    }
-    await interaction.editReply({embeds: [new EmojiEmbed()
-        .setTitle("Verify")
-        .setDescription(`One moment...` + step(3))
-        .setStatus("Warning")
-        .setEmoji("NUCLEUS.LOADING")
-    ]});
-    let code = ""
-    let length = 5
-    let itt = 0
-    const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-    while (true) {
-        itt += 1
-        code = ""
-        for (let i = 0; i < length; i++) { code += chars.charAt(Math.floor(Math.random() * chars.length)); }
-        if (code in verify) continue;
-        if (itt > 1000) {
-            itt = 0
-            length += 1
-            continue
-        }
-        break;
-    }
-    verify[code] = {
-        uID: interaction.member.user.id,
-        gID: interaction.guild.id,
-        rID: config.verify.role,
-        rName: (await interaction.guild.roles.fetch(config.verify.role)).name,
-        uName: interaction.member.user.username,
-        gName: interaction.guild.name,
-        gIcon: interaction.guild.iconURL({format: "png"}),
-        interaction: interaction
-    }
-    await interaction.editReply({embeds: [new EmojiEmbed()
-        .setTitle("Verify")
-        .setDescription(`Looking good!\nClick the button below to get verified` + step(4))
-        .setStatus("Success")
-        .setEmoji("MEMBER.JOIN")
-    ], components: [new Discord.MessageActionRow().addComponents([new Discord.MessageButton()
-        .setLabel("Verify")
-        .setStyle("LINK")
-        .setURL(`${client.config.baseUrl}/nucleus/verify?code=${code}`)
-    ])]});
-}
diff --git a/src/automations/welcome.ts b/src/automations/welcome.ts
deleted file mode 100644
index 5b80fbd..0000000
--- a/src/automations/welcome.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import log from '../utils/log.js'
-import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
-import client from '../utils/client.js';
-
-export async function callback(_, member) {
-    if (member.bot) return
-    let config = await client.database.guilds.read(member.guild.id);
-    if (!config.welcome.enabled) return
-
-    if (!config.welcome.verificationRequired.role) {
-        if (config.welcome.welcomeRole) {
-            try {
-                await member.roles.add(config.welcome.welcomeRole)
-            } catch (err) {
-                console.error(err)
-            }
-        }
-    }
-
-    if (!config.welcome.verificationRequired.message && config.welcome.channel) {
-        let string = config.welcome.message
-        if (string) {
-            string = await convertCurlyBracketString(string, member.id, member.displayName, member.guild.name, member.guild.members)
-
-            if (config.welcome.channel === 'dm') {
-                try {
-                    await member.send(string)
-                } catch (err) {
-                    console.error(err)
-                }
-            } else {
-                let channel = await member.client.channels.fetch(config.welcome.channel)
-                if (channel.guild.id !== member.guild.id) return
-                if (!channel) return
-                try {
-                    await channel.send(string)
-                } catch (err) {
-                    console.error(err)
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/commands/categorisationTest.ts b/src/commands/categorisationTest.ts
index a6c7479..f89a899 100644
--- a/src/commands/categorisationTest.ts
+++ b/src/commands/categorisationTest.ts
@@ -1,68 +1,97 @@
-import { CommandInteraction, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js";
-import { SlashCommandBuilder } from "@discordjs/builders";
+import { CommandInteraction, GuildChannel, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js";
+import { SelectMenuOption, SlashCommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
 import EmojiEmbed from "../utils/generateEmojiEmbed.js";
-import generateKeyValueList, { toCapitals } from "../utils/generateKeyValueList.js";
-import getEmojiByName from "../utils/getEmojiByName.js";
 import client from "../utils/client.js"
+import addPlural from "../utils/plurals.js";
+import getEmojiByName from "../utils/getEmojiByName.js";
 
 const command = new SlashCommandBuilder()
     .setName("categorise")
     .setDescription("Categorises your servers channels")
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
-    const { renderChannel } = client.logger
-
     let channels = interaction.guild.channels.cache.filter(c => c.type !== "GUILD_CATEGORY");
     let categorised = {}
-
     await interaction.reply({embeds: [new EmojiEmbed()
         .setTitle("Loading...")
         .setEmoji("NUCLEUS.LOADING")
         .setStatus("Success")
     ], ephemeral: true});
+    let predicted = {}
+    let types = {
+        general: ["general", "muted", "main", "topic", "discuss"],
+        commands: ["bot", "command", "music"],
+        images: ["pic", "selfies", "image"],
+        nsfw: ["porn", "nsfw", "sex"],
+        links: ["links"],
+        advertising: ["ads", "advert", "server", "partner"],
+        staff: ["staff", "mod", "admin"],
+        spam: ["spam"],
+        other: ["random"]
+    }
     for (let c of channels.values()) {
-        let predicted = []
-        let types = {
-            general: ["general"],
-            commands: ["bot", "command", "music"],
-            images: ["pic", "selfies", "image"],
-            nsfw: ["porn", "nsfw", "sex"],
-            links: ["links"],
-            advertising: ["ads", "advert", "server", "partner"],
-            staff: ["staff", "mod", "admin"]
-        }
-
         for (let type in types) {
             for (let word of types[type]) {
                 if (c.name.toLowerCase().includes(word)) {
-                    predicted.push(type)
+                    predicted[c.id] = predicted[c.id] ?? []
+                    predicted[c.id].push(type)
                 }
             }
         }
-
-        await interaction.editReply({embeds: [new EmojiEmbed()
-            .setTitle("Categorise")
-            .setDescription(generateKeyValueList({
-                channel: renderChannel(c),
-                category: c.parent ? c.parent.name : "Uncategorised"
-            }) + "\n\n" + `Suggested tags: ${predicted.join(", ")}`)
-            .setEmoji("CHANNEL.TEXT.CREATE")
-            .setStatus("Success")
-        ], components: [ new MessageActionRow().addComponents([
-            new MessageButton()
-                .setLabel("Use suggested")
-                .setStyle("PRIMARY")
-                .setCustomId("accept")
-                .setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
-        ]), new MessageActionRow().addComponents([new MessageSelectMenu()
-            .setPlaceholder("Select a category")
-            .setCustomId("category")
-            .setMinValues(0)
-            .setMaxValues(1)
-            .setOptions(Object.keys(types).map(type => {return {label: toCapitals(type), value: type}}))
-        ])]});
     }
+    let m;
+    for (let c of channels) {
+        // convert channel to a channel if its a string
+        let channel: any
+        console.log(c)
+        if (typeof c === "string") channel = interaction.guild.channels.cache.get(channel).id
+        // @ts-ignore
+        else channel = c[0].id
+        console.log(channel)
+        if (!predicted[channel]) predicted[channel] = []
+        m = await interaction.editReply({embeds: [new EmojiEmbed()
+            .setTitle("Categorise")
+            .setDescription(`Select all types that apply to <#${channel}>.\n\n` +
+            `${addPlural(predicted[channel].length, "Suggestion")}: ${predicted[channel].join(", ")}`)
+            .setEmoji("CHANNEL.CATEGORY.CREATE")
+            .setStatus("Success")
+        ], components: [
+            new MessageActionRow().addComponents([new MessageSelectMenu()
+                .setCustomId("selected")
+                .setMaxValues(Object.keys(types).length)
+                .setMinValues(1)
+                .setPlaceholder("Select all types that apply to this channel")
+                .setOptions(Object.keys(types).map(type => ({label: type, value: type})))
+            ]),
+            new MessageActionRow().addComponents([
+                new MessageButton().setLabel("Accept Suggestion").setCustomId("accept").setStyle("SUCCESS").setDisabled(predicted[channel].length === 0)
+                    .setEmoji(client.emojis.cache.get(getEmojiByName("ICONS.TICK", "id"))),
+                new MessageButton().setLabel("Use \"Other\"").setCustomId("reject").setStyle("SECONDARY")
+                    .setEmoji(client.emojis.cache.get(getEmojiByName("ICONS.CROSS", "id")))
+            ])
+        ]})
+        let i;
+        try {
+            i = await m.awaitMessageComponent({ time: 300000 });
+        } catch (e) {
+            return await interaction.editReply({embeds: [new EmojiEmbed()
+                .setTitle("Categorise")
+                .setEmoji("CHANNEL.CATEGORY.DELETE")
+                .setStatus("Danger")
+                .setDescription(`Select all types that apply to <#${channel}>.\n\n` +
+                `${addPlural(predicted[channel].length, "Suggestion")}: ${predicted[channel].join(", ")}`)
+                .setFooter({text: "Message timed out"})
+            ]})
+        }
+        i.deferUpdate()
+        let selected;
+        if (i.customId === "select") { selected = i.values; }
+        if (i.customId === "accept") { selected = predicted[channel]; }
+        if (i.customId === "reject") { selected = ["other"]; }
+        categorised[channel] = selected
+    }
+    console.log(categorised)
 }
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts
index 11c6c79..0239951 100644
--- a/src/commands/mod/ban.ts
+++ b/src/commands/mod/ban.ts
@@ -12,27 +12,34 @@
     .setName("ban")
     .setDescription("Bans a user from the server")
     .addUserOption(option => option.setName("user").setDescription("The user to ban").setRequired(true))
-    .addStringOption(option => option.setName("reason").setDescription("The reason for the ban").setRequired(false))
-    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are banned | Default yes").setRequired(false)
+    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are banned | Default: Yes").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]])
     )
-    .addIntegerOption(option => option.setName("delete").setDescription("The days of messages to delete | Default 0").setMinValue(0).setMaxValue(7).setRequired(false))
+    .addIntegerOption(option => option.setName("delete").setDescription("The days of messages to delete | Default: 0").setMinValue(0).setMaxValue(7).setRequired(false))
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
     const { renderUser } = client.logger
     // TODO:[Modals] Replace this with a modal
-    let confirmation = await new confirmationMessage(interaction)
-        .setEmoji("PUNISH.BAN.RED")
-        .setTitle("Ban")
-        .setDescription(keyValueList({
-            "user": renderUser(interaction.options.getUser("user")),
-            "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
-        })
-        + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n`
-        + `${addPlurals(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
-        + `Are you sure you want to ban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
-        .setColor("Danger")
-    .send()
+    let reason = null
+    let confirmation
+    while (true) {
+        confirmation = await new confirmationMessage(interaction)
+            .setEmoji("PUNISH.BAN.RED")
+            .setTitle("Ban")
+            .setDescription(keyValueList({
+                "user": renderUser(interaction.options.getUser("user")),
+                "reason": reason ? ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
+            })
+            + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n`
+            + `${addPlurals(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
+            + `Are you sure you want to ban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
+            .setColor("Danger")
+            .addReasonButton(reason ?? "")
+            .send(reason !== null)
+        reason = reason ?? ""
+        if (confirmation.newReason === undefined) break
+        reason = confirmation.newReason
+    }
     if (confirmation.success) {
         let dmd = false
         let dm;
@@ -44,7 +51,7 @@
                         .setEmoji("PUNISH.BAN.RED")
                         .setTitle("Banned")
                         .setDescription(`You have been banned in ${interaction.guild.name}` +
-                                    (interaction.options.getString("reason") ? ` for:\n> ${interaction.options.getString("reason")}` : "."))
+                                    (reason ? ` for:\n> ${reason}` : "."))
                         .setStatus("Danger")
                     ],
                     components: [new MessageActionRow().addComponents(config.moderation.ban.text ? [new MessageButton()
@@ -58,7 +65,6 @@
         } catch {}
         try {
             let member = (interaction.options.getMember("user") as GuildMember)
-            let reason = interaction.options.getString("reason")
             member.ban({
                 days: Number(interaction.options.getInteger("delete") ?? 0),
                 reason: reason ?? "No reason provided"
diff --git a/src/commands/mod/info.ts b/src/commands/mod/info.ts
index c3a4388..b7f4b74 100644
--- a/src/commands/mod/info.ts
+++ b/src/commands/mod/info.ts
@@ -1,6 +1,6 @@
 import { HistorySchema } from '../../utils/database';
 import Discord, { CommandInteraction, GuildMember, MessageActionRow, MessageButton, TextInputComponent } from "discord.js";
-import { SlashCommandSubcommandBuilder, SelectMenuOption } from "@discordjs/builders";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import getEmojiByName from "../../utils/getEmojiByName.js";
@@ -18,6 +18,7 @@
 const types = {
     "warn": {emoji: "PUNISH.WARN.YELLOW", text: "Warned"},
     "mute": {emoji: "PUNISH.MUTE.YELLOW", text: "Muted"},
+    "unmute": {emoji: "PUNISH.MUTE.GREEN", text: "Unmuted"},
     "join": {emoji: "MEMBER.JOIN", text: "Joined"},
     "leave": {emoji: "MEMBER.LEAVE", text: "Left"},
     "kick": {emoji: "MEMBER.KICK", text: "Kicked"},
diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts
index b1464ee..793a630 100644
--- a/src/commands/mod/kick.ts
+++ b/src/commands/mod/kick.ts
@@ -12,25 +12,32 @@
     .setName("kick")
     .setDescription("Kicks a user from the server")
     .addUserOption(option => option.setName("user").setDescription("The user to kick").setRequired(true))
-    .addStringOption(option => option.setName("reason").setDescription("The reason for the kick").setRequired(false))
-    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are kicked | Default yes").setRequired(false)
+    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are kicked | Default: Yes").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]])
     )
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
     const { renderUser } = client.logger
     // TODO:[Modals] Replace this with a modal
-    let confirmation = await new confirmationMessage(interaction)
-        .setEmoji("PUNISH.KICK.RED")
-        .setTitle("Kick")
-        .setDescription(keyValueList({
-            "user": renderUser(interaction.options.getUser("user")),
-            "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
-        })
-        + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
-        + `Are you sure you want to kick <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
-        .setColor("Danger")
-    .send()
+    let reason = null;
+    let confirmation
+    while (true) {
+        confirmation = await new confirmationMessage(interaction)
+            .setEmoji("PUNISH.KICK.RED")
+            .setTitle("Kick")
+            .setDescription(keyValueList({
+                "user": renderUser(interaction.options.getUser("user")),
+                "reason": reason ? ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
+            })
+            + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
+            + `Are you sure you want to kick <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
+            .setColor("Danger")
+            .addReasonButton(reason ?? "")
+        .send(reason !== null)
+        reason = reason ?? ""
+        if (confirmation.newReason === undefined) break
+        reason = confirmation.newReason
+    }
     if (confirmation.success) {
         let dmd = false
         let dm;
@@ -42,7 +49,7 @@
                         .setEmoji("PUNISH.KICK.RED")
                         .setTitle("Kicked")
                         .setDescription(`You have been kicked in ${interaction.guild.name}` +
-                                    (interaction.options.getString("reason") ? ` for:\n> ${interaction.options.getString("reason")}` : "."))
+                                    (reason ? ` for:\n> ${reason}` : "."))
                         .setStatus("Danger")
                     ],
                     components: [new MessageActionRow().addComponents(config.moderation.kick.text ? [new MessageButton()
@@ -55,9 +62,8 @@
             }
         } catch {}
         try {
-            (interaction.options.getMember("user") as GuildMember).kick(interaction.options.getString("reason") ?? "No reason provided.")
+            (interaction.options.getMember("user") as GuildMember).kick(reason ?? "No reason provided.")
             let member = (interaction.options.getMember("user") as GuildMember)
-            let reason = interaction.options.getString("reason") ?? null
             try { await client.database.history.create("kick", interaction.guild.id, member.user, interaction.user, reason) } catch {}
             // @ts-ignore
             const { log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger
diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts
index 4c326e7..5e1a18b 100644
--- a/src/commands/mod/mute.ts
+++ b/src/commands/mod/mute.ts
@@ -7,24 +7,23 @@
 import keyValueList from "../../utils/generateKeyValueList.js";
 import humanizeDuration from "humanize-duration";
 import client from "../../utils/client.js";
+import { areTicketsEnabled, create } from "../../actions/createModActionTicket.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
     .setName("mute")
-    .setDescription("Mutes a member using Discord's \"Timeout\" feature")
+    .setDescription("Mutes a member, stopping them from talking in the server")
     .addUserOption(option => option.setName("user").setDescription("The user to mute").setRequired(true))
-    .addIntegerOption(option => option.setName("days").setDescription("The number of days to mute the user for | Default 0").setMinValue(0).setMaxValue(27).setRequired(false))
-    .addIntegerOption(option => option.setName("hours").setDescription("The number of hours to mute the user for | Default 0").setMinValue(0).setMaxValue(23).setRequired(false))
-    .addIntegerOption(option => option.setName("minutes").setDescription("The number of minutes to mute the user for | Default 0").setMinValue(0).setMaxValue(59).setRequired(false))
-    .addIntegerOption(option => option.setName("seconds").setDescription("The number of seconds to mute the user for | Default 0").setMinValue(0).setMaxValue(59).setRequired(false))
-    .addStringOption(option => option.setName("reason").setDescription("The reason for the mute").setRequired(false))
-    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are muted | Default yes").setRequired(false)
+    .addIntegerOption(option => option.setName("days").setDescription("The number of days to mute the user for | Default: 0").setMinValue(0).setMaxValue(27).setRequired(false))
+    .addIntegerOption(option => option.setName("hours").setDescription("The number of hours to mute the user for | Default: 0").setMinValue(0).setMaxValue(23).setRequired(false))
+    .addIntegerOption(option => option.setName("minutes").setDescription("The number of minutes to mute the user for | Default: 0").setMinValue(0).setMaxValue(59).setRequired(false))
+    .addIntegerOption(option => option.setName("seconds").setDescription("The number of seconds to mute the user for | Default: 0").setMinValue(0).setMaxValue(59).setRequired(false))
+    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are muted | Default: yes").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]]))
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
-    const { log, NucleusColors, renderUser, entry } = client.logger
+    const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger
     const user = interaction.options.getMember("user") as GuildMember
-    const reason = interaction.options.getString("reason")
     const time = {
         days: interaction.options.getInteger("days") || 0,
         hours: interaction.options.getInteger("hours") || 0,
@@ -119,31 +118,43 @@
         ], ephemeral: true, fetchReply: true})
     }
     // TODO:[Modals] Replace this with a modal
-    let confirmation = await new confirmationMessage(interaction)
-        .setEmoji("PUNISH.MUTE.RED")
-        .setTitle("Mute")
-        .setDescription(keyValueList({
-            "user": renderUser(user),
-            "time": `${humanizeDuration(muteTime * 1000, {round: true})}`,
-            "reason": `\n> ${reason ? reason : "*No reason provided*"}`
-        })
-        + `The user will be ` + serverSettingsDescription + "\n"
-        + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
-        + `Are you sure you want to mute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
-        .setColor("Danger")
-    .send(true)
+    let reason = null;
+    let confirmation;
+    while (true) {
+        confirmation = await new confirmationMessage(interaction)
+            .setEmoji("PUNISH.MUTE.RED")
+            .setTitle("Mute")
+            .setDescription(keyValueList({
+                "user": renderUser(user.user),
+                "time": `${humanizeDuration(muteTime * 1000, {round: true})}`,
+                "reason": reason ?  ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
+            })
+            + `The user will be ` + serverSettingsDescription + "\n"
+            + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
+            + `Are you sure you want to mute <@!${user.id}>?`)
+            .setColor("Danger")
+            .addCustomBoolean(
+                "Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
+                async () => await create(interaction.guild, user.user, interaction.user, reason),
+                "An appeal ticket will be created when Confirm is clicked")
+            .addReasonButton(reason ?? "")
+        .send(true)
+        reason = reason ?? ""
+        if (confirmation.newReason === undefined) break
+        reason = confirmation.newReason
+    }
     if (confirmation.success) {
         let dmd = false
         let dm;
         let config = await client.database.guilds.read(interaction.guild.id);
         try {
             if (interaction.options.getString("notify") != "no") {
-                dm = await (interaction.options.getMember("user") as GuildMember).send({
+                dm = await user.send({
                     embeds: [new EmojiEmbed()
                         .setEmoji("PUNISH.MUTE.RED")
                         .setTitle("Muted")
                         .setDescription(`You have been muted in ${interaction.guild.name}` +
-                                    (interaction.options.getString("reason") ? ` for:\n> ${interaction.options.getString("reason")}` : ".\n\n" +
+                                    (reason ? ` for:\n> ${reason}` : ".\n\n" +
                                     `You will be unmuted at: <t:${Math.round((new Date).getTime() / 1000) + muteTime}:D> at <t:${Math.round((new Date).getTime() / 1000) + muteTime}:T> (<t:${Math.round((new Date).getTime() / 1000) + muteTime}:R>)`))
                         .setStatus("Danger")
                     ],
@@ -156,19 +167,36 @@
                 dmd = true
             }
         } catch {}
-        let member = (interaction.options.getMember("user") as GuildMember)
+        let member = user
+        let errors = 0
         try {
             if (config.moderation.mute.timeout) {
-                member.timeout(muteTime * 1000, interaction.options.getString("reason") || "No reason provided")
+                await member.timeout(muteTime * 1000, reason || "No reason provided")
+                if (config.moderation.mute.role !== null) {
+                    await member.roles.add(config.moderation.mute.role)
+                    await client.database.eventScheduler.schedule("naturalUnmute", new Date().getTime() + muteTime * 1000, {
+                        guild: interaction.guild.id,
+                        user: user.id,
+                        expires: new Date().getTime() + muteTime * 1000
+                    })
+                }
             }
-            if (config.moderation.mute.role) {
-                member.roles.add(config.moderation.mute.role)
-            } // make sure this gets removed
-        } catch {
+        } catch { errors++ }
+        try {
+            if (config.moderation.mute.role !== null) {
+                await member.roles.add(config.moderation.mute.role)
+                await client.database.eventScheduler.schedule("unmuteRole", new Date().getTime() + muteTime * 1000, {
+                    guild: interaction.guild.id,
+                    user: user.id,
+                    role: config.moderation.mute.role
+                })
+            }
+        } catch (e){ console.log(e); errors++ }
+        if (errors == 2) {
             await interaction.editReply({embeds: [new EmojiEmbed()
                 .setEmoji("PUNISH.MUTE.RED")
                 .setTitle(`Mute`)
-                .setDescription("Something went wrong and the user was not mute")
+                .setDescription("Something went wrong and the user was not muted")
                 .setStatus("Danger")
             ], components: []})
             if (dmd) await dm.delete()
@@ -192,10 +220,12 @@
                 timestamp: new Date().getTime()
             },
             list: {
-                user: entry(member.user.id, renderUser(member.user)),
+                memberId: entry(member.user.id, `\`${member.user.id}\``),
+                name: entry(member.user.id, renderUser(member.user)),
+                mutedUntil: entry(new Date().getTime() + muteTime * 1000, renderDelta(new Date().getTime() + muteTime * 1000)),
+                muted: entry(new Date().getTime(), renderDelta(new Date().getTime() - 1000)),
                 mutedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)),
-                time: entry(muteTime, `${humanizeDuration(muteTime * 1000, {round: true})}`),
-                reason: (interaction.options.getString("reason") ? `\n> ${interaction.options.getString("reason")}` : "No reason provided")
+                reason: entry(reason, reason ? reason : '*No reason provided*')
             },
             hidden: {
                 guild: interaction.guild.id
diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts
index 8dcbf62..f842d76 100644
--- a/src/commands/mod/nick.ts
+++ b/src/commands/mod/nick.ts
@@ -4,7 +4,7 @@
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import keyValueList from "../../utils/generateKeyValueList.js";
-import { create, areTicketsEnabled } from "../../automations/createModActionTicket.js";
+import { create, areTicketsEnabled } from "../../actions/createModActionTicket.js";
 import client from "../../utils/client.js"
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
@@ -13,7 +13,7 @@
     .setDescription("Changes a users nickname")
     .addUserOption(option => option.setName("user").setDescription("The user to change").setRequired(true))
     .addStringOption(option => option.setName("name").setDescription("The name to set | Leave blank to clear").setRequired(false))
-    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when their nickname is changed | Default no").setRequired(false)
+    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when their nickname is changed | Default: No").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]])
     )
 
@@ -32,7 +32,7 @@
         .setColor("Danger")
         .addCustomBoolean(
             "Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
-            async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, interaction.options.getString("reason")),
+            async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, null),
             "An appeal ticket will be created when Confirm is clicked")
     .send()
     if (confirmation.success) {
diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts
index ea4a447..a368e7a 100644
--- a/src/commands/mod/softban.ts
+++ b/src/commands/mod/softban.ts
@@ -12,27 +12,34 @@
     .setName("softban")
     .setDescription("Kicks a user and deletes their messages")
     .addUserOption(option => option.setName("user").setDescription("The user to softban").setRequired(true))
-    .addStringOption(option => option.setName("reason").setDescription("The reason for the softban").setRequired(false))
-    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are softbanned | Default yes").setRequired(false)
+    .addIntegerOption(option => option.setName("delete").setDescription("The days of messages to delete | Default: 0").setMinValue(0).setMaxValue(7).setRequired(false))
+    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are softbanned | Default: Yes").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]])
     )
-    .addIntegerOption(option => option.setName("delete").setDescription("The days of messages to delete | Default 0").setMinValue(0).setMaxValue(7).setRequired(false))
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
     const { renderUser } = client.logger
     // TODO:[Modals] Replace this with a modal
-    let confirmation = await new confirmationMessage(interaction)
-        .setEmoji("PUNISH.BAN.RED")
-        .setTitle("Softban")
-        .setDescription(keyValueList({
-            "user": renderUser(interaction.options.getUser("user")),
-            "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
-        })
-        + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n`
-        + `${addPlural(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
-        + `Are you sure you want to softban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
-        .setColor("Danger")
-    .send()
+    let reason = null;
+    let confirmation;
+    while (true) {
+        let confirmation = await new confirmationMessage(interaction)
+            .setEmoji("PUNISH.BAN.RED")
+            .setTitle("Softban")
+            .setDescription(keyValueList({
+                "user": renderUser(interaction.options.getUser("user")),
+                "reason": reason ? ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
+            })
+            + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n`
+            + `${addPlural(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
+            + `Are you sure you want to softban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
+            .setColor("Danger")
+            .addReasonButton(reason ?? "")
+        .send(reason !== null)
+        reason = reason ?? ""
+        if (confirmation.newReason === undefined) break
+        reason = confirmation.newReason
+    }
     if (confirmation.success) {
         let dmd = false;
         let config = await client.database.guilds.read(interaction.guild.id);
@@ -43,7 +50,7 @@
                         .setEmoji("PUNISH.BAN.RED")
                         .setTitle("Softbanned")
                         .setDescription(`You have been softbanned from ${interaction.guild.name}` +
-                                    (interaction.options.getString("reason") ? ` for:\n> ${interaction.options.getString("reason")}` : "."))
+                                    (reason ? ` for:\n> ${reason}` : "."))
                         .setStatus("Danger")
                     ],
                     components: [new MessageActionRow().addComponents(config.moderation.ban.text ? [new MessageButton()
@@ -59,7 +66,7 @@
         try {
             await member.ban({
                 days: Number(interaction.options.getInteger("delete") ?? 0),
-                reason: interaction.options.getString("reason")
+                reason: reason
             });
             await interaction.guild.members.unban(member, "Softban");
         } catch {
@@ -70,7 +77,7 @@
                 .setStatus("Danger")
             ], components: []})
         }
-        try { await client.database.history.create("softban", interaction.guild.id, member.user, interaction.options.getString("reason")) } catch {}
+        try { await client.database.history.create("softban", interaction.guild.id, member.user, reason) } catch {}
         let failed = (dmd == false && interaction.options.getString("notify") != "no")
         await interaction.editReply({embeds: [new EmojiEmbed()
             .setEmoji(`PUNISH.BAN.${failed ? "YELLOW" : "GREEN"}`)
diff --git a/src/commands/mod/unmute.ts b/src/commands/mod/unmute.ts
index 5a1b67c..d5f4205 100644
--- a/src/commands/mod/unmute.ts
+++ b/src/commands/mod/unmute.ts
@@ -11,25 +11,32 @@
     .setName("unmute")
     .setDescription("Unmutes a user")
     .addUserOption(option => option.setName("user").setDescription("The user to unmute").setRequired(true))
-    .addStringOption(option => option.setName("reason").setDescription("The reason for the unmute").setRequired(false))
-    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are unmuted | Default no").setRequired(false)
+    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are unmuted | Default: No").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]])
     )
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
-    const { renderUser } = client.logger
+    const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger
     // TODO:[Modals] Replace this with a modal
-    let confirmation =  await new confirmationMessage(interaction)
-        .setEmoji("PUNISH.MUTE.RED")
-        .setTitle("Unmute")
-        .setDescription(keyValueList({
-            "user": renderUser(interaction.options.getUser("user")),
-            "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
-        })
-        + `The user **will${interaction.options.getString("notify") === "yes" ? '' : ' not'}** be notified\n\n`
-        + `Are you sure you want to unmute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
-        .setColor("Danger")
-    .send()
+    let reason = null;
+    let confirmation;
+    while (true) {
+        confirmation =  await new confirmationMessage(interaction)
+            .setEmoji("PUNISH.MUTE.RED")
+            .setTitle("Unmute")
+            .setDescription(keyValueList({
+                "user": renderUser(interaction.options.getUser("user")),
+                "reason": `\n> ${reason ? reason : "*No reason provided*"}`
+            })
+            + `The user **will${interaction.options.getString("notify") === "yes" ? '' : ' not'}** be notified\n\n`
+            + `Are you sure you want to unmute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
+            .setColor("Danger")
+            .addReasonButton(reason ?? "")
+        .send(reason !== null)
+        reason = reason ?? ""
+        if (confirmation.newReason === undefined) break
+        reason = confirmation.newReason
+    }
     if (confirmation.success) {
         let dmd = false
         let dm;
@@ -40,15 +47,16 @@
                         .setEmoji("PUNISH.MUTE.GREEN")
                         .setTitle("Unmuted")
                         .setDescription(`You have been unmuted in ${interaction.guild.name}` +
-                                    (interaction.options.getString("reason") ? ` for:\n> ${interaction.options.getString("reason")}` : " with no reason provided."))
+                                    (reason ? ` for:\n> ${reason}` : " with no reason provided."))
                         .setStatus("Success")
                     ]
                 })
                 dmd = true
             }
         } catch {}
+        let member = (interaction.options.getMember("user") as GuildMember)
         try {
-            (interaction.options.getMember("user") as GuildMember).timeout(0, interaction.options.getString("reason") || "No reason provided")
+            member.timeout(0, reason || "No reason provided")
         } catch {
             await interaction.editReply({embeds: [new EmojiEmbed()
                 .setEmoji("PUNISH.MUTE.RED")
@@ -59,7 +67,27 @@
             if (dmd) await dm.delete()
             return
         }
-        try { await client.database.history.create("unmute", interaction.guild.id, (interaction.options.getMember("user") as GuildMember).user, interaction.user, interaction.options.getString("reason")) } catch {}
+        try { await client.database.history.create("unmute", interaction.guild.id, (interaction.options.getMember("user") as GuildMember).user, interaction.user, reason) } catch {}
+        let data = {
+            meta: {
+                type: 'memberUnmute',
+                displayName: 'Unmuted',
+                calculateType: 'guildMemberPunish',
+                color: NucleusColors.green,
+                emoji: "PUNISH.MUTE.GREEN",
+                timestamp: new Date().getTime()
+            },
+            list: {
+                memberId: entry(member.user.id, `\`${member.user.id}\``),
+                name: entry(member.user.id, renderUser(member.user)),
+                unmuted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+                unmutedBy: entry(interaction.user.id, renderUser(interaction.user)),
+            },
+            hidden: {
+                guild: interaction.guild.id
+            }
+        }
+        log(data);
         let failed = (dmd == false && interaction.options.getString("notify") != "no")
         await interaction.editReply({embeds: [new EmojiEmbed()
             .setEmoji(`PUNISH.MUTE.${failed ? "YELLOW" : "GREEN"}`)
diff --git a/src/commands/mod/warn.ts b/src/commands/mod/warn.ts
index 370f347..379d49c 100644
--- a/src/commands/mod/warn.ts
+++ b/src/commands/mod/warn.ts
@@ -4,7 +4,7 @@
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import keyValueList from "../../utils/generateKeyValueList.js";
-import { create, areTicketsEnabled } from "../../automations/createModActionTicket.js";
+import { create, areTicketsEnabled } from "../../actions/createModActionTicket.js";
 import client from "../../utils/client.js"
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
@@ -12,29 +12,37 @@
     .setName("warn")
     .setDescription("Warns a user")
     .addUserOption(option => option.setName("user").setDescription("The user to warn").setRequired(true))
-    .addStringOption(option => option.setName("reason").setDescription("The reason for the warn").setRequired(false))
-    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are warned | Default yes").setRequired(false)
+    .addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are warned | Default: Yes").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]])
     )
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
     const { log, NucleusColors, renderUser, entry } = client.logger
     // TODO:[Modals] Replace this with a modal
-    let confirmation = await new confirmationMessage(interaction)
-        .setEmoji("PUNISH.WARN.RED")
-        .setTitle("Warn")
-        .setDescription(keyValueList({
-            "user": renderUser(interaction.options.getUser("user")),
-            "reason": `\n> ${interaction.options.getString("reason") ? interaction.options.getString("reason") : "*No reason provided*"}`
-        })
-        + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
-        + `Are you sure you want to warn <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
-        .setColor("Danger")
-        .addCustomBoolean(
-            "Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
-            async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, interaction.options.getString("reason")),
-            "An appeal ticket will be created when Confirm is clicked")
-    .send()
+    let reason = null;
+    let confirmation;
+    while (true) {
+        confirmation = await new confirmationMessage(interaction)
+            .setEmoji("PUNISH.WARN.RED")
+            .setTitle("Warn")
+            .setDescription(keyValueList({
+                "user": renderUser(interaction.options.getUser("user")),
+                "reason": reason ? ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
+            })
+            + `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
+            + `Are you sure you want to warn <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
+            .setColor("Danger")
+            .addCustomBoolean(
+                "Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
+                async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, reason),
+                "An appeal ticket will be created when Confirm is clicked")
+                .addReasonButton(reason)
+            .addReasonButton(reason ?? "")
+        .send(reason !== null)
+        reason = reason ?? ""
+        if (confirmation.newReason === undefined) break
+        reason = confirmation.newReason
+    }
     if (confirmation.success) {
         let dmd = false
         try {
@@ -44,7 +52,7 @@
                         .setEmoji("PUNISH.WARN.RED")
                         .setTitle("Warned")
                         .setDescription(`You have been warned in ${interaction.guild.name}` +
-                                    (interaction.options.getString("reason") ? ` for:\n> ${interaction.options.getString("reason")}` : ".") + "\n\n" +
+                                    (reason ? ` for:\n> ${reason}` : ".") + "\n\n" +
                                     (confirmation.buttonClicked ? `You can appeal this here ticket: <#${confirmation.response}>` : ``))
                         .setStatus("Danger")
                     ]
@@ -71,7 +79,7 @@
             list: {
                 user: entry((interaction.options.getMember("user") as GuildMember).user.id, renderUser((interaction.options.getMember("user") as GuildMember).user)),
                 warnedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)),
-                reason: (interaction.options.getString("reason") ? `\n> ${interaction.options.getString("reason")}` : "No reason provided")
+                reason: reason ? `\n> ${reason}` : "No reason provided"
             },
             hidden: {
                 guild: interaction.guild.id
@@ -80,7 +88,7 @@
         try { await client.database.history.create(
             "warn", interaction.guild.id,
             (interaction.options.getMember("user") as GuildMember).user,
-            interaction.user, interaction.options.getString("reason")
+            interaction.user, reason
         )} catch {}
         log(data);
         let failed = (dmd == false && interaction.options.getString("notify") != "no")
@@ -129,7 +137,7 @@
                         .setEmoji(`PUNISH.WARN.RED`)
                         .setTitle(`Warn`)
                         .setDescription(`You have been warned` +
-                                    (interaction.options.getString("reason") ? ` for:\n> ${interaction.options.getString("reason")}` : "."))
+                                    (reason ? ` for:\n> ${reason}` : "."))
                         .setStatus("Danger")
                     ],
                     content: `<@!${(interaction.options.getMember("user") as GuildMember).id}>`,
diff --git a/src/commands/nucleus/guide.ts b/src/commands/nucleus/guide.ts
index eb94de4..5f2cde2 100644
--- a/src/commands/nucleus/guide.ts
+++ b/src/commands/nucleus/guide.ts
@@ -3,7 +3,7 @@
 import { WrappedCheck } from "jshaiku";
 import getEmojiByName from "../../utils/getEmojiByName.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
-import guide from "../../automations/guide.js";
+import guide from "../../reflex/guide.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
diff --git a/src/commands/privacy.ts b/src/commands/privacy.ts
index cc6c554..6d62745 100644
--- a/src/commands/privacy.ts
+++ b/src/commands/privacy.ts
@@ -1,15 +1,35 @@
-import Discord, { CommandInteraction } from "discord.js";
+import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
 import { SlashCommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
-import { testLink, testMalware, testNSFW } from '../utils/scanners.js';
+import { testLink, testMalware, testNSFW } from "../reflex/scanners.js";
+import EmojiEmbed from "../utils/generateEmojiEmbed.js";
 
 const command = new SlashCommandBuilder()
     .setName("privacy")
-    .setDescription("we changed the fucking charger again!")
-    .addStringOption(option => option.setName("link").setDescription("fuck you").setRequired(false))
+    .setDescription("Information and options for you and your server's settings")
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
-    console.log(await testLink(interaction.options.getString("link")))
+    let components = [];
+    if (interaction.inCachedGuild() && interaction.member.permissions.has("MANAGE_GUILD")) {
+        components.push(new MessageActionRow().addComponents([new MessageButton()
+            .setLabel("Clear all data")
+            .setEmoji("CONTROL.CROSS")
+            .setCustomId("clear")
+            .setStyle("DANGER")
+        ]));
+    }
+    await interaction.reply({embeds: [new EmojiEmbed()
+        .setTitle("Privacy")
+        .setDescription(
+            "**Link Scanning Types**\n" +
+            "> Facebook - Facebook trackers include data such as your date of birth, and guess your age if not entered, your preferences, who you interact with and more.\n" +
+            "> AMP - AMP is a technology that allows websites to be served by Google. This means Google can store and track data, and are pushing this to as many pages as possible.\n\n" +
+            "**Transcripts**\n" +
+            "> Transcripts allow you to store all messages sent in a channel. This could be an issue in some cases, as they are hosted on [Pastebin](https://pastebin.com), so a leaked link could show all messages sent in the channel.\n"
+        )
+        .setStatus("Success")
+        .setEmoji("NUCLEUS.COMMANDS.LOCK")
+    ], components: components});
 }
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
diff --git a/src/commands/role/all.ts b/src/commands/role/all.ts
index b2b8c52..7ea7cb6 100644
--- a/src/commands/role/all.ts
+++ b/src/commands/role/all.ts
@@ -114,7 +114,7 @@
                     count ++;
                     return (count == 1 ? getEmojiByName("ICONS.FILTER") : (all ? "**and** " : "**or** ")) +
                         (f.inverted ? "**not** " : "") + `${f.name}`
-                }).join("\n") + "\n\n" + `This will affect ${addPlural(affected.length.toString(), "member")}`)
+                }).join("\n") + "\n\n" + `This will affect ${addPlural(affected.length, "member")}`)
             .setEmoji("GUILD.ROLES.CREATE")
             .setStatus("Success")
         ], components: [
diff --git a/src/commands/rolemenu.ts b/src/commands/rolemenu.ts
index f53e10a..39e18f7 100644
--- a/src/commands/rolemenu.ts
+++ b/src/commands/rolemenu.ts
@@ -1,7 +1,7 @@
 import { CommandInteraction } from "discord.js";
 import { SlashCommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
-import { callback as roleMenu } from "../automations/roleMenu.js"
+import { callback as roleMenu } from "../actions/roleMenu.js"
 
 const command = new SlashCommandBuilder()
     .setName("rolemenu")
diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts
index e664bd0..ba16751 100644
--- a/src/commands/settings/tickets.ts
+++ b/src/commands/settings/tickets.ts
@@ -16,7 +16,7 @@
     .addStringOption(option => option.setName("enabled").setDescription("If users should be able to create tickets").setRequired(false)
         .addChoices([["Yes", "yes"], ["No", "no"]]))
     .addChannelOption(option => option.setName("category").setDescription("The category where tickets are created").addChannelType(ChannelType.GuildCategory).setRequired(false))
-    .addNumberOption(option => option.setName("maxticketsperuser").setDescription("The maximum amount of tickets a user can create | Default 5").setRequired(false).setMinValue(1))
+    .addNumberOption(option => option.setName("maxticketsperuser").setDescription("The maximum amount of tickets a user can create | Default: 5").setRequired(false).setMinValue(1))
     .addRoleOption(option => option.setName("supportrole").setDescription("This role will have view access to all tickets and will be pinged when a ticket is created").setRequired(false))
 
 const callback = async (interaction: CommandInteraction): Promise<any> => {
@@ -139,7 +139,7 @@
         }
     }
     let data = await client.database.guilds.read(interaction.guild.id);
-    data.tickets.customTypes = data.tickets.customTypes.filter((v, i, a) => a.indexOf(v) === i)
+    data.tickets.customTypes = (data.tickets.customTypes || []).filter((v, i, a) => a.indexOf(v) === i)
     let lastClicked = "";
     let embed;
     data = {
diff --git a/src/commands/tag.ts b/src/commands/tag.ts
index a9f0075..d032598 100644
--- a/src/commands/tag.ts
+++ b/src/commands/tag.ts
@@ -1,7 +1,7 @@
 import { CommandInteraction } from "discord.js";
 import { SlashCommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
-import { callback as statsChannelAdd } from '../automations/statsChannelAdd.js';
+import { callback as statsChannelAdd } from '../reflex/statsChannelAdd.js';
 import client from "../utils/client.js"
 
 const command = new SlashCommandBuilder()
diff --git a/src/commands/ticket/close.ts b/src/commands/ticket/close.ts
index 15abb0a..5a3371e 100644
--- a/src/commands/ticket/close.ts
+++ b/src/commands/ticket/close.ts
@@ -1,7 +1,7 @@
 import { CommandInteraction } from "discord.js";
 import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
-import close from "../../automations/tickets/delete.js";
+import close from "../../actions/tickets/delete.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
diff --git a/src/commands/ticket/create.ts b/src/commands/ticket/create.ts
index c44e576..3d796aa 100644
--- a/src/commands/ticket/create.ts
+++ b/src/commands/ticket/create.ts
@@ -1,7 +1,7 @@
 import { CommandInteraction } from "discord.js";
 import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
-import create from "../../automations/tickets/create.js";
+import create from "../../actions/tickets/create.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
diff --git a/src/commands/user/track.ts b/src/commands/user/track.ts
index da332da..54af9d7 100644
--- a/src/commands/user/track.ts
+++ b/src/commands/user/track.ts
@@ -163,7 +163,7 @@
 
 const check = async (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
     let tracks = (await client.database.guilds.read(interaction.guild.id)).tracks
-    if (!tracks) throw "This server does not have any tracks"
+    if (tracks.length === 0) throw "This server does not have any tracks"
     let member = (interaction.member as GuildMember)
     // Allow the owner to promote anyone
     if (member.id == interaction.guild.ownerId) return true
diff --git a/src/commands/verify.ts b/src/commands/verify.ts
index 4ccc807..bd71fe4 100644
--- a/src/commands/verify.ts
+++ b/src/commands/verify.ts
@@ -1,7 +1,7 @@
 import { CommandInteraction } from "discord.js";
 import { SlashCommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
-import verify from "../automations/verify.js";
+import verify from "../reflex/verify.js";
 
 const command = new SlashCommandBuilder()
     .setName("verify")
diff --git a/src/config/default.json b/src/config/default.json
index 3b0882f..81c7880 100644
--- a/src/config/default.json
+++ b/src/config/default.json
@@ -66,6 +66,10 @@
         },
         "staff": {
             "channel": null
+        },
+        "attachments": {
+            "channel": null,
+            "saved": {}
         }
     },
     "verify": {
diff --git a/src/config/emojis.json b/src/config/emojis.json
index e9fb578..2f32df1 100644
--- a/src/config/emojis.json
+++ b/src/config/emojis.json
@@ -20,6 +20,8 @@
         "EDIT": "989911820267577366",
         "HISTORY": "989911933069168690",
         "FILTER": "990242059451514902",
+        "ATTACHMENT": "997570687193587812",
+        "LOGGING": "999613304446144562",
         "OPP": {
             "ADD": "837355918831124500",
             "REMOVE": "837355918420869162"
@@ -40,6 +42,7 @@
     },
     "CONTROL": {
         "TICK": "947441964234702849",
+        "REDTICK": "999612396727439370",
         "CROSS": "947441948543815720",
         "BLOCKCROSS": "952261738349330493",
         "BLOCKTICK": "991805475777695855",
diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts
index 5c7139a..ffe3eae 100644
--- a/src/events/channelDelete.ts
+++ b/src/events/channelDelete.ts
@@ -39,7 +39,7 @@
             }
         }
         let list = {
-            channelIid: entry(channel.id, `\`${channel.id}\``),
+            channelId: entry(channel.id, `\`${channel.id}\``),
             name: entry(channel.id, `${channel.name}`),
             topic: null,
             type: entry(channel.type, readableType),
diff --git a/src/events/commandError.ts b/src/events/commandError.ts
index 8edb480..6d3672e 100644
--- a/src/events/commandError.ts
+++ b/src/events/commandError.ts
@@ -5,7 +5,7 @@
 export async function callback(client, interaction, error) {
     if (interaction.replied || interaction.deferred) {
         await interaction.followUp({embeds: [new EmojiEmbed()
-            .setTitle("Something went wrong")
+            .setTitle("Something went")
             .setDescription(error.message ?? error.toString())
             .setStatus("Danger")
             .setEmoji("CONTROL.BLOCKCROSS")
diff --git a/src/events/guildBanAdd.ts b/src/events/guildBanAdd.ts
index 05b98ed..64a1604 100644
--- a/src/events/guildBanAdd.ts
+++ b/src/events/guildBanAdd.ts
@@ -1,5 +1,5 @@
-import { purgeByUser } from '../automations/tickets/delete.js';
-import { callback as statsChannelRemove } from '../automations/statsChannelRemove.js';
+import { purgeByUser } from '../actions/tickets/delete.js';
+import { callback as statsChannelRemove } from '../reflex/statsChannelRemove.js';
 
 export const event = 'guildBanAdd';
 
diff --git a/src/events/guildBanRemove.ts b/src/events/guildBanRemove.ts
index 85dc2af..a6d1c14 100644
--- a/src/events/guildBanRemove.ts
+++ b/src/events/guildBanRemove.ts
@@ -1,6 +1,6 @@
 import humanizeDuration from 'humanize-duration';
-import { purgeByUser } from '../automations/tickets/delete.js';
-import { callback as statsChannelRemove } from '../automations/statsChannelRemove.js';
+import { purgeByUser } from '../actions/tickets/delete.js';
+import { callback as statsChannelRemove } from '../reflex/statsChannelRemove.js';
 
 export const event = 'guildBanRemove';
 
diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts
index 10986a5..413e5fb 100644
--- a/src/events/guildCreate.ts
+++ b/src/events/guildCreate.ts
@@ -1,7 +1,7 @@
 import { MessageActionRow, MessageButton } from "discord.js";
 import EmojiEmbed from "../utils/generateEmojiEmbed.js";
 import getEmojiByName from "../utils/getEmojiByName.js";
-import guide from "../automations/guide.js";
+import guide from "../reflex/guide.js";
 
 export const event = 'guildCreate';
 
diff --git a/src/events/guildMemberUpdate.ts b/src/events/guildMemberUpdate.ts
index dab89fa..e68aee4 100644
--- a/src/events/guildMemberUpdate.ts
+++ b/src/events/guildMemberUpdate.ts
@@ -1,15 +1,12 @@
-import { callback as statsChannelAdd } from '../automations/statsChannelAdd.js';
-import { callback as welcome } from '../automations/welcome.js';
-import log from '../utils/log.js';
 export const event = 'guildMemberUpdate'
 
 export async function callback(client, before, after) {
     try {
         const { log, NucleusColors, entry, renderUser, renderDelta, getAuditLog } = after.client.logger
+        let auditLog = await getAuditLog(after.guild, 'MEMBER_UPDATE');
+        let audit = auditLog.entries.filter(entry => entry.target.id == after.id).first();
+        if (audit.executor.id == client.user.id) return;
         if (before.nickname != after.nickname) {
-            let auditLog = await getAuditLog(after.guild, 'MEMBER_UPDATE');
-            let audit = auditLog.entries.filter(entry => entry.target.id == after.id).first();
-            if (audit.executor.id == client.user.id) return;
             try { await client.database.history.create(
                 "nickname", after.guild.id, after.user, audit.executor,
                 null, before.nickname || before.user.username, after.nickname || after.user.username) } catch {}
@@ -24,16 +21,74 @@
                 },
                 list: {
                     memberId: entry(after.id, `\`${after.id}\``),
+                    name: entry(after.user.id, renderUser(after.user)),
                     before: entry(before.nickname, before.nickname ? before.nickname : '*None*'),
                     after: entry(after.nickname, after.nickname ? after.nickname : '*None*'),
-                    updated: entry(new Date().getTime(), renderDelta(new Date().getTime())),
-                    updatedBy: entry(audit.executor.id, renderUser(audit.executor))
+                    changed: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+                    changedBy: entry(audit.executor.id, renderUser(audit.executor))
                 },
                 hidden: {
                     guild: after.guild.id
                 }
             }
             log(data);
+        } else if (before.communicationDisabledUntilTimestamp < new Date().getTime() && after.communicationDisabledUntil > new Date().getTime()) {
+            try { await client.database.history.create(
+                "mute", after.guild.id, after.user, audit.executor, audit.reason, null, null, null
+            )} catch {}
+            let data = {
+                meta: {
+                    type: 'memberMute',
+                    displayName: 'Muted',
+                    calculateType: 'guildMemberPunish',
+                    color: NucleusColors.yellow,
+                    emoji: "PUNISH.MUTE.YELLOW",
+                    timestamp: new Date().getTime()
+                },
+                list: {
+                    memberId: entry(after.id, `\`${after.id}\``),
+                    name: entry(after.user.id, renderUser(after.user)),
+                    mutedUntil: entry(after.communicationDisabledUntilTimestamp, renderDelta(after.communicationDisabledUntilTimestamp)),
+                    muted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+                    mutedBy: entry(audit.executor.id, renderUser(audit.executor)),
+                    reason: entry(audit.reason, audit.reason ? audit.reason : '\n> *No reason provided*')
+                },
+                hidden: {
+                    guild: after.guild.id
+                }
+            }
+            log(data);
+            client.database.eventScheduler.schedule("naturalUnmute", after.communicationDisabledUntil,
+                {guild: after.guild.id, user: after.id, expires: after.communicationDisabledUntilTimestamp}
+            );
+        } else if (
+            after.communicationDisabledUntil === null && before.communicationDisabledUntilTimestamp !== null &&
+            new Date().getTime() >= audit.createdTimestamp
+        ) {
+            try { await client.database.history.create(
+                "unmute", after.guild.id, after.user, audit.executor, null, null, null, null
+            )} catch {}
+            let data = {
+                meta: {
+                    type: 'memberUnmute',
+                    displayName: 'Unmuted',
+                    calculateType: 'guildMemberPunish',
+                    color: NucleusColors.green,
+                    emoji: "PUNISH.MUTE.GREEN",
+                    timestamp: new Date().getTime()
+                },
+                list: {
+                    memberId: entry(after.id, `\`${after.id}\``),
+                    name: entry(after.user.id, renderUser(after.user)),
+                    unmuted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+                    unmutedBy: entry(audit.executor.id, renderUser(audit.executor))
+                },
+                hidden: {
+                    guild: after.guild.id
+                }
+            }
+            log(data);
+            client.database.eventScheduler.cancel("naturalUnmute", {guild: after.guild.id, user: after.id, expires: before.communicationDisabledUntilTimestamp});
         }
     } catch (e) { console.log(e) }
 }
diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts
index 3470450..1fd1e1f 100644
--- a/src/events/interactionCreate.ts
+++ b/src/events/interactionCreate.ts
@@ -1,7 +1,8 @@
-import { callback as roleMenu } from "../automations/roleMenu.js"
-import verify from "../automations/verify.js";
-import create from "../automations/tickets/create.js";
-import close from "../automations/tickets/delete.js";
+import { callback as roleMenu } from "../actions/roleMenu.js"
+import verify from "../reflex/verify.js";
+import create from "../actions/tickets/create.js";
+import close from "../actions/tickets/delete.js";
+import createTranscript from "../premium/createTranscript.js";
 
 export const event = 'interactionCreate';
 
@@ -11,6 +12,7 @@
         if (interaction.customId === "verifybutton") return verify(interaction)
         if (interaction.customId === "createticket") return create(interaction)
         if (interaction.customId === "closeticket") return close(interaction)
+        if (interaction.customId === "createtranscript") return createTranscript(interaction)
     } else if (interaction.componentType === "MESSAGE_COMPONENT") {
         console.table(interaction)
     }
diff --git a/src/events/memberJoin.ts b/src/events/memberJoin.ts
index 2031b12..b1c2700 100644
--- a/src/events/memberJoin.ts
+++ b/src/events/memberJoin.ts
@@ -1,5 +1,5 @@
-import { callback as statsChannelAdd } from '../automations/statsChannelAdd.js';
-import { callback as welcome } from '../automations/welcome.js';
+import { callback as statsChannelAdd } from '../reflex/statsChannelAdd.js';
+import { callback as welcome } from '../reflex/welcome.js';
 import log from '../utils/log.js';
 import client from '../utils/client.js';
 
diff --git a/src/events/memberLeave.ts b/src/events/memberLeave.ts
index b9d6e84..592a630 100644
--- a/src/events/memberLeave.ts
+++ b/src/events/memberLeave.ts
@@ -1,6 +1,6 @@
 import humanizeDuration from 'humanize-duration';
-import { purgeByUser } from '../automations/tickets/delete.js';
-import { callback as statsChannelRemove } from '../automations/statsChannelRemove.js';
+import { purgeByUser } from '../actions/tickets/delete.js';
+import { callback as statsChannelRemove } from '../reflex/statsChannelRemove.js';
 
 export const event = 'guildMemberRemove'
 
diff --git a/src/events/messageChecks.ts b/src/events/messageChecks.ts
deleted file mode 100644
index 2755ff1..0000000
--- a/src/events/messageChecks.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { LinkCheck, MalwareCheck, NSFWCheck, SizeCheck, TestString, TestImage } from '../automations/unscan.js'
-import { Message } from 'discord.js'
-import client from '../utils/client.js'
-
-export const event = 'messageCreate'
-
-export async function callback(client, message) {
-    const { log, NucleusColors, entry, renderUser } = client.logger
-    if (message.author.bot) return
-    if (message.channel.type === 'dm') return
-
-    let content = message.content.toLowerCase() || ''
-    let config = await client.memory.readGuildInfo(message.guild.id);
-
-    if (config.filters.invite.enabled) {
-        if (!config.filters.invite.allowed.users.includes(message.author.id) ||
-            !config.filters.invite.allowed.channels.includes(message.channel.id) ||
-            !message.author.roles.cache.some(role => config.filters.invite.allowed.roles.includes(role.id))
-        ) {
-            if ((/(?:https?:\/\/)?discord(?:app)?\.(?:com\/invite|gg)\/[a-zA-Z0-9]+\/?/.test(content))) {
-                return message.delete();
-            }
-        }
-    }
-
-    let attachments = message.attachments.map(element => element)
-    attachments = [...attachments, ...content.match(
-        /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi
-    ) ?? []].filter(element => (element.url ? element.url : element))
-    if (attachments.length > 0) {
-        attachments.forEach(async element => {
-            if(!message) return;
-            let url = element.url ? element.url : element
-            if (url != undefined) {
-                if(/\.+(webp|png|jpg|jpeg|bmp)/.test(url)) {
-                    if (config.filters.images.NSFW && !message.channel.nsfw) {
-                        if (await NSFWCheck(url)) {
-                            return await message.delete()
-                        }
-                    }
-                    if (config.filters.images.size) {
-                        if(!url.match(/\.+(webp|png|jpg)$/gi)) return
-                        if(!await SizeCheck(element)) {
-                            return await message.delete()
-                        }
-                    }
-                }
-                if (config.filters.malware) {
-                    if (!MalwareCheck(url)) {
-                        return await message.delete()
-                    }
-                }
-            }
-        });
-    }
-    if(!message) return;
-
-    if (await LinkCheck(message)) {
-        return await message.delete()
-    }
-
-    if (config.filters.wordFilter.enabled) {
-        let check = TestString(content, config.filters.wordFilter.words.loose, config.filters.wordFilter.words.strict)
-        if(check !== null) {
-            return await message.delete()
-        }
-    }
-
-    if (!config.filters.pings.allowed.users.includes(message.author.id) ||
-        !config.filters.pings.allowed.channels.includes(message.channel.id) ||
-        !message.author.roles.cache.some(role => config.filters.pings.allowed.roles.includes(role.id))
-    ) {
-        if (config.filters.pings.everyone && message.mentions.everyone) {
-            return message.delete();
-        }
-        if (config.filters.pings.roles) {
-            for(let role of message.mentions.roles) {
-                if(!message) return;
-                if (!config.filters.pings.allowed.roles.includes(role.id)) {
-                    return message.delete();
-                }
-            }
-        }
-        if(!message) return;
-        if (message.mentions.users.size >= config.filters.pings.mass && config.filters.pings.mass) {
-            return message.delete();
-        }
-    }
-}
diff --git a/src/events/messageDelete.ts b/src/events/messageDelete.ts
index f0fbb57..2263d51 100644
--- a/src/events/messageDelete.ts
+++ b/src/events/messageDelete.ts
@@ -3,6 +3,7 @@
 export async function callback(client, message) {
     try {
         if (message.author.id == client.user.id) return;
+        if (client.noLog.includes(`${message.guild.id}/${message.channel.id}/${message.id}`)) return;
         const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = message.channel.client.logger
         let auditLog = await getAuditLog(message.guild, 'MEMBER_BAN_ADD')
         let audit = auditLog.entries.filter(entry => entry.target.id == message.author.id).first();
@@ -11,7 +12,11 @@
         }
         message.reference = message.reference || {}
         let content = message.cleanContent
+        content.replace(`\``, `\\\``)
         if (content.length > 256) content = content.substring(0, 253) + '...'
+        let attachmentJump = ""
+        let config = (await client.database.guilds.read(message.guild.id)).logging.attachments.saved[message.channel.id + message.id];
+        if (config) { attachmentJump = ` [[View attachments]](${config})` }
         let data = {
             meta: {
                 type: 'messageDelete',
@@ -30,7 +35,7 @@
                 sentIn: entry(message.channel.id, renderChannel(message.channel)),
                 deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
                 mentions: message.mentions.users.size,
-                attachments: message.attachments.size,
+                attachments: entry(message.attachments.size, message.attachments.size + attachmentJump),
                 repliedTo: entry(
                     message.reference.messageId || null,
                     message.reference.messageId ? `[[Jump to message]](https://discord.com/channels/${message.guild.id}/${message.channel.id}/${message.reference.messageId})` : "None"
@@ -41,5 +46,5 @@
             }
         }
         log(data);
-    } catch {}
+    } catch(e) { console.log(e) }
 }
diff --git a/src/events/messageEdit.ts b/src/events/messageEdit.ts
index 3502963..5e63797 100644
--- a/src/events/messageEdit.ts
+++ b/src/events/messageEdit.ts
@@ -7,6 +7,9 @@
         newMessage.reference = newMessage.reference || {}
         let newContent = newMessage.cleanContent.replaceAll("`", "‘")
         let oldContent = oldMessage.cleanContent.replaceAll("`", "‘")
+        let attachmentJump = "";
+        let config = (await client.database.guilds.read(newMessage.guild.id)).logging.attachments.saved[newMessage.channel.id + newMessage.id];
+        if (config) { attachmentJump = ` [[View attachments]](${config})` }
         if (newContent == oldContent) {
             if (!oldMessage.flags.has("CROSSPOSTED") && newMessage.flags.has("CROSSPOSTED")) {
                 let data = {
@@ -28,16 +31,19 @@
                         sent: entry(new Date(newMessage.createdTimestamp), renderDelta(new Date(newMessage.createdTimestamp))),
                         published: entry(new Date(newMessage.editedTimestamp), renderDelta(new Date(newMessage.editedTimestamp))),
                         mentions: renderNumberDelta(oldMessage.mentions.users.size, newMessage.mentions.users.size),
-                        attachments: renderNumberDelta(oldMessage.attachments.size, newMessage.attachments.size)
+                        attachments: entry(
+                            renderNumberDelta(oldMessage.attachments.size, newMessage.attachments.size),
+                            renderNumberDelta(oldMessage.attachments.size, newMessage.attachments.size) + attachmentJump
+                        )
                     },
                     hidden: {
                         guild: newMessage.channel.guild.id
                     }
                 }
-                log(data);
+                return log(data);
             }
-            return
         };
+        if (!newMessage.editedTimestamp) { return }
         if (newContent.length > 256) newContent = newContent.substring(0, 253) + '...'
         if (oldContent.length > 256) oldContent = oldContent.substring(0, 253) + '...'
         let data = {
@@ -61,7 +67,10 @@
                 sent: entry(new Date(newMessage.createdTimestamp), renderDelta(new Date(newMessage.createdTimestamp))),
                 edited: entry(new Date(newMessage.editedTimestamp), renderDelta(new Date(newMessage.editedTimestamp))),
                 mentions: renderNumberDelta(oldMessage.mentions.users.size, newMessage.mentions.users.size),
-                attachments: renderNumberDelta(oldMessage.attachments.size, newMessage.attachments.size),
+                attachments: entry(
+                    renderNumberDelta(oldMessage.attachments.size, newMessage.attachments.size),
+                    renderNumberDelta(oldMessage.attachments.size, newMessage.attachments.size) + attachmentJump
+                ),
                 repliedTo: entry(
                     newMessage.reference.messageId || null,
                     newMessage.reference.messageId ? `[[Jump to message]](https://discord.com/channels/${newMessage.guild.id}/${newMessage.channel.id}/${newMessage.reference.messageId})` : "None"
diff --git a/src/events/roleUpdate.ts b/src/events/roleUpdate.ts
index baf4399..7c6ca75 100644
--- a/src/events/roleUpdate.ts
+++ b/src/events/roleUpdate.ts
@@ -13,7 +13,7 @@
         let changes = {
             roleId: entry(nr.id, `\`${nr.id}\``),
             role: entry(nr.id, renderRole(nr)),
-            edited: entry(nr.createdTimestamp, renderDelta(nr.createdTimestamp)),
+            edited: entry(new Date().getTime(), renderDelta(new Date().getTime())),
             editedBy: entry(audit.executor.id, renderUser((await nr.guild.members.fetch(audit.executor.id)).user)),
         }
         let mentionable = ["", ""]
@@ -28,6 +28,8 @@
         if (or.mentionable != nr.mentionable) changes["mentionable"] = entry([or.mentionable, nr.mentionable], `${mentionable[0]} -> ${mentionable[1]}`);
         if (or.hexColor != nr.hexColor) changes["color"] = entry([or.hexColor, nr.hexColor], `\`${or.hexColor}\` -> \`${nr.hexColor}\``);
 
+        if (Object.keys(changes).length == 4) return
+
         let data = {
             meta:{
                 type: 'roleUpdate',
@@ -41,7 +43,7 @@
             hidden: {
                 guild: nr.guild.id
             }
-        } // TODO: show perms changed
+        } // TODO: show perms changed (webpage)
         log(data);
     } catch {}
 }
\ No newline at end of file
diff --git a/src/events/webhookUpdate.ts b/src/events/webhookUpdate.ts
index a20608c..66b1cd0 100644
--- a/src/events/webhookUpdate.ts
+++ b/src/events/webhookUpdate.ts
@@ -72,7 +72,7 @@
             hidden: {
                 guild: channel.guild.id
             }
-        } // TODO
+        }
         log(data);
     } catch(e) { console.log(e) }
 }
diff --git a/src/index.ts b/src/index.ts
index 35afc14..0ae917c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,7 +2,7 @@
 import { Logger } from './utils/log.js';
 import runServer from './api/index.js';
 import Memory from './utils/memory.js';
-import { Guilds, History, ModNotes, EventSchedulerDatabase } from './utils/database.js';
+import { Guilds, History, ModNotes, Premium } from './utils/database.js';
 import client from './utils/client.js';
 import EventScheduler from './utils/eventScheduler.js';
 
@@ -16,11 +16,13 @@
 client.verify = {}
 client.roleMenu = {}
 client.memory = new Memory()
+client.noLog = []
 client.database = {
     guilds: await new Guilds().setup(),
     history: await new History().setup(),
     notes: await new ModNotes().setup(),
-    eventScheduler: new EventSchedulerDatabase()
+    premium: await new Premium().setup(),
+    eventScheduler: await new EventScheduler().start()
 }
 
 await client.login();
\ No newline at end of file
diff --git a/src/utils/calculate.ts b/src/utils/calculate.ts
index 7f3b16d..6b8c058 100644
--- a/src/utils/calculate.ts
+++ b/src/utils/calculate.ts
@@ -11,14 +11,14 @@
     "messageDelete",
     "messageDeleteBulk",
     "messageReactionUpdate",
-    "messagePing",
     "messageMassPing",
     "messageAnnounce",
     "threadUpdate",
     "webhookUpdate",
     "guildMemberVerify",
-    "autoModeratorDeleted", // TODO: Not implemented
-    "nucleusSettingsUpdated"
+    "autoModeratorDeleted",
+    "nucleusSettingsUpdated",
+    "ticketUpdate"
 ]
 
 const tickets = [
diff --git a/src/utils/confirmationMessage.ts b/src/utils/confirmationMessage.ts
index 7eaa369..da10cfb 100644
--- a/src/utils/confirmationMessage.ts
+++ b/src/utils/confirmationMessage.ts
@@ -1,33 +1,27 @@
-import Discord, { CommandInteraction, MessageActionRow, Message } from "discord.js";
+import Discord, { CommandInteraction, MessageActionRow, Message, MessageButton, TextInputComponent } from "discord.js";
+import { modalInteractionCollector } from "./dualCollector.js";
 import EmojiEmbed from "./generateEmojiEmbed.js"
 import getEmojiByName from "./getEmojiByName.js";
 
 class confirmationMessage {
     interaction: CommandInteraction;
-    title: string;
-    emoji: string;
-    description: string;
-    color: string;
-    customCallback: () => any;
+    title: string = "";
+    emoji: string = "";
+    description: string = "";
+    color: string = "";
+    customCallback: () => any = () => {};
     customButtonTitle: string;
     customButtonDisabled: boolean;
     customCallbackString: string = "";
     customCallbackClicked: boolean = false;
     customCallbackResponse: any = null;
-    customBoolean: () => any;
+    customBoolean: () => any = () => {}; // allow multiple booleans
     customBooleanClicked: boolean = null;
-    inverted: boolean;
+    inverted: boolean = false;
+    reason: string | null = null;
 
     constructor(interaction: CommandInteraction) {
         this.interaction = interaction;
-
-        this.title = "";
-        this.emoji = "";
-        this.description = "";
-        this.color = "";
-        this.inverted = false;
-        this.customCallback = () => {};
-        this.customBoolean = () => {};
     }
 
     setTitle(title: string) { this.title = title; return this }
@@ -52,8 +46,10 @@
         this.customBooleanClicked = false;
         return this;
     }
-
-
+    addReasonButton(reason: string) {
+        this.reason = reason;
+        return this;
+    }
     async send(editOnly?: boolean) {
         while (true) {
             let object = {
@@ -86,6 +82,12 @@
                         )
                         .setDisabled(this.customButtonDisabled)
                         .setEmoji(getEmojiByName("CONTROL.TICKET", "id"))
+                    ] : [])
+                    .concat(this.reason !== null ? [new Discord.MessageButton()
+                        .setCustomId("reason")
+                        .setLabel(`Edit Reason`)
+                        .setStyle("PRIMARY")
+                        .setEmoji(getEmojiByName("ICONS.EDIT", "id"))
                     ] : []))
                 ],
                 ephemeral: true,
@@ -132,9 +134,42 @@
                     this.customButtonDisabled = true;
                 }
                 editOnly = true;
+            } else if (component.customId === "reason") {
+                await component.showModal(new Discord.Modal().setCustomId("modal").setTitle(`Editing reason`).addComponents(
+                    // @ts-ignore
+                    new MessageActionRow().addComponents(new TextInputComponent()
+                        .setCustomId("reason")
+                        .setLabel("Reason")
+                        .setMaxLength(2000)
+                        .setRequired(false)
+                        .setStyle("PARAGRAPH")
+                        .setPlaceholder("Spammed in #general")
+                        .setValue(this.reason ? this.reason : "")
+                    )
+                ))
+                await this.interaction.editReply({
+                    embeds: [new EmojiEmbed()
+                        .setTitle(this.title)
+                        .setDescription("Modal opened. If you can't see it, click back and try again.")
+                        .setStatus(this.color)
+                        .setEmoji(this.emoji)
+                    ], components: [new MessageActionRow().addComponents([new MessageButton()
+                        .setLabel("Back")
+                        .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
+                        .setStyle("PRIMARY")
+                        .setCustomId("back")
+                    ])]
+                });
+                let out;
+                try {
+                    out = await modalInteractionCollector(m, (m) => m.channel.id == this.interaction.channel.id, (m) => m.customId == "reason")
+                } catch (e) { continue }
+                if (out.fields) {
+                    return {newReason: out.fields.getTextInputValue("reason") ?? ""};
+                } else { return { newReason: this.reason } }
             }
         }
     }
 }
 
-export default confirmationMessage;
\ No newline at end of file
+export default confirmationMessage;
diff --git a/src/utils/database.ts b/src/utils/database.ts
index 66d7e67..c0ae9be 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -132,26 +132,17 @@
     }
 }
 
-export class EventSchedulerDatabase {
-    events: Collection<EventSchedulerSchema>;
-    defaultData: GuildConfig;
+export class Premium {
+    premium: Collection<PremiumSchema>;
 
     async setup() {
-        this.events = database.collection<EventSchedulerSchema>("eventScheduler");
+        this.premium = database.collection<PremiumSchema>("premium");
         return this;
     }
 
-    async create(timestamp: Date, data: object) {
-        await this.events.insertOne({ timestamp: timestamp, data: data});
-    }
-
-    async getNext() {
-        let entry = await this.events.findOne({ timestamp: { $lte: new Date() }});
-        return entry;
-    }
-
-    async remove(timestamp: Date, data: object) {
-        await this.events.deleteOne({ timestamp: timestamp, data: data});
+    async hasPremium(guild: string) {
+        let entry = await this.premium.findOne({ appliesTo: { $in: [guild] } });
+        return entry != null;
     }
 }
 
@@ -222,6 +213,10 @@
         },
         staff: {
             channel: string | null,
+        },
+        attachments: {
+            channel: string | null,
+            saved: {}  // {channelID+messageID: log url (string)}
         }
     }
     verify: {
@@ -307,7 +302,9 @@
     note: string
 }
 
-export interface EventSchedulerSchema {
-    timestamp: Date,
-    data: object
+export interface PremiumSchema {
+    user: string,
+    level: number,
+    expires: Date,
+    appliesTo: string[]
 }
\ No newline at end of file
diff --git a/src/utils/eventScheduler.ts b/src/utils/eventScheduler.ts
index 9210883..396b202 100644
--- a/src/utils/eventScheduler.ts
+++ b/src/utils/eventScheduler.ts
@@ -1,37 +1,70 @@
-import { EventSchedulerDatabase } from './database';
+import { Agenda } from "agenda/es.js";
 import client from './client.js';
+import * as fs from 'fs';
+import * as path from 'path';
+import config from '../config/main.json' assert {type: 'json'};
 
 class EventScheduler {
-    database: any;
-    next: {timestamp: Date, data: object, responded: boolean} | {};
+    private agenda: Agenda;
 
     constructor() {
-        this.database = EventSchedulerDatabase;
-        this.next = {};
+        this.agenda = new Agenda({db: {address: config.mongoUrl + "Nucleus", collection: 'eventScheduler'}})
+
+        this.agenda.define("unmuteRole", async (job: Agenda.job) => {
+            let guild = await client.guilds.fetch(job.attrs.data.guild);
+            let user = await guild.members.fetch(job.attrs.data.user);
+            let role = await guild.roles.fetch(job.attrs.data.role);
+            await user.roles.remove(role);
+            await job.remove();
+        })
+        this.agenda.define("deleteFile", async (job: Agenda.job) => {
+            fs.rm(path.resolve("dist/utils/temp", job.attrs.data.fileName), (err) => {})
+            await job.remove();
+        })
+        this.agenda.define("naturalUnmute", async (job: Agenda.job) => {
+            const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger
+            let guild = await client.guilds.fetch(job.attrs.data.guild);
+            let user = await guild.members.fetch(job.attrs.data.user);
+            if (user.communicationDisabledUntil === null) return
+            try { await client.database.history.create(
+                "unmute", user.guild.id, user.user, null, null, null, null
+            )} catch {}
+            let data = {
+                meta: {
+                    type: 'memberUnmute',
+                    displayName: 'Unmuted',
+                    calculateType: 'guildMemberPunish',
+                    color: NucleusColors.green,
+                    emoji: "PUNISH.MUTE.GREEN",
+                    timestamp: new Date().getTime()
+                },
+                list: {
+                    memberId: entry(user.user.id, `\`${user.user.id}\``),
+                    name: entry(user.user.id, renderUser(user.user)),
+                    unmuted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+                    unmutedBy: entry(null, "*Time out ended*")
+                },
+                hidden: {
+                    guild: guild.id
+                }
+            }
+            log(data);
+        })
     }
 
-    async create(timestamp: Date, data: object) {
-        await this.database.create(timestamp, data);
-        if (this.next === {}) {
-            this.next = this.next = await this.getNext();
-            return
-        }
-        if (timestamp.getTime() < (this.next as {timestamp: Date}).timestamp.getTime()) {
-            this.next = {timestamp: timestamp, data: data, responded: false};
-        }
+    async start() {
+        await new Promise(resolve => this.agenda.once('ready', resolve));
+        this.agenda.start()
+        return this
     }
 
-    async getNext() {
-        let entry = await this.database.getNext();
-        if (entry) {
-            this.next = entry;
-        }
-        return this.next;
+    async schedule(name: string, time: string, data: any) {
+        await this.agenda.schedule(time, name, data)
     }
 
-    async delete(timestamp: Date, data: object) {
-        await this.database.delete(timestamp, data);
-    } // TODO: add a loop
+    cancel(name: string, data: Object) {
+        this.agenda.cancel({name, data})
+    }
 }
 
 export default EventScheduler;
\ No newline at end of file
diff --git a/src/utils/generateKeyValueList.ts b/src/utils/generateKeyValueList.ts
index c9eb0c8..1a76fa7 100644
--- a/src/utils/generateKeyValueList.ts
+++ b/src/utils/generateKeyValueList.ts
@@ -1,14 +1,18 @@
 const forceCaps = [
     "ID",
-    "NSFW"
+    "NSFW",
+    "URL"
 ]
 
 export function capitalize(s: string) {
     s = s.replace(/([A-Z])/g, ' $1');
-    return forceCaps.includes(s.toUpperCase()) ? s.toUpperCase() : s[0]
-        .toUpperCase() + s.slice(1)
-        .toLowerCase()
-        .replace("discord", "Discord");
+    s = s.split(" ").map(word => {
+        return forceCaps.includes(word.toUpperCase()) ? word.toUpperCase() : word[0]
+            .toUpperCase() + word.slice(1)
+            .toLowerCase()
+            .replace("discord", "Discord")
+    }).join(" ");
+    return s
 }
 
 export function toCapitals(s: string) {
diff --git a/src/utils/plurals.ts b/src/utils/plurals.ts
index f956057..48b889c 100644
--- a/src/utils/plurals.ts
+++ b/src/utils/plurals.ts
@@ -1,4 +1,5 @@
 function addPlural(amount: any, unit: string) {
+    amount = amount.toString();
     if (amount === '1') return `${amount} ${unit}`
     return `${amount} ${unit}s`
 }
diff --git a/src/utils/scanners.ts b/src/utils/scanners.ts
deleted file mode 100644
index 5abd726..0000000
--- a/src/utils/scanners.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import * as us from 'unscan'
-import fetch from 'node-fetch'
-import { writeFileSync } from 'fs'
-import generateFileName from './temp/generateFileName.js'
-import * as path from 'path'
-import {fileURLToPath} from 'url';
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
-
-export async function testNSFW(link: string): Promise<JSON> {
-    const image = (await (await fetch(link)).buffer()).toString('base64')
-    let fileName = generateFileName(link.split('/').pop().split('.').pop())
-    let p = path.join(__dirname, '/temp', fileName)
-    writeFileSync(p, image, 'base64')
-    let result = await us.nsfw.file(p)
-    return result
-}
-
-export async function testMalware(link: string): Promise<JSON> {
-    const file = (await (await fetch(link)).buffer()).toString('base64')
-    let fileName = generateFileName(link.split('/').pop().split('.').pop())
-    let p = path.join(__dirname, '/temp', fileName)
-    writeFileSync(p, file, 'base64')
-    let result = await us.malware.file(p)
-    return result
-}
-
-export async function testLink(link: string): Promise<JSON> {
-    return await us.link.scan(link)
-}
diff --git a/src/utils/temp/generateFileName.ts b/src/utils/temp/generateFileName.ts
index 98240a1..f9662ad 100644
--- a/src/utils/temp/generateFileName.ts
+++ b/src/utils/temp/generateFileName.ts
@@ -1,5 +1,10 @@
 import * as fs from 'fs';
 import * as crypto from 'crypto';
+import client from '../client.js';
+import * as path from 'path'
+import {fileURLToPath} from 'url';
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
 
 export default function generateFileName(ending: string): string {
     let fileName = crypto.randomBytes(35).toString('hex');
@@ -7,5 +12,6 @@
     if (fs.existsSync(`./${fileName}`)) {
         fileName = generateFileName(ending);
     }
-    return fileName + '.' + ending;
-}
\ No newline at end of file
+    client.database.eventScheduler.schedule("deleteFile", new Date().getTime() + (60 * 1000), {fileName: `${fileName}.${ending}`});
+    return path.join(__dirname, fileName + '.' + ending);
+}