Fixed welcome
diff --git a/src/commands/createTestButton.ts b/src/commands/createTestButton.ts
deleted file mode 100644
index c04e13d..0000000
--- a/src/commands/createTestButton.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
-import { SlashCommandBuilder } from "@discordjs/builders";
-import { WrappedCheck } from "jshaiku";
-
-const command = new SlashCommandBuilder()
-    .setName("createtestbutton")
-    .setDescription("creates a test button") // TODO: remove for release
-
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply({components: [new MessageActionRow().addComponents([
-        new MessageButton()
-            .setCustomId("createticket")
-            .setLabel("Create Ticket")
-            .setStyle("PRIMARY")
-            .setDisabled(false),
-        new MessageButton()
-            .setCustomId("verifybutton")
-            .setLabel("Verify")
-            .setStyle("PRIMARY")
-            .setDisabled(false),
-        new MessageButton()
-            .setCustomId("rolemenu")
-            .setLabel("Get roles")
-            .setStyle("PRIMARY")
-            .setDisabled(false)
-    ])]});
-}
-
-const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
-    return true;
-}
-
-export { command };
-export { callback };
-export { check };
\ No newline at end of file
diff --git a/src/commands/help.ts b/src/commands/help.ts
index 568a90c..c3b015c 100644
--- a/src/commands/help.ts
+++ b/src/commands/help.ts
@@ -6,7 +6,7 @@
     .setName("help")
     .setDescription("Shows help for commands")
 
