Development (#11)

We need this NOW.

---------

Co-authored-by: PineaFan <ash@pinea.dev>
Co-authored-by: pineafan <pineapplefanyt@gmail.com>
Co-authored-by: PineappleFan <PineaFan@users.noreply.github.com>
Co-authored-by: Skyler <skyler3665@gmail.com>
diff --git a/src/commands/settings/logs/attachment.ts b/src/commands/settings/logs/attachment.ts
index 2709bee..238b8b9 100644
--- a/src/commands/settings/logs/attachment.ts
+++ b/src/commands/settings/logs/attachment.ts
@@ -1,198 +1,110 @@
 import { LoadingEmbed } from "../../../utils/defaults.js";
-import { ChannelType } from "discord-api-types/v9";
-import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonInteraction } from "discord.js";
+import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelSelectMenuBuilder, ChannelType } from "discord.js";
 import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
-import confirmationMessage from "../../../utils/confirmationMessage.js";
 import getEmojiByName from "../../../utils/getEmojiByName.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
 import client from "../../../utils/client.js";
+import { getCommandMentionByName } from "../../../utils/getCommandDataByName.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder
         .setName("attachments")
         .setDescription("Where attachments should be logged to (Premium only)")
-        .addChannelOption((option) =>
-            option
-                .setName("channel")
-                .setDescription("The channel to log attachments in")
-                .addChannelTypes(ChannelType.GuildText)
-                .setRequired(false)
-        );
 
 const callback = async (interaction: CommandInteraction): Promise<unknown> => {
-    const m = (await interaction.reply({
+    if (interaction.guild) client.database.premium.hasPremium(interaction.guild.id).finally(() => {});
+    await interaction.reply({
         embeds: LoadingEmbed,
         ephemeral: true,
         fetchReply: true
-    })) as Discord.Message;
-    if (interaction.options.get("channel")?.channel) {
-        let channel;
-        try {
-            channel = interaction.options.get("channel")?.channel;
-        } catch {
-            return await interaction.editReply({
-                embeds: [
-                    new EmojiEmbed()
-                        .setEmoji("CHANNEL.TEXT.DELETE")
-                        .setTitle("Attachment Log Channel")
-                        .setDescription("The channel you provided is not a valid channel")
-                        .setStatus("Danger")
-                ]
-            });
-        }
-        channel = channel as Discord.TextChannel;
-        if (channel.guild.id !== interaction.guild!.id) {
-            return interaction.editReply({
-                embeds: [
-                    new EmojiEmbed()
-                        .setTitle("Attachment Log Channel")
-                        .setDescription("You must choose a channel in this server")
-                        .setStatus("Danger")
-                        .setEmoji("CHANNEL.TEXT.DELETE")
-                ]
-            });
-        }
-        const confirmation = await new confirmationMessage(interaction)
-            .setEmoji("CHANNEL.TEXT.EDIT")
-            .setTitle("Attachment Log Channel")
-            .setDescription(
-                "This will be the channel all attachments will be sent to.\n\n" +
-                    `Are you sure you want to set the attachment log channel to <#${channel.id}>?`
-            )
-            .setColor("Warning")
-            .setFailedMessage("Attachment log channel not set", "Warning", "CHANNEL.TEXT.DELETE")
-            .setInverted(true)
-            .send(true);
-        if (confirmation.cancelled) return;
-        if (confirmation.success) {
-            try {
-                await client.database.guilds.write(interaction.guild!.id, {
-                    "logging.attachments.channel": channel.id
-                });
-                const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger;
-                const data = {
-                    meta: {
-                        type: "attachmentChannelUpdate",
-                        displayName: "Attachment Log Channel Updated",
-                        calculateType: "nucleusSettingsUpdated",
-                        color: NucleusColors.yellow,
-                        emoji: "CHANNEL.TEXT.EDIT",
-                        timestamp: new Date().getTime()
-                    },
-                    list: {
-                        memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
-                        changedBy: entry(interaction.user.id, renderUser(interaction.user)),
-                        channel: entry(channel.id, renderChannel(channel))
-                    },
-                    hidden: {
-                        guild: interaction.guild!.id
-                    }
-                };
-                log(data);
-            } catch (e) {
-                return interaction.editReply({
-                    embeds: [
-                        new EmojiEmbed()
-                            .setTitle("Attachment Log Channel")
-                            .setDescription("Something went wrong and the attachment log channel could not be set")
-                            .setStatus("Danger")
-                            .setEmoji("CHANNEL.TEXT.DELETE")
-                    ],
-                    components: []
-                });
-            }
-        } else {
-            return interaction.editReply({
-                embeds: [
-                    new EmojiEmbed()
-                        .setTitle("Attachment Log Channel")
-                        .setDescription("No changes were made")
-                        .setStatus("Success")
-                        .setEmoji("CHANNEL.TEXT.CREATE")
-                ],
-                components: []
-            });
-        }
-    }
-    let clicks = 0;
-    const data = await client.database.guilds.read(interaction.guild!.id);
-    let channel = data.logging.staff.channel;
+    })
 
-    let timedOut = false;
-    while (!timedOut) {
-        await interaction.editReply({
-            embeds: [
-                new EmojiEmbed()
-                    .setTitle("Attachment Log Channel")
-                    .setDescription(
-                        channel
-                            ? `Your attachment log channel is currently set to <#${channel}>`
-                            : "This server does not have an attachment log channel" +
-                                  (await client.database.premium.hasPremium(interaction.guild!.id)
-                                      ? ""
-                                      : "\n\nThis server does not have premium, so this feature is disabled")
-                    )
-                    .setStatus("Success")
-                    .setEmoji("CHANNEL.TEXT.CREATE")
-            ],
-            components: [
-                new ActionRowBuilder<ButtonBuilder>().addComponents([
-                    new ButtonBuilder()
-                        .setCustomId("clear")
-                        .setLabel(clicks ? "Click again to confirm" : "Reset channel")
-                        .setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
-                        .setStyle(ButtonStyle.Danger)
-                        .setDisabled(!channel)
-                ])
-            ]
-        });
-        let i;
-        try {
-            i = await m.awaitMessageComponent({
-                time: 300000,
-                filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
-            });
-        } catch (e) {
-            timedOut = true;
-            continue;
-        }
-        i.deferUpdate();
-        if ((i.component as unknown as ButtonInteraction).customId === "clear") {
-            clicks += 1;
-            if (clicks === 2) {
-                clicks = 0;
-                await client.database.guilds.write(interaction.guild!.id, null, ["logging.announcements.channel"]);
-                channel = null;
-            }
-        }
-    }
-    await interaction.editReply({
+    if(!await client.database.premium.hasPremium(interaction.guild!.id)) return interaction.editReply({
         embeds: [
             new EmojiEmbed()
-                .setTitle("Attachment Log Channel")
-                .setDescription(
-                    channel
-                        ? `Your attachment log channel is currently set to <#${channel}>`
-                        : "This server does not have an attachment log channel"
-                )
-                .setStatus("Success")
-                .setEmoji("CHANNEL.TEXT.CREATE")
-                .setFooter({ text: "Message closed" })
-        ],
-        components: [
-            new ActionRowBuilder<ButtonBuilder>().addComponents([
+                .setTitle("Premium Required")
+                .setDescription(`This feature is exclusive to ${getCommandMentionByName("nucleus/premium")} servers.`)
+                .setStatus("Danger")
+                .setEmoji("NUCLEUS.PREMIUM")
+        ]
+    });
+
+    let data = await client.database.guilds.read(interaction.guild!.id);
+    let channel = data.logging.staff.channel;
+
+    let closed = false;
+    do {
+        const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>()
+            .addComponents(
+                new ChannelSelectMenuBuilder()
+                    .setCustomId("channel")
+                    .setPlaceholder("Select a channel")
+                    .setChannelTypes(ChannelType.GuildText)
+            );
+        const buttons = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents(
                 new ButtonBuilder()
                     .setCustomId("clear")
                     .setLabel("Clear")
-                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-                    .setStyle(ButtonStyle.Secondary)
-                    .setDisabled(true)
-            ])
-        ]
-    });
+                    .setStyle(ButtonStyle.Danger)
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id") as Discord.APIMessageComponentEmoji)
+                    .setDisabled(!channel),
+                new ButtonBuilder()
+                    .setCustomId("save")
+                    .setLabel("Save")
+                    .setStyle(ButtonStyle.Success)
+                    .setEmoji(getEmojiByName("ICONS.SAVE", "id") as Discord.APIMessageComponentEmoji)
+                    .setDisabled(channel === data.logging.staff.channel)
+            );
+
+        const embed = new EmojiEmbed()
+            .setTitle("Attachments")
+            .setDescription(
+                `The channel to send all attachments from the server, allowing you to check them if they are deleted\n` +
+                `**Channel:** ${channel ? `<#${channel}>` : "*None*"}\n`
+            )
+            .setStatus("Success")
+            .setEmoji("CHANNEL.TEXT.CREATE")
+
+        await interaction.editReply({
+            embeds: [embed],
+            components: [channelMenu, buttons]
+        });
+
+        let i: Discord.ButtonInteraction | Discord.SelectMenuInteraction;
+        try {
+            i = (await interaction.channel!.awaitMessageComponent({
+                filter: (i: Discord.Interaction) => i.user.id === interaction.user.id,
+                time: 300000
+            })) as Discord.ButtonInteraction | Discord.SelectMenuInteraction;
+        } catch (e) {
+            closed = true;
+            continue;
+        }
+        await i.deferUpdate();
+        if(i.isButton()) {
+            switch (i.customId) {
+                case "clear": {
+                    channel = null;
+                    break;
+                }
+                case "save": {
+                    await client.database.guilds.write(interaction.guild!.id, {
+                        "logging.attachments.channel": channel
+                    });
+                    data = await client.database.guilds.read(interaction.guild!.id);
+                    break;
+                }
+            }
+        } else {
+            channel = i.values[0]!;
+        }
+
+    } while (!closed);
+    await interaction.deleteReply()
 };
 