-const callback = (interaction: CommandInteraction) => {
+const callback = async (interaction: CommandInteraction): Promise<any> => {
     interaction.reply("hel p"); // TODO: FINISH THIS FOR RELEASE
 }
 
diff --git a/src/commands/nucleus/invite.ts b/src/commands/nucleus/invite.ts
index 7f7d4b8..96e1449 100644
--- a/src/commands/nucleus/invite.ts
+++ b/src/commands/nucleus/invite.ts
@@ -9,7 +9,7 @@
     .setName("invite")
     .setDescription("Invites Nucleus to your server")
 
-const callback = (interaction: CommandInteraction) => {
+const callback = async (interaction: CommandInteraction): Promise<any> => {
     interaction.reply({embeds: [new EmojiEmbed()
         .setTitle("Invite")
         .setDescription("You can invite Nucleus to your server by clicking the button below")
diff --git a/src/commands/nucleus/premium.ts b/src/commands/nucleus/premium.ts
index 740ab7b..9d273b9 100644
--- a/src/commands/nucleus/premium.ts
+++ b/src/commands/nucleus/premium.ts
@@ -8,7 +8,7 @@
     .setName("premium")
     .setDescription("Information about Nucleus Premium")
 
-const callback = (interaction: CommandInteraction) => {
+const callback = async (interaction: CommandInteraction): Promise<any> => {
     interaction.reply({embeds: [new EmojiEmbed()
         .setTitle("Premium")
         .setDescription(
diff --git a/src/commands/nucleus/stats.ts b/src/commands/nucleus/stats.ts
index cb10e7a..beea94b 100644
--- a/src/commands/nucleus/stats.ts
+++ b/src/commands/nucleus/stats.ts
@@ -9,7 +9,7 @@
     .setName("stats")
     .setDescription("Gets the bot's stats")
 
-const callback = (interaction: CommandInteraction) => {
+const callback = async (interaction: CommandInteraction): Promise<any> => {
     interaction.reply({
         embeds: [new EmojiEmbed()
             .setTitle("Stats")
diff --git a/src/commands/settings/filters.ts b/src/commands/settings/filters.ts
new file mode 100644
index 0000000..183b91c
--- /dev/null
+++ b/src/commands/settings/filters.ts
@@ -0,0 +1,29 @@
+import { LoadingEmbed } from './../../utils/defaultEmbeds.js';
+import Discord, { CommandInteraction, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import client from '../../utils/client.js';
+import confirmationMessage from '../../utils/confirmationMessage.js';
+import generateKeyValueList from '../../utils/generateKeyValueList.js';
+import { ChannelType } from 'discord-api-types';
+import getEmojiByName from '../../utils/getEmojiByName.js';
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+    .setName("filter")
+    .setDescription("Setting for message filters")
+
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the *Manage Server* permission to use this command"
+    return true;
+}
+
+export { command };
+export { callback };
+export { check };
\ No newline at end of file
diff --git a/src/commands/settings/logs/_meta.ts b/src/commands/settings/logs/_meta.ts
index 15a6fd4..f46987f 100644
--- a/src/commands/settings/logs/_meta.ts
+++ b/src/commands/settings/logs/_meta.ts
@@ -1,4 +1,4 @@
-const name = "log";
+const name = "logs";
 const description = "Settings for logging";
 
 export { name, description };
\ No newline at end of file
diff --git a/src/commands/settings/logs/events.ts b/src/commands/settings/logs/events.ts
index dac200c..ef303cb 100644
--- a/src/commands/settings/logs/events.ts
+++ b/src/commands/settings/logs/events.ts
@@ -1,17 +1,106 @@
-import { CommandInteraction } from "discord.js";
+import { LoadingEmbed } from './../../../utils/defaultEmbeds.js';
+import Discord, { CommandInteraction, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js";
 import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
+import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
+import client from '../../../utils/client.js';
+import { toHexArray, toHexInteger } from "../../../utils/calculate.js";
+
+
+const logs = {
+    "channelUpdate": "Channels created, deleted or modified",
+    "emojiUpdate": "Server emojis modified",
+    "stickerUpdate": "Server stickers modified",
+    "guildUpdate": "Server settings updated",
+    "guildMemberUpdate": "Member updated (i.e. nickname)",
+    "guildMemberPunish": "Members punished (i.e. muted, banned, kicked)",
+    "guildRoleUpdate": "Role settings changed",
+    "guildInviteUpdate": "Server invite created or deleted",
+    "messageUpdate": "Message edited",
+    "messageDelete": "Message deleted",
+    "messageDeleteBulk": "Messages purged",
+    "messageReactionUpdate": "Message reactions cleared",
+    "messageMassPing": "Message pings multiple members at once",
+    "messageAnnounce": "Message published in announcement channel",
+    "threadUpdate": "Thread created or deleted",
+    "webhookUpdate": "Webhooks created or deleted",
+    "guildMemberVerify": "Member runs verify",
+    "autoModeratorDeleted": "Messages auto deleted by Nucleus",
+    "nucleusSettingsUpdated": "Nucleus' settings updated by a moderator",
+    "ticketUpdate": "Tickets created or deleted",
+}
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
     .setName("events")
     .setDescription("Sets what events should be logged")
 
-const callback = (interaction: CommandInteraction) => {
-    interaction.reply("This command is not yet finished [settings/log/events]");
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    await interaction.reply({embeds: LoadingEmbed, fetchReply: true, ephemeral: true});
+    let m;
+    while (true) {
+        let config = await client.database.guilds.read(interaction.guild.id)
+        let converted = toHexArray(config.logging.logs.toLog)
+        m = await interaction.editReply({embeds: [new EmojiEmbed()
+            .setTitle("Logging Events")
+            .setDescription("Below are the events being logged in the server. You can toggle them on and off in the dropdown")
+            .setStatus("Success")
+            .setEmoji("CHANNEL.TEXT.CREATE")
+        ], components: [
+            new MessageActionRow().addComponents([new MessageSelectMenu()
+                .setPlaceholder("Set events to log")
+                .setMaxValues(Object.keys(logs).length)
+                .setCustomId("logs")
+                .setMinValues(0)
+                .setOptions(Object.keys(logs).map((e, i) => ({
+                    label: logs[e],
+                    value: i.toString(),
+                    default: converted.includes(e)
+                })))
+            ]),
+            new MessageActionRow().addComponents([
+                new MessageButton()
+                    .setLabel("Select all")
+                    .setStyle("PRIMARY")
+                    .setCustomId("all"),
+                new MessageButton()
+                    .setLabel("Select none")
+                    .setStyle("DANGER")
+                    .setCustomId("none")
+            ])
+        ]})
+        let i;
+        try {
+            i = await m.awaitMessageComponent({ time: 300000 });
+        } catch (e) {
+            break
+        }
+        i.deferUpdate()
+        if (i.customId === "logs") {
+            let selected = i.values;
+            let newLogs = toHexInteger(selected.map(e => Object.keys(logs)[parseInt(e)]))
+            await client.database.guilds.write(interaction.guild.id, {"logging.logs.toLog": newLogs})
+        } else if (i.customId === "all") {
+            let newLogs = toHexInteger(Object.keys(logs).map(e => e))
+            await client.database.guilds.write(interaction.guild.id, {"logging.logs.toLog": newLogs})
+        } else if (i.customId === "none") {
+            await client.database.guilds.write(interaction.guild.id, {"logging.logs.toLog": 0})
+        } else {
+            break
+        }
+    }
+    m = await interaction.editReply({embeds: [new EmojiEmbed()
+        .setTitle("Logging Events")
+        .setDescription("Below are the events being logged in the server. You can toggle them on and off in the dropdown")
+        .setFooter({text: "Message timed out"})
+        .setStatus("Success")
+        .setEmoji("CHANNEL.TEXT.CREATE")
+    ]})
 }
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the *Manage Server* permission to use this command"
     return true;
 }
 
diff --git a/src/commands/settings/stats.ts b/src/commands/settings/stats.ts
index d972674..be15869 100644
--- a/src/commands/settings/stats.ts
+++ b/src/commands/settings/stats.ts
@@ -144,12 +144,7 @@
             await client.database.guilds.write(interaction.guild.id, null, toRemove.map(k => `stats.${k}`));
         }
     }
-    await interaction.editReply({embeds: [new EmojiEmbed()
-        .setTitle("Stats Channel")
-        .setDescription("The following channels update when someone joins or leaves the server. You can select a channel to remove it from the list.")
-        .setStatus("Danger")
-        .setEmoji("CHANNEL.TEXT.DELETE")
-    ], components: []})
+    await interaction.editReply({embeds: [m.embeds[0].setFooter({text: "Message closed"})], components: []});
 }
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts
index f199ac3..44f974e 100644
--- a/src/commands/settings/tickets.ts
+++ b/src/commands/settings/tickets.ts
@@ -192,6 +192,11 @@
                     .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
                     .setStyle("SECONDARY")
                     .setCustomId("manageTypes"),
+                new MessageButton()
+                    .setLabel("Add create ticket button")
+                    .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
+                    .setStyle("PRIMARY")
+                    .setCustomId("send"),
             ])]
         });
         let i;
@@ -217,6 +222,122 @@
                 await client.database.guilds.write(interaction.guild.id, null, ["tickets.supportRole"])
                 data.supportRole = undefined;
             } else lastClicked = "sup";
+        } else if (i.component.customId === "send") {
+            const ticketMessages = [
+                {label: "Create ticket", description: "Click the button below to create a ticket"},
+                {label: "Issues, questions or feedback?", description: "Click below to open a ticket and get help from our staff team"},
+                {label: "Contact Us", description: "Click the button below to speak to us privately"},
+            ]
+            while (true) {
+                let enabled = data.enabled && data.category !== null;
+                await interaction.editReply({embeds: [new EmojiEmbed()
+                    .setTitle("Ticket Button")
+                    .setDescription("Select a message template to send in this channel")
+                    .setFooter({text: enabled ? "" : "Tickets are not set up correctly so the button may not work for users. Check the main menu to find which options must be set."})
+                    .setStatus(enabled ? "Success" : "Warning")
+                    .setEmoji("GUILD.ROLES.CREATE")
+                ], components: [
+                    new MessageActionRow().addComponents([
+                        new MessageSelectMenu().setOptions(ticketMessages.map((t: {label: string, description: string, value?: string}, index) => {
+                            t.value = index.toString(); return t as {value: string, label: string, description: string}
+                        })).setCustomId("template").setMaxValues(1).setMinValues(1).setPlaceholder("Select a message template"),
+                    ]),
+                    new MessageActionRow().addComponents([
+                        new MessageButton()
+                            .setCustomId("back")
+                            .setLabel("Back")
+                            .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
+                            .setStyle("DANGER"),
+                        new MessageButton()
+                            .setCustomId("blank")
+                            .setLabel("Empty")
+                            .setStyle("SECONDARY"),
+                        new MessageButton()
+                            .setCustomId("custom")
+                            .setLabel("Custom")
+                            .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
+                            .setStyle("PRIMARY")
+                    ])
+                ]});
+                let i;
+                try {
+                    i = await m.awaitMessageComponent({time: 300000});
+                } catch(e) { break }
+                if (i.component.customId === "template") {
+                    i.deferUpdate()
+                    await interaction.channel.send({embeds: [new EmojiEmbed()
+                        .setTitle(ticketMessages[parseInt(i.values[0])].label)
+                        .setDescription(ticketMessages[parseInt(i.values[0])].description)
+                        .setStatus("Success")
+                        .setEmoji("GUILD.TICKET.OPEN")
+                    ], components: [new MessageActionRow().addComponents([new MessageButton()
+                        .setLabel("Create Ticket")
+                        .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
+                        .setStyle("SUCCESS")
+                        .setCustomId("createticket")
+                    ])]});
+                    break
+                } else if (i.component.customId === "blank") {
+                    i.deferUpdate()
+                    await interaction.channel.send({components: [new MessageActionRow().addComponents([new MessageButton()
+                        .setLabel("Create Ticket")
+                        .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
+                        .setStyle("SUCCESS")
+                        .setCustomId("createticket")
+                    ])]});
+                    break
+                } else if (i.component.customId === "custom") {
+                    await i.showModal(new Discord.Modal().setCustomId("modal").setTitle(`Enter embed details`).addComponents(
+                        new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
+                            .setCustomId("title")
+                            .setLabel("Title")
+                            .setMaxLength(256)
+                            .setRequired(true)
+                            .setStyle("SHORT")
+                        ),
+                        new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
+                            .setCustomId("description")
+                            .setLabel("Description")
+                            .setMaxLength(4000)
+                            .setRequired(true)
+                            .setStyle("PARAGRAPH")
+                        )
+                    ))
+                    await interaction.editReply({
+                        embeds: [new EmojiEmbed()
+                            .setTitle("Ticket Button")
+                            .setDescription("Modal opened. If you can't see it, click back and try again.")
+                            .setStatus("Success")
+                            .setEmoji("GUILD.TICKET.OPEN")
+                        ], 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 === interaction.channel.id, (m) => m.customId === "modify")
+                    } catch (e) { break }
+                    if (out.fields) {
+                        let title = out.fields.getTextInputValue("title");
+                        let description = out.fields.getTextInputValue("description");
+                        await interaction.channel.send({embeds: [new EmojiEmbed()
+                            .setTitle(title)
+                            .setDescription(description)
+                            .setStatus("Success")
+                            .setEmoji("GUILD.TICKET.OPEN")
+                        ], components: [new MessageActionRow().addComponents([new MessageButton()
+                            .setLabel("Create Ticket")
+                            .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
+                            .setStyle("SUCCESS")
+                            .setCustomId("createticket")
+                        ])]});
+                        break
+                    } else { continue }
+                }
+            }
         } else if (i.component.customId === "enabled") {
             await client.database.guilds.write(interaction.guild.id, { "tickets.enabled": !data.enabled })
             data.enabled = !data.enabled;
diff --git a/src/commands/settings/verify.ts b/src/commands/settings/verify.ts
index 14dfe8d..a77f2f4 100644
--- a/src/commands/settings/verify.ts
+++ b/src/commands/settings/verify.ts
@@ -1,11 +1,12 @@
 import { LoadingEmbed } from './../../utils/defaultEmbeds.js';
-import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
+import Discord, { CommandInteraction, Emoji, MessageActionRow, MessageButton, MessageSelectMenu, TextInputComponent } from "discord.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import getEmojiByName from "../../utils/getEmojiByName.js";
 import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
 import { WrappedCheck } from "jshaiku";
 import client from "../../utils/client.js";
+import { modalInteractionCollector } from '../../utils/dualCollector.js';
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
@@ -53,7 +54,7 @@
                     let data = {
                         meta:{
                             type: 'verifyRoleChanged',
-                            displayName: 'Ignored Groups Changed',
+                            displayName: 'Verify Role Changed',
                             calculateType: 'nucleusSettingsUpdated',
                             color: NucleusColors.green,
                             emoji: "CONTROL.BLOCKTICK",
@@ -103,7 +104,12 @@
                 .setLabel(clicks ? "Click again to confirm" : "Reset role")
                 .setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
                 .setStyle("DANGER")
-                .setDisabled(!role)
+                .setDisabled(!role),
+            new MessageButton()
+                .setCustomId("send")
+                .setLabel("Add verify button")
+                .setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
+                .setStyle("PRIMARY")
         ])]});
         let i;
         try {
@@ -117,23 +123,127 @@
                 await client.database.guilds.write(interaction.guild.id, null, ["verify.role", "verify.enabled"])
                 role = undefined;
             }
+        } else if (i.component.customId === "send") {
+            const verifyMessages = [
+                {label: "Verify", description: "Click the button below to get verified"},
+                {label: "Get verified", description: "To get access to the rest of the server, click the button below"},
+                {label: "Ready to verify?", description: "Click the button below to verify yourself"},
+            ]
+            while (true) {
+                await interaction.editReply({embeds: [new EmojiEmbed()
+                    .setTitle("Verify Button")
+                    .setDescription("Select a message template to send in this channel")
+                    .setFooter({text: role ? "" : "You do no have a verify role set so the button will not work."})
+                    .setStatus(role ? "Success" : "Warning")
+                    .setEmoji("GUILD.ROLES.CREATE")
+                ], components: [
+                    new MessageActionRow().addComponents([
+                        new MessageSelectMenu().setOptions(verifyMessages.map((t: {label: string, description: string, value?: string}, index) => {
+                            t.value = index.toString(); return t as {value: string, label: string, description: string}
+                        })).setCustomId("template").setMaxValues(1).setMinValues(1).setPlaceholder("Select a message template"),
+                    ]),
+                    new MessageActionRow().addComponents([
+                        new MessageButton()
+                            .setCustomId("back")
+                            .setLabel("Back")
+                            .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
+                            .setStyle("DANGER"),
+                        new MessageButton()
+                            .setCustomId("blank")
+                            .setLabel("Empty")
+                            .setStyle("SECONDARY"),
+                        new MessageButton()
+                            .setCustomId("custom")
+                            .setLabel("Custom")
+                            .setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
+                            .setStyle("PRIMARY")
+                    ])
+                ]});
+                let i;
+                try {
+                    i = await m.awaitMessageComponent({time: 300000});
+                } catch(e) { break }
+                if (i.component.customId === "template") {
+                    i.deferUpdate()
+                    await interaction.channel.send({embeds: [new EmojiEmbed()
+                        .setTitle(verifyMessages[parseInt(i.values[0])].label)
+                        .setDescription(verifyMessages[parseInt(i.values[0])].description)
+                        .setStatus("Success")
+                        .setEmoji("CONTROL.BLOCKTICK")
+                    ], components: [new MessageActionRow().addComponents([new MessageButton()
+                        .setLabel("Verify")
+                        .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
+                        .setStyle("SUCCESS")
+                        .setCustomId("verifybutton")
+                    ])]});
+                    break
+                } else if (i.component.customId === "blank") {
+                    i.deferUpdate()
+                    await interaction.channel.send({components: [new MessageActionRow().addComponents([new MessageButton()
+                        .setLabel("Verify")
+                        .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
+                        .setStyle("SUCCESS")
+                        .setCustomId("verifybutton")
+                    ])]});
+                    break
+                } else if (i.component.customId === "custom") {
+                    await i.showModal(new Discord.Modal().setCustomId("modal").setTitle(`Enter embed details`).addComponents(
+                        new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
+                            .setCustomId("title")
+                            .setLabel("Title")
+                            .setMaxLength(256)
+                            .setRequired(true)
+                            .setStyle("SHORT")
+                        ),
+                        new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
+                            .setCustomId("description")
+                            .setLabel("Description")
+                            .setMaxLength(4000)
+                            .setRequired(true)
+                            .setStyle("PARAGRAPH")
+                        )
+                    ))
+                    await interaction.editReply({
+                        embeds: [new EmojiEmbed()
+                            .setTitle("Verify Button")
+                            .setDescription("Modal opened. If you can't see it, click back and try again.")
+                            .setStatus("Success")
+                            .setEmoji("GUILD.TICKET.OPEN")
+                        ], 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 === interaction.channel.id, (m) => m.customId === "modify")
+                    } catch (e) { break }
+                    if (out.fields) {
+                        let title = out.fields.getTextInputValue("title");
+                        let description = out.fields.getTextInputValue("description");
+                        await interaction.channel.send({embeds: [new EmojiEmbed()
+                            .setTitle(title)
+                            .setDescription(description)
+                            .setStatus("Success")
+                            .setEmoji("CONTROL.BLOCKTICK")
+                        ], components: [new MessageActionRow().addComponents([new MessageButton()
+                            .setLabel("Verify")
+                            .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
+                            .setStyle("SUCCESS")
+                            .setCustomId("verifybutton")
+                        ])]});
+                        break
+                    } else { continue }
+                }
+            }
         } else {
-            break
+            i.deferUpdate()
+            break;
         }
     }