-const check = (interaction: CommandInteraction) => {
+const check = (interaction: CommandInteraction, _partial: boolean = false) => {
     const member = interaction.member as Discord.GuildMember;
     if (!member.permissions.has("ManageGuild"))
         return "You must have the *Manage Server* permission to use this command";
diff --git a/src/commands/settings/logs/channel.ts b/src/commands/settings/logs/channel.ts
deleted file mode 100644
index 992491a..0000000
--- a/src/commands/settings/logs/channel.ts
+++ /dev/null
@@ -1,197 +0,0 @@
-import { LoadingEmbed } from "../../../utils/defaults.js";
-import { ChannelType } from "discord-api-types/v9";
-import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonInteraction, ButtonComponent } from "discord.js";
-import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
-import confirmationMessage from "../../../utils/confirmationMessage.js";
-import getEmojiByName from "../../../utils/getEmojiByName.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import client from "../../../utils/client.js";
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-        .setName("channel")
-        .setDescription("Sets or shows the log channel")
-        .addChannelOption((option) =>
-            option
-                .setName("channel")
-                .setDescription("The channel to set the log channel to")
-                .addChannelTypes(ChannelType.GuildText)
-        );
-
-const callback = async (interaction: CommandInteraction): Promise<unknown> => {
-    const m = (await interaction.reply({
-        embeds: LoadingEmbed,
-        ephemeral: true,
-        fetchReply: true
-    })) as Discord.Message;
-    if (interaction.options.get("channel")?.channel) {
-        let channel;
-        try {
-            channel = interaction.options.get("channel")?.channel;
-        } catch {
-            return await interaction.editReply({
-                embeds: [
-                    new EmojiEmbed()
-                        .setEmoji("CHANNEL.TEXT.DELETE")
-                        .setTitle("Log Channel")
-                        .setDescription("The channel you provided is not a valid channel")
-                        .setStatus("Danger")
-                ]
-            });
-        }
-        channel = channel as Discord.TextChannel;
-        if (channel.guild.id !== interaction.guild!.id) {
-            return interaction.editReply({
-                embeds: [
-                    new EmojiEmbed()
-                        .setTitle("Log Channel")
-                        .setDescription("You must choose a channel in this server")
-                        .setStatus("Danger")
-                        .setEmoji("CHANNEL.TEXT.DELETE")
-                ]
-            });
-        }
-        const confirmation = await new confirmationMessage(interaction)
-            .setEmoji("CHANNEL.TEXT.EDIT")
-            .setTitle("Log Channel")
-            .setDescription(`Are you sure you want to set the log channel to <#${channel.id}>?`)
-            .setColor("Warning")
-            .setFailedMessage("The log channel was not changed", "Danger", "CHANNEL.TEXT.DELETE")
-            .setInverted(true)
-            .send(true);
-        if (confirmation.cancelled) return;
-        if (confirmation.success) {
-            try {
-                await client.database.guilds.write(interaction.guild!.id, {
-                    "logging.logs.channel": channel.id
-                });
-                const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger;
-                const data = {
-                    meta: {
-                        type: "logChannelUpdate",
-                        displayName: "Log Channel Changed",
-                        calculateType: "nucleusSettingsUpdated",
-                        color: NucleusColors.yellow,
-                        emoji: "CHANNEL.TEXT.EDIT",
-                        timestamp: new Date().getTime()
-                    },
-                    list: {
-                        memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
-                        changedBy: entry(interaction.user.id, renderUser(interaction.user)),
-                        channel: entry(channel.id, renderChannel(channel))
-                    },
-                    hidden: {
-                        guild: channel.guild.id
-                    }
-                };
-                log(data);
-            } catch (e) {
-                console.log(e);
-                return interaction.editReply({
-                    embeds: [
-                        new EmojiEmbed()
-                            .setTitle("Log Channel")
-                            .setDescription("Something went wrong and the log channel could not be set")
-                            .setStatus("Danger")
-                            .setEmoji("CHANNEL.TEXT.DELETE")
-                    ],
-                    components: []
-                });
-            }
-        } else {
-            return interaction.editReply({
-                embeds: [
-                    new EmojiEmbed()
-                        .setTitle("Log Channel")
-                        .setDescription("No changes were made")
-                        .setStatus("Success")
-                        .setEmoji("CHANNEL.TEXT.CREATE")
-                ],
-                components: []
-            });
-        }
-    }
-    let clicks = 0;
-    const data = await client.database.guilds.read(interaction.guild!.id);
-    let channel = data.logging.logs.channel;
-    let timedOut = false;
-    while (!timedOut) {
-        await interaction.editReply({
-            embeds: [
-                new EmojiEmbed()
-                    .setTitle("Log channel")
-                    .setDescription(
-                        channel
-                            ? `Your log channel is currently set to <#${channel}>`
-                            : "This server does not have a log channel"
-                    )
-                    .setStatus("Success")
-                    .setEmoji("CHANNEL.TEXT.CREATE")
-            ],
-            components: [
-                new ActionRowBuilder<ButtonBuilder>().addComponents([
-                    new ButtonBuilder()
-                        .setCustomId("clear")
-                        .setLabel(clicks ? "Click again to confirm" : "Reset channel")
-                        .setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
-                        .setStyle(ButtonStyle.Danger)
-                        .setDisabled(!channel)
-                ])
-            ]
-        });
-        let i: ButtonInteraction;
-        try {
-            i = await m.awaitMessageComponent({
-                time: 300000,
-                filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
-            }) as ButtonInteraction;
-        } catch (e) {
-            timedOut = true;
-        }
-        i = i!
-        i.deferUpdate();
-        if ((i.component as ButtonComponent).customId === "clear") {
-            clicks += 1;
-            if (clicks === 2) {
-                clicks = 0;
-                await client.database.guilds.write(interaction.guild!.id, null, ["logging.logs.channel"]);
-                channel = null;
-            }
-        }
-    }
-    await interaction.editReply({
-        embeds: [
-            new EmojiEmbed()
-                .setTitle("Log channel")
-                .setDescription(
-                    channel
-                        ? `Your log channel is currently set to <#${channel}>`
-                        : "This server does not have a log channel"
-                )
-                .setStatus("Success")
-                .setEmoji("CHANNEL.TEXT.CREATE")
-                .setFooter({ text: "Message closed" })
-        ],
-        components: [
-            new ActionRowBuilder<ButtonBuilder>().addComponents([
-                new ButtonBuilder()
-                    .setCustomId("clear")
-                    .setLabel("Clear")
-                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-                    .setStyle(ButtonStyle.Secondary)
-                    .setDisabled(true)
-            ])
-        ]
-    });
-};
-
-const check = (interaction: CommandInteraction) => {
-    const member = interaction.member as Discord.GuildMember;
-    if (!member.permissions.has("ManageGuild"))
-        return "You must have the *Manage Server* permission to use this command";
-    return true;
-};
-
-export { command };
-export { callback };
-export { check };
diff --git a/src/commands/settings/logs/events.ts b/src/commands/settings/logs/events.ts
index fbe79fa..eeef8fb 100644
--- a/src/commands/settings/logs/events.ts
+++ b/src/commands/settings/logs/events.ts
@@ -1,9 +1,11 @@
 import { LoadingEmbed } from "../../../utils/defaults.js";
-import Discord, { CommandInteraction, Message, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder, EmbedBuilder, StringSelectMenuInteraction } from "discord.js";
-import { SlashCommandSubcommandBuilder, StringSelectMenuOptionBuilder } from "@discordjs/builders";
-import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
+import Discord, { CommandInteraction, ActionRowBuilder, ChannelSelectMenuBuilder, ChannelType, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ButtonInteraction, StringSelectMenuInteraction, ChannelSelectMenuInteraction, APIMessageComponentEmoji } from "discord.js";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
 import client from "../../../utils/client.js";
+import compare from "lodash";
 import { toHexArray, toHexInteger } from "../../../utils/calculate.js";
+import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
+import getEmojiByName from "../../../utils/getEmojiByName.js";
 
 const logs: Record<string, string> = {
     channelUpdate: "Channels created, deleted or modified",
@@ -24,88 +26,138 @@
     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"
+    ticketUpdate: "Tickets created or deleted",
+    //nucleusSettingsUpdated: "Nucleus' settings updated by a moderator"  // TODO
 };
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder.setName("events").setDescription("Sets what events should be logged");
+    builder
+        .setName("events")
+        .setDescription("The general log channel for the server, and setting what events to show")
 
 const callback = async (interaction: CommandInteraction): Promise<void> => {
-    await interaction.reply({
+    const m = (await interaction.reply({
         embeds: LoadingEmbed,
-        fetchReply: true,
-        ephemeral: true
-    });
-    let m: Message;
-    let timedOut = false;
+        ephemeral: true,
+        fetchReply: true
+    })) as Discord.Message;
+
+    let config = await client.database.guilds.read(interaction.guild!.id);
+    let data = Object.assign({}, config.logging.logs);
+    let closed = false;
+    let show = false;
     do {
-        const config = await client.database.guilds.read(interaction.guild!.id);
-        const converted = toHexArray(config.logging.logs.toLog);
-        const selectPane = new StringSelectMenuBuilder()
-            .setPlaceholder("Set events to log")
-            .setMaxValues(Object.keys(logs).length)
-            .setCustomId("logs")
-            .setMinValues(0)
-        Object.keys(logs).map((e, i) => {
-            selectPane.addOptions(new StringSelectMenuOptionBuilder()
-                .setLabel(logs[e]!)
-                .setValue(i.toString())
-                .setDefault(converted.includes(e))
+        const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>()
+            .addComponents(
+                new ChannelSelectMenuBuilder()
+                    .setCustomId("channel")
+                    .setPlaceholder("Select a channel")
+                    .setChannelTypes(ChannelType.GuildText)
+            )
+        const buttons = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents(
+                new ButtonBuilder()
+                    .setCustomId("switch")
+                    .setLabel(data.enabled ? "Enabled" : "Disabled")
+                    .setStyle(data.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
+                    .setEmoji(getEmojiByName((data.enabled ? "CONTROL.TICK" : "CONTROL.CROSS"), "id") as APIMessageComponentEmoji),
+                new ButtonBuilder()
+                    .setCustomId("remove")
+                    .setLabel("Remove")
+                    .setStyle(ButtonStyle.Danger)
+                    .setDisabled(!data.channel),
+                new ButtonBuilder()
+                    .setCustomId("show")
+                    .setLabel("Manage Events")
+                    .setStyle(ButtonStyle.Primary),
+                new ButtonBuilder()
+                    .setCustomId("save")
+                    .setLabel("Save")
+                    .setStyle(ButtonStyle.Success)
+                    .setDisabled(compare.isEqual(data, config.logging.logs))
+            )
+
+        const converted = toHexArray(data.toLog);
+        const toLogMenu = new ActionRowBuilder<StringSelectMenuBuilder>()
+            .addComponents(
+                new StringSelectMenuBuilder()
+                    .setPlaceholder("Set events to log")
+                    .setMaxValues(Object.keys(logs).length)
+                    .setCustomId("logs")
+                    .setMinValues(0)
+            )
+        Object.keys(logs).map((e) => {
+            toLogMenu.components[0]!.addOptions(
+                new StringSelectMenuOptionBuilder()
+                    .setLabel(logs[e]!)
+                    .setValue(e)
+                    .setDefault(converted.includes(e))
             )
         });
-        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 ActionRowBuilder<StringSelectMenuBuilder>().addComponents(selectPane),
-                new ActionRowBuilder<ButtonBuilder>().addComponents([
-                    new ButtonBuilder().setLabel("Select all").setStyle(ButtonStyle.Primary).setCustomId("all"),
-                    new ButtonBuilder().setLabel("Select none").setStyle(ButtonStyle.Danger).setCustomId("none")
-                ])
-            ]
-        })) as Message;
-        let i;
+
+        const embed = new EmojiEmbed()
+            .setTitle("General Log Channel")
+            .setStatus("Success")
+            .setEmoji("CHANNEL.TEXT.CREATE")
+            .setDescription(
+                `This is the channel that all events you set to be logged will be stored\n` +
+                `**Channel:** ${data.channel ? `<#${data.channel}>` : "None"}\n`
+            )
+
+        const components: ActionRowBuilder<ButtonBuilder | ChannelSelectMenuBuilder | StringSelectMenuBuilder>[] = [channelMenu, buttons];
+        if(show) components.push(toLogMenu);
+
+        await interaction.editReply({
+            embeds: [embed],
+            components: components
+        });
+
+        let i: ButtonInteraction | StringSelectMenuInteraction | ChannelSelectMenuInteraction;
         try {
             i = await m.awaitMessageComponent({
-                time: 300000,
-                filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
-            });
+                filter: (i) => i.user.id === interaction.user.id,
+                time: 300000
+            }) as ButtonInteraction | StringSelectMenuInteraction | ChannelSelectMenuInteraction;
         } catch (e) {
-            timedOut = true;
+            closed = true;
             continue;
         }
-        i.deferUpdate();
-        if (i.customId === "logs") {
-            const selected = (i as StringSelectMenuInteraction).values;
-            const newLogs = toHexInteger(selected.map((e: string) => Object.keys(logs)[parseInt(e)]!));
-            await client.database.guilds.write(interaction.guild!.id, {
-                "logging.logs.toLog": newLogs
-            });
-        } else if (i.customId === "all") {
-            const 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
-            });
-        }
-    } while (!timedOut);
 
-    await interaction.editReply({ embeds: [new EmbedBuilder(m.embeds[0]!.data).setFooter({ text: "Message timed out" })] });
-    return;
+        await i.deferUpdate();
+
+        if(i.isButton()) {
+            switch(i.customId) {
+                case "show": {
+                    show = !show;
+                    break;
+                }
+                case "switch": {
+                    data.enabled = !data.enabled;
+                    break;
+                }
+                case "save": {
+                    await client.database.guilds.write(interaction.guild!.id, {"logging.logs": data});
+                    config = await client.database.guilds.read(interaction.guild!.id);
+                    data = Object.assign({}, config.logging.logs);
+                    break;
+                }
+                case "remove": {
+                    data.channel = null;
+                    break;
+                }
+            }
+        } else if(i.isStringSelectMenu()) {
+            const hex = toHexInteger(i.values);
+            data.toLog = hex;
+        } else if(i.isChannelSelectMenu()) {
+            data.channel = i.values[0]!;
+        }
+
+    } while (!closed);
+    await interaction.deleteReply()
 };
 
-const check = (interaction: CommandInteraction) => {
+const check = (interaction: CommandInteraction, _partial: boolean = false) => {
     const member = interaction.member as Discord.GuildMember;
     if (!member.permissions.has("ManageGuild"))
         return "You must have the *Manage Server* permission to use this command";
diff --git a/src/commands/settings/logs/staff.ts b/src/commands/settings/logs/staff.ts
deleted file mode 100644
index 13125ef..0000000
--- a/src/commands/settings/logs/staff.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-import { LoadingEmbed } from "../../../utils/defaults.js";
-import { ChannelType } from "discord-api-types/v9";
-import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonComponent } from "discord.js";
-import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
-import confirmationMessage from "../../../utils/confirmationMessage.js";
-import getEmojiByName from "../../../utils/getEmojiByName.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
-import client from "../../../utils/client.js";
-
-const command = (builder: SlashCommandSubcommandBuilder) =>
-    builder
-        .setName("staff")
-        .setDescription("Settings for the staff notifications channel")
-        .addChannelOption((option) =>
-            option
-                .setName("channel")
-                .setDescription("The channel to set the staff notifications channel to")
-                .addChannelTypes(ChannelType.GuildText)
-                .setRequired(false)
-        );
-
-const callback = async (interaction: CommandInteraction): Promise<unknown> => {
-    if (!interaction.guild) return;
-    const m = (await interaction.reply({
-        embeds: LoadingEmbed,
-        ephemeral: true,
-        fetchReply: true
-    })) as Discord.Message;
-    if (interaction.options.get("channel")?.channel) {
-        let channel;
-        try {
-            channel = interaction.options.get("channel")?.channel;
-        } catch {
-            return await interaction.editReply({
-                embeds: [
-                    new EmojiEmbed()
-                        .setEmoji("CHANNEL.TEXT.DELETE")
-                        .setTitle("Staff Notifications Channel")
-                        .setDescription("The channel you provided is not a valid channel")
-                        .setStatus("Danger")
-                ]
-            });
-        }
-        channel = channel as Discord.TextChannel;
-        if (channel.guild.id !== interaction.guild.id) {
-            return interaction.editReply({
-                embeds: [
-                    new EmojiEmbed()
-                        .setTitle("Staff Notifications Channel")
-                        .setDescription("You must choose a channel in this server")
-                        .setStatus("Danger")
-                        .setEmoji("CHANNEL.TEXT.DELETE")
-                ]
-            });
-        }
-        const confirmation = await new confirmationMessage(interaction)
-            .setEmoji("CHANNEL.TEXT.EDIT")
-            .setTitle("Staff Notifications Channel")
-            .setDescription(
-                "This will be the channel all notifications, updates, user reports etc. will be sent to.\n\n" +
-                    `Are you sure you want to set the staff notifications channel to <#${channel.id}>?`
-            )
-            .setColor("Warning")
-            .setFailedMessage("Staff notifications channel not set", "Warning", "CHANNEL.TEXT.DELETE")
-            .setInverted(true)
-            .send(true);
-        if (confirmation.cancelled) return;
-        if (confirmation.success) {
-            try {
-                await client.database.guilds.write(interaction.guild.id, {
-                    "logging.staff.channel": channel.id
-                });
-                const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger;
-                const data = {
-                    meta: {
-                        type: "staffChannelUpdate",
-                        displayName: "Staff Notifications Channel Updated",
-                        calculateType: "nucleusSettingsUpdated",
-                        color: NucleusColors.yellow,
-                        emoji: "CHANNEL.TEXT.EDIT",
-                        timestamp: new Date().getTime()
-                    },
-                    list: {
-                        memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
-                        changedBy: entry(interaction.user.id, renderUser(interaction.user)),
-                        channel: entry(channel.id, renderChannel(channel))
-                    },
-                    hidden: {
-                        guild: interaction.guild.id
-                    }
-                };
-                log(data);
-            } catch (e) {
-                return interaction.editReply({
-                    embeds: [
-                        new EmojiEmbed()
-                            .setTitle("Staff Notifications Channel")
-                            .setDescription("Something went wrong and the staff notifications channel could not be set")
-                            .setStatus("Danger")
-                            .setEmoji("CHANNEL.TEXT.DELETE")
-                    ],
-                    components: []
-                });
-            }
-        } else {
-            return interaction.editReply({
-                embeds: [
-                    new EmojiEmbed()
-                        .setTitle("Staff Notifications Channel")
-                        .setDescription("No changes were made")
-                        .setStatus("Success")
-                        .setEmoji("CHANNEL.TEXT.CREATE")
-                ],
-                components: []
-            });
-        }
-    }
-    let clicks = 0;
-    const data = await client.database.guilds.read(interaction.guild.id);
-    let channel = data.logging.staff.channel;
-    let timedOut = false;
-    while (!timedOut) {
-        await interaction.editReply({
-            embeds: [
-                new EmojiEmbed()
-                    .setTitle("Staff Notifications channel")
-                    .setDescription(
-                        channel
-                            ? `Your staff notifications channel is currently set to <#${channel}>`
-                            : "This server does not have a staff notifications channel"
-                    )
-                    .setStatus("Success")
-                    .setEmoji("CHANNEL.TEXT.CREATE")
-            ],
-            components: [
-                new ActionRowBuilder<ButtonBuilder>().addComponents([
-                    new ButtonBuilder()
-                        .setCustomId("clear")
-                        .setLabel(clicks ? "Click again to confirm" : "Reset channel")
-                        .setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
-                        .setStyle(ButtonStyle.Danger)
-                        .setDisabled(!channel)
-                ])
-            ]
-        });
-        let i;
-        try {
-            i = await m.awaitMessageComponent({
-                time: 300000,
-                filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
-            });
-        } catch (e) {
-            timedOut = true;
-            continue;
-        }
-        i.deferUpdate();
-        if ((i.component as ButtonComponent).customId === "clear") {
-            clicks += 1;
-            if (clicks === 2) {
-                clicks = 0;
-                await client.database.guilds.write(interaction.guild.id, null, ["logging.staff.channel"]);
-                channel = null;
-            }
-        }
-    }
-    await interaction.editReply({
-        embeds: [
-            new EmojiEmbed()
-                .setTitle("Staff Notifications channel")
-                .setDescription(
-                    channel
-                        ? `Your staff notifications channel is currently set to <#${channel}>`
-                        : "This server does not have a staff notifications channel"
-                )
-                .setStatus("Success")
-                .setEmoji("CHANNEL.TEXT.CREATE")
-                .setFooter({ text: "Message closed" })
-        ],
-        components: [
-            new ActionRowBuilder<ButtonBuilder>().addComponents([
-                new ButtonBuilder()
-                    .setCustomId("clear")
-                    .setLabel("Clear")
-                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-                    .setStyle(ButtonStyle.Secondary)
-                    .setDisabled(true)
-            ])
-        ]
-    });
-};
-
-const check = (interaction: CommandInteraction) => {
-    const member = interaction.member as Discord.GuildMember;
-    if (!member.permissions.has("ManageGuild"))
-        return "You must have the *Manage Server* permission to use this command";
-    return true;
-};
-
-export { command };
-export { callback };
-export { check };
diff --git a/src/commands/settings/logs/warnings.ts b/src/commands/settings/logs/warnings.ts
new file mode 100644
index 0000000..84772e6
--- /dev/null
+++ b/src/commands/settings/logs/warnings.ts
@@ -0,0 +1,104 @@
+import { LoadingEmbed } from "../../../utils/defaults.js";
+import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelSelectMenuBuilder, ChannelType } from "discord.js";
+import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
+import getEmojiByName from "../../../utils/getEmojiByName.js";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
+import client from "../../../utils/client.js";
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+    builder
+        .setName("warnings")
+        .setDescription("Settings for the staff notifications channel")
+
+const callback = async (interaction: CommandInteraction): Promise<unknown> => {
+    if (!interaction.guild) return;
+    await interaction.reply({
+        embeds: LoadingEmbed,
+        ephemeral: true,
+        fetchReply: true
+    })
+
+    let data = await client.database.guilds.read(interaction.guild.id);
+    let channel = data.logging.staff.channel;
+    let closed = false;
+    do {
+        const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>()
+        .addComponents(
+            new ChannelSelectMenuBuilder()
+                .setCustomId("channel")
+                .setPlaceholder("Select a channel")
+                .setChannelTypes(ChannelType.GuildText)
+        );
+        const buttons = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents(
+                new ButtonBuilder()
+                    .setCustomId("clear")
+                    .setLabel("Clear")
+                    .setStyle(ButtonStyle.Danger)
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id") as Discord.APIMessageComponentEmoji)
+                    .setDisabled(!channel),
+                new ButtonBuilder()
+                    .setCustomId("save")
+                    .setLabel("Save")
+                    .setStyle(ButtonStyle.Success)
+                    .setEmoji(getEmojiByName("ICONS.SAVE", "id") as Discord.APIMessageComponentEmoji)
+                    .setDisabled(channel === data.logging.staff.channel)
+            );
+
+        const embed = new EmojiEmbed()
+            .setTitle("Staff Notifications Channel")
+            .setStatus("Success")
+            .setEmoji("CHANNEL.TEXT.CREATE")
+            .setDescription(
+                `Logs which require an action from a moderator or administrator will be sent to this channel.\n` +
+                `**Channel:** ${channel ? `<#${channel}>` : "*None*"}\n`
+            )
+
+        await interaction.editReply({
+            embeds: [embed],
+            components: [channelMenu, buttons]
+        });
+
+        let i: Discord.ButtonInteraction | Discord.SelectMenuInteraction;
+        try {
+            i = (await interaction.channel!.awaitMessageComponent({
+                filter: (i: Discord.Interaction) => i.user.id === interaction.user.id,
+                time: 300000
+            })) as Discord.ButtonInteraction | Discord.SelectMenuInteraction;
+        } catch (e) {
+            closed = true;
+            continue;
+        }
+        await i.deferUpdate();
+        if(i.isButton()) {
+            switch (i.customId) {
+                case "clear": {
+                    channel = null;
+                    break;
+                }
+                case "save": {
+                    await client.database.guilds.write(interaction.guild!.id, {
+                        "logging.warnings.channel": channel
+                    });
+                    data = await client.database.guilds.read(interaction.guild!.id);
+                    break;
+                }
+            }
+        } else {
+            channel = i.values[0]!;
+        }
+    } while (!closed);
+
+    await interaction.deleteReply()
+};
+
+const check = (interaction: CommandInteraction, _partial: boolean = false) => {
+    const member = interaction.member as Discord.GuildMember;
+    if (!member.permissions.has("ManageGuild"))
+        return "You must have the *Manage Server* permission to use this command";
+    return true;
+};
+
+export { command };
+export { callback };
+export { check };