-    await interaction.editReply({embeds: [new EmojiEmbed()
-        .setTitle("Verify Role")
-        .setDescription(role ? `Your verify role is currently set to <@&${role}}>` : `You have not set a verify role`)
-        .setStatus("Success")
-        .setEmoji("GUILD.ROLE.CREATE")
-        .setFooter({text: "Message closed"})
-    ], components: [new MessageActionRow().addComponents([new MessageButton()
-        .setCustomId("clear")
-        .setLabel("Clear")
-        .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-        .setStyle("SECONDARY")
-        .setDisabled(true),
-    ])]});
+    await interaction.editReply({embeds: [m.embeds[0].setFooter({text: "Message closed"})], components: []});
 }
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
diff --git a/src/commands/settings/welcome.ts b/src/commands/settings/welcome.ts
new file mode 100644
index 0000000..1a107ed
--- /dev/null
+++ b/src/commands/settings/welcome.ts
@@ -0,0 +1,209 @@
+import { LoadingEmbed } from './../../utils/defaultEmbeds.js';
+import Discord, { CommandInteraction, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import client from '../../utils/client.js';
+import confirmationMessage from '../../utils/confirmationMessage.js';
+import generateKeyValueList from '../../utils/generateKeyValueList.js';
+import { ChannelType } from 'discord-api-types';
+import getEmojiByName from '../../utils/getEmojiByName.js';
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+    .setName("welcome")
+    .setDescription("Messages and roles sent or given when someone joins the server")
+    .addStringOption(option => option.setName("message").setDescription("The message to send when someone joins the server").setAutocomplete(true))
+    .addRoleOption(option => option.setName("role").setDescription("The role given when someone joins the server"))
+    .addRoleOption(option => option.setName("ping").setDescription("The role pinged when someone joins the server"))
+    .addChannelOption(option => option.setName("channel").setDescription("The channel the welcome message should be sent to").addChannelTypes([
+        ChannelType.GuildText, ChannelType.GuildNews
+    ]))
+
+const callback = async (interaction: CommandInteraction): Promise<any> => {
+    const { renderRole, renderChannel, log, NucleusColors, entry, renderUser } = client.logger
+    await interaction.reply({embeds: LoadingEmbed, fetchReply: true, ephemeral: true});
+    let m;
+    if (interaction.options.getRole("role") || interaction.options.getChannel("channel") || interaction.options.getString("message")) {
+        let role;
+        let ping;
+        let message = interaction.options.getString("message");
+        try {
+            role = interaction.options.getRole("role")
+            ping = interaction.options.getRole("ping")
+        } catch {
+            return await interaction.editReply({embeds: [new EmojiEmbed()
+                .setEmoji("GUILD.ROLES.DELETE")
+                .setTitle("Welcome Events")
+                .setDescription("The role you provided is not a valid role")
+                .setStatus("Danger")
+            ]})
+        }
+        let channel;
+        try {
+            channel = interaction.options.getChannel("channel")
+        } catch {
+            return await interaction.editReply({embeds: [new EmojiEmbed()
+                .setEmoji("GUILD.ROLES.DELETE")
+                .setTitle("Welcome Events")
+                .setDescription("The channel you provided is not a valid channel")
+                .setStatus("Danger")
+            ]})
+        }
+        role = role as Discord.Role
+        ping = ping as Discord.Role
+        channel = channel as Discord.TextChannel
+        let options = {}
+        if (role) options["role"] = renderRole(role)
+        if (ping) options["ping"] = renderRole(ping)
+        if (channel) options["channel"] = renderChannel(channel)
+        if (message) options["message"] = "\n> " + message
+        let confirmation = await new confirmationMessage(interaction)
+            .setEmoji("GUILD.ROLES.EDIT")
+            .setTitle("Welcome Events")
+            .setDescription(generateKeyValueList(options))
+            .setColor("Warning")
+            .setInverted(true)
+        .send(true)
+        if (confirmation.cancelled) return
+        if (confirmation.success) {
+            try {
+                let toChange = {}
+                if (role) toChange["welcome.role"] = role.id
+                if (ping) toChange["welcome.ping"] = ping.id
+                if (channel) toChange["welcome.channel"] = channel.id
+                if (message) toChange["welcome.message"] = message
+                await client.database.guilds.write(interaction.guild.id, toChange);
+                let list = {
+                    memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
+                    changedBy: entry(interaction.user.id, renderUser(interaction.user))
+                }
+                if (role) list["role"] = entry(role.id, renderRole(role))
+                if (ping) list["ping"] = entry(ping.id, renderRole(ping))
+                if (channel) list["channel"] = entry(channel.id, renderChannel(channel.id))
+                if (message) list["message"] = entry(message, `\`${message}\``)
+                try {
+                    let data = {
+                        meta:{
+                            type: 'welcomeSettingsUpdated',
+                            displayName: 'Welcome Settings Changed',
+                            calculateType: 'nucleusSettingsUpdated',
+                            color: NucleusColors.green,
+                            emoji: "CONTROL.BLOCKTICK",
+                            timestamp: new Date().getTime()
+                        },
+                        list: list,
+                        hidden: {
+                            guild: interaction.guild.id
+                        }
+                    }
+                    log(data);
+                } catch {}
+            } catch (e) {
+                console.log(e)
+                return interaction.editReply({embeds: [new EmojiEmbed()
+                    .setTitle("Welcome Events")
+                    .setDescription(`Something went wrong while updating welcome settings`)
+                    .setStatus("Danger")
+                    .setEmoji("GUILD.ROLES.DELETE")
+                ], components: []});
+            }
+        } else {
+            return interaction.editReply({embeds: [new EmojiEmbed()
+                .setTitle("Welcome Events")
+                .setDescription(`No changes were made`)
+                .setStatus("Success")
+                .setEmoji("GUILD.ROLES.CREATE")
+            ], components: []});
+        }
+    }
+    let lastClicked = null
+    while (true) {
+        let config = await client.database.guilds.read(interaction.guild.id)
+        m = await interaction.editReply({embeds: [new EmojiEmbed()
+            .setTitle("Welcome Events")
+            .setDescription(
+                `**Message:** ${config.welcome.message ? `\n> ${config.welcome.message}` : "*None set*"}\n` +
+                `**Role:** ${config.welcome.role ? renderRole(await interaction.guild.roles.fetch(config.welcome.role)) : "*None set*"}\n` +
+                `**Ping:** ${config.welcome.ping ? renderRole(await interaction.guild.roles.fetch(config.welcome.ping)) : "*None set*"}\n` +
+                `**Channel:** ${config.welcome.channel ? (config.welcome.channel == "dm" ? "DM" : renderChannel(await interaction.guild.channels.fetch(config.welcome.channel))) : "*None set*"}`
+            )
+            .setStatus("Success")
+            .setEmoji("CHANNEL.TEXT.CREATE")
+        ], components: [
+            new MessageActionRow().addComponents([
+                new MessageButton()
+                    .setLabel(lastClicked == "clear-message" ? "Click again to confirm" : "Clear Message")
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+                    .setCustomId("clear-message")
+                    .setDisabled(!config.welcome.message)
+                    .setStyle("DANGER"),
+                new MessageButton()
+                    .setLabel(lastClicked == "clear-role" ? "Click again to confirm" : "Clear Role")
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+                    .setCustomId("clear-role")
+                    .setDisabled(!config.welcome.role)
+                    .setStyle("DANGER"),
+                new MessageButton()
+                    .setLabel(lastClicked == "clear-ping" ? "Click again to confirm" : "Clear Ping")
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+                    .setCustomId("clear-ping")
+                    .setDisabled(!config.welcome.ping)
+                    .setStyle("DANGER"),
+                new MessageButton()
+                    .setLabel(lastClicked == "clear-channel" ? "Click again to confirm" : "Clear Channel")
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+                    .setCustomId("clear-channel")
+                    .setDisabled(!config.welcome.channel)
+                    .setStyle("DANGER"),
+                new MessageButton()
+                    .setLabel("Set Channel to DM")
+                    .setCustomId("set-channel-dm")
+                    .setDisabled(config.welcome.channel == "dm")
+                    .setStyle("SECONDARY")
+            ])
+        ]})
+        let i;
+        try {
+            i = await m.awaitMessageComponent({ time: 300000 });
+        } catch (e) {
+            break
+        }
+        i.deferUpdate()
+        if (i.customId == "clear-message") {
+            if (lastClicked == "clear-message") {
+                await client.database.guilds.write(interaction.guild.id, {"welcome.message": null});
+                lastClicked = null
+            } else { lastClicked = "clear-message" }
+        } else if (i.customId == "clear-role") {
+            if (lastClicked == "clear-role") {
+                await client.database.guilds.write(interaction.guild.id, {"welcome.role": null});
+                lastClicked = null
+            } else { lastClicked = "clear-role" }
+        } else if (i.customId == "clear-ping") {
+            if (lastClicked == "clear-ping") {
+                await client.database.guilds.write(interaction.guild.id, {"welcome.ping": null});
+                lastClicked = null
+            } else { lastClicked = "clear-ping" }
+        } else if (i.customId == "clear-channel") {
+            if (lastClicked == "clear-channel") {
+                await client.database.guilds.write(interaction.guild.id, {"welcome.channel": null});
+                lastClicked = null
+            } else { lastClicked = "clear-channel" }
+        } else if (i.customId == "set-channel-dm") {
+            await client.database.guilds.write(interaction.guild.id, {"welcome.channel": "dm"});
+            lastClicked = null
+        }
+    }
+    await interaction.editReply({embeds: [m.embeds[0].setFooter({text: "Message closed"})], components: []});
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+    let member = (interaction.member as Discord.GuildMember)
+    if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the *Manage Server* permission to use this command"
+    return true;
+}
+
+export { command };
+export { callback };
+export { check };
\ No newline at end of file
diff --git a/src/commands/tag.ts b/src/commands/tag.ts
index 03b13c6..70d5a75 100644
--- a/src/commands/tag.ts
+++ b/src/commands/tag.ts
@@ -9,7 +9,7 @@
     .setDescription("Get and manage the servers tags")
     .addStringOption(o => o.setName("tag").setDescription("The tag to get").setAutocomplete(true).setRequired(true))
 
-const callback = async (interaction: CommandInteraction) => {
+const callback = async (interaction: CommandInteraction): Promise<any> => {
     const config = await client.database.guilds.read(interaction.guild.id)
     const tags = config.getKey("tags")
     const tag = tags[interaction.options.getString("tag")]
diff --git a/src/commands/tags/create.ts b/src/commands/tags/create.ts
index b0a278d..4615def 100644
--- a/src/commands/tags/create.ts
+++ b/src/commands/tags/create.ts
@@ -79,7 +79,7 @@
 
 const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
     let member = (interaction.member as Discord.GuildMember)
-    if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the *Manage Server* permission to use this command"
+    if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the *Manage Messages* permission to use this command"
     return true;
 }