Update purge.ts - In theory the buttons should work now (#37)

diff --git a/src/actions/roleMenu.ts b/src/actions/roleMenu.ts
index 27b5f07..9796b28 100644
--- a/src/actions/roleMenu.ts
+++ b/src/actions/roleMenu.ts
@@ -70,7 +70,7 @@
     if (!interaction.member) return;
     if (!interaction.guild) return;
     const config = await client.database.guilds.read(interaction.guild.id);
-    if (!config.roleMenu.enabled)
+    if (!config.roleMenu.enabled) {
         return await interaction.reply({
             embeds: [
                 new EmojiEmbed()
@@ -83,6 +83,7 @@
             ],
             ephemeral: true
         });
+    }
     if (config.roleMenu.options.length === 0)
         return await interaction.reply({
             embeds: [
diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts
index c5fe2fb..ff69079 100644
--- a/src/commands/mod/purge.ts
+++ b/src/commands/mod/purge.ts
@@ -102,7 +102,7 @@
             try {
                 component = m.awaitMessageComponent({
                     filter: (i) =>
-                        i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.id === m.id,
+                        i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id,
                     time: 300000
                 });
             } catch (e) {
diff --git a/src/commands/nucleus/invite.ts b/src/commands/nucleus/invite.ts
index 6805adb..9f78cc5 100644
--- a/src/commands/nucleus/invite.ts
+++ b/src/commands/nucleus/invite.ts
@@ -23,7 +23,7 @@
                     .setURL(
                         `https://discord.com/api/oauth2/authorize?client_id=${
                             client.user!.id
-                        }&permissions=295157886134&scope=bot%20applications.commands`
+                        }&permissions=407900777662&scope=bot%20applications.commands`
                     )
             ])
         ],
diff --git a/src/commands/nucleus/stats.ts b/src/commands/nucleus/stats.ts
index 19c0949..058695c 100644
--- a/src/commands/nucleus/stats.ts
+++ b/src/commands/nucleus/stats.ts
@@ -1,22 +1,182 @@
-import type { CommandInteraction } from "discord.js";
+import { ActionRowBuilder, AttachmentBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, ChannelType, CommandInteraction, ComponentType, Guild, ModalBuilder, ModalSubmitInteraction, TextInputBuilder, TextInputStyle } from "discord.js";
 import type { SlashCommandSubcommandBuilder } from "discord.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import client from "../../utils/client.js";
+import config from "../../config/main.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder.setName("stats").setDescription("Gets the bot's stats");
 
 const callback = async (interaction: CommandInteraction): Promise<void> => {
-    interaction.reply({
+    const description = `**Servers:** ${client.guilds.cache.size}\n` + `**Ping:** \`${client.ws.ping * 2}ms\``
+    const m = await interaction.reply({
         embeds: [
             new EmojiEmbed()
                 .setTitle("Stats")
-                .setDescription(`**Servers:** ${client.guilds.cache.size}\n` + `**Ping:** \`${client.ws.ping * 2}ms\``)
+                .setDescription(description)
                 .setStatus("Success")
                 .setEmoji("SETTINGS.STATS.GREEN")
         ],
-        ephemeral: true
+        ephemeral: true,
+        fetchReply: true
     });
+    if (config.owners.includes(interaction.user.id)) {
+        interaction.editReply({
+            embeds: [
+                new EmojiEmbed()
+                    .setTitle("Admin")
+                    .setDescription(description)
+                    .setStatus("Success")
+                    .setEmoji("SETTINGS.STATS.GREEN")
+            ], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder().setCustomId("admin").setLabel("Admin Panel").setStyle(ButtonStyle.Primary))]
+        });
+
+        const modal = new ModalBuilder()
+            .addComponents(
+                new ActionRowBuilder<TextInputBuilder>()
+                    .addComponents(
+                        new TextInputBuilder()
+                            .setStyle(TextInputStyle.Short)
+                            .setLabel("Guild ID")
+                            .setCustomId("guildID")
+                            .setPlaceholder("Guild ID")
+                            .setMinLength(16)
+                            .setMaxLength(25)
+                    )
+            )
+            .setTitle("Admin Panel")
+            .setCustomId("adminPanel")
+        let i1: ButtonInteraction;
+        const channel = await client.channels.fetch(interaction.channelId)
+        if(!channel || [ChannelType.GuildCategory, ChannelType.GroupDM, ChannelType.GuildStageVoice].includes(channel.type)) return;
+        // console.log(interaction)
+        if (!("awaitMessageComponent" in channel)) return;
+        try {
+            i1 = await channel!.awaitMessageComponent<ComponentType.Button>({
+                filter: (i) => i.customId === "admin" && i.user.id === interaction.user.id,
+                time: 300000
+            });
+        } catch (e) { console.log(e); return }
+        await i1.showModal(modal)
+        let out: ModalSubmitInteraction;
+        try {
+            out = await i1.awaitModalSubmit({
+                filter: (i) => i.customId === "adminPanel" && i.user.id === interaction.user.id,
+                time: 300000
+            })
+        } catch { return }
+        out.deferUpdate();
+        const GuildID = out.fields.getTextInputValue("guildID");
+        if (!client.guilds.cache.has(GuildID)) {
+            await interaction.editReply({
+                embeds: [
+                    new EmojiEmbed()
+                        .setTitle("Admin")
+                        .setDescription("Not in server")
+                        .setStatus("Danger")
+                ], components: []
+            });
+        };
+
+        await interaction.editReply({
+            embeds: [],
+            components: [
+                new ActionRowBuilder<ButtonBuilder>().addComponents(
+                    new ButtonBuilder().setCustomId("stats").setLabel("Stats").setStyle(ButtonStyle.Primary),
+                    new ButtonBuilder().setCustomId("leave").setLabel("Leave").setStyle(ButtonStyle.Danger),
+                    new ButtonBuilder().setCustomId("data").setLabel("Guild data").setStyle(ButtonStyle.Secondary),
+                    new ButtonBuilder().setCustomId("purge").setLabel("Delete data").setStyle(ButtonStyle.Danger),
+                    new ButtonBuilder().setCustomId("cache").setLabel("Reset cache").setStyle(ButtonStyle.Success)
+                )
+        ]});
+        let i;
+        try {
+            i = await m.awaitMessageComponent<ComponentType.Button>({
+                filter: (i) => i.user.id === interaction.user.id,
+                time: 300000
+            })
+        } catch { return }
+        i.deferUpdate();
+        const guild = await client.guilds.fetch(GuildID) as Guild | null;
+        if (!guild) {
+            await interaction.editReply({
+                embeds: [
+                    new EmojiEmbed()
+                        .setTitle("Admin")
+                        .setDescription("Not in server")
+                        .setStatus("Danger")
+                ], components: []
+            });
+            return;
+        }
+        if (i.customId === "stats") {
+            await interaction.editReply({
+                embeds: [
+                    new EmojiEmbed()
+                        .setTitle("Stats")
+                        .setDescription(
+                            `**Name:** ${guild.name}\n` +
+                            `**ID:** \`${guild.id}\`\n` +
+                            `**Owner:** ${client.users.cache.get(guild.ownerId)!.tag}\n` +
+                            `**Member Count:** ${guild.memberCount}\n` +
+                            `**Created:** <t:${guild.createdTimestamp}:F>\n` +
+                            `**Added Nucleus:** <t:${guild.members.me!.joinedTimestamp}:R>\n` +
+                            `**Nucleus' Perms:** https://discordapi.com/permissions.html#${guild.members.me!.permissions.valueOf()}\n`
+                        )
+                        .setStatus("Success")
+                        .setEmoji("SETTINGS.STATS.GREEN")
+                ]
+            })
+        } else if (i.customId === "leave") {
+            await guild.leave();
+            await interaction.editReply({
+                embeds: [
+                    new EmojiEmbed()
+                        .setTitle("Left")
+                        .setDescription(`Left ${guild.name}`)
+                        .setStatus("Success")
+                        .setEmoji("SETTINGS.STATS.GREEN")
+                ], components: []
+            })
+        } else if (i.customId === "data") {
+            // Get all the data and convert to a string
+            const data = await client.database.guilds.read(guild.id);
+            const stringified = JSON.stringify(data, null, 2);
+            const buffer = Buffer.from(stringified);
+            const attachment = new AttachmentBuilder(buffer).setName("data.json");
+            await interaction.editReply({
+                embeds: [
+                    new EmojiEmbed().setTitle("Data").setDescription(`Data for ${guild.name}`).setStatus("Success")
+                ], components: [],
+                files: [attachment]
+            })
+        } else if (i.customId === "purge") {
+            await client.database.guilds.delete(GuildID);
+            await client.database.history.delete(GuildID);
+            await client.database.notes.delete(GuildID);
+            await client.database.transcripts.deleteAll(GuildID);
+            await interaction.editReply({
+                embeds: [
+                    new EmojiEmbed()
+                        .setTitle("Purge")
+                        .setDescription(`Deleted data for ${guild.name}`)
+                        .setStatus("Success")
+                        .setEmoji("SETTINGS.STATS.GREEN")
+                ], components: []
+            })
+        } else if (i.customId === "cache") {
+            await client.memory.forceUpdate(guild.id);
+            await interaction.editReply({
+                embeds: [
+                    new EmojiEmbed()
+                        .setTitle("Cache")
+                        .setDescription(`Reset cache for ${guild.name}`)
+                        .setStatus("Success")
+                        .setEmoji("SETTINGS.STATS.GREEN")
+                ], components: []
+            })
+        }
+    }
 };
 
 export { command };
diff --git a/src/commands/settings/rolemenu.ts b/src/commands/settings/rolemenu.ts
index 11c6b67..0c174f5 100644
--- a/src/commands/settings/rolemenu.ts
+++ b/src/commands/settings/rolemenu.ts
@@ -25,9 +25,9 @@
 import { configToDropdown } from "../../actions/roleMenu.js";
 import { modalInteractionCollector } from "../../utils/dualCollector.js";
 import ellipsis from "../../utils/ellipsis.js";
-import lodash from "lodash";
+import _ from "lodash";
 
-const isEqual = lodash.isEqual;
+const isEqual = _.isEqual;
 
 const command = (builder: SlashCommandSubcommandBuilder) => builder.setName("rolemenu").setDescription("rolemenu");
 
@@ -163,19 +163,20 @@
     return [name, description];
 };
 
+const defaultRoleMenuData = {
+    name: "Role Menu Page",
+    description: "A new role menu page",
+    min: 0,
+    max: 0,
+    options: []
+};
+
 const editRoleMenuPage = async (
     interaction: StringSelectMenuInteraction | ButtonInteraction,
     m: Message,
     data?: ObjectSchema
 ): Promise<ObjectSchema | null> => {
-    if (!data)
-        data = {
-            name: "Role Menu Page",
-            description: "A new role menu page",
-            min: 0,
-            max: 0,
-            options: []
-        };
+    if (!data) data = _.cloneDeep(defaultRoleMenuData)
     const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
         new ButtonBuilder()
             .setCustomId("back")
@@ -357,7 +358,7 @@
     let page = 0;
     let closed = false;
     const config = await client.database.guilds.read(interaction.guild.id);
-    let currentObject: ObjectSchema[] = config.roleMenu.options;
+    let currentObject: ObjectSchema[] = _.cloneDeep(config.roleMenu.options);
     let modified = false;
     do {
         const embed = new EmojiEmbed().setTitle("Role Menu").setEmoji("GUILD.GREEN").setStatus("Success");
@@ -392,7 +393,7 @@
                 .setCustomId("next")
                 .setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji)
                 .setStyle(ButtonStyle.Primary)
-                .setDisabled(page === Object.keys(currentObject).length - 1),
+                .setDisabled(page === Object.keys(currentObject).length - 1 || noRoleMenus),
             new ButtonBuilder()
                 .setCustomId("add")
                 .setLabel("New Page")
@@ -472,7 +473,7 @@
                 }
                 case "add": {
                     const newPage = await editRoleMenuPage(i, m);
-                    if (!newPage) break;
+                    if (_.isEqual(newPage, defaultRoleMenuData)) break;
                     currentObject.push();
                     page = currentObject.length - 1;
                     break;
diff --git a/src/commands/settings/tracks.ts b/src/commands/settings/tracks.ts
index 5215b3f..625bcfb 100644
--- a/src/commands/settings/tracks.ts
+++ b/src/commands/settings/tracks.ts
@@ -28,6 +28,7 @@
 import getEmojiByName from "../../utils/getEmojiByName.js";
 import ellipsis from "../../utils/ellipsis.js";
 import { modalInteractionCollector } from "../../utils/dualCollector.js";
+import _ from "lodash";
 
 const { renderRole } = client.logger;
 
@@ -96,7 +97,8 @@
 };
 
 const reorderTracks = async (
-    interaction: ButtonInteraction,
+    interaction: ButtonInteraction | StringSelectMenuInteraction,
+    buttonInteraction: ButtonInteraction,
     m: Message,
     roles: Collection<string, Role>,
     currentObj: string[]
@@ -132,7 +134,7 @@
     let out: StringSelectMenuInteraction | ButtonInteraction | null;
     try {
         out = (await m.awaitMessageComponent({
-            filter: (i) => i.channel!.id === interaction.channel!.id,
+            filter: (i) => i.channel!.id === buttonInteraction.channel!.id,
             time: 300000
         })) as StringSelectMenuInteraction | ButtonInteraction | null;
     } catch (e) {
@@ -152,6 +154,14 @@
     return newOrder;
 };
 
+const defaultTrackData = {
+    name: "",
+    retainPrevious: false,
+    nullable: true,
+    track: [],
+    manageableBy: []
+};
+
 const editTrack = async (
     interaction: ButtonInteraction | StringSelectMenuInteraction,
     message: Message,
@@ -160,13 +170,7 @@
 ) => {
     const isAdmin = (interaction.member!.permissions as PermissionsBitField).has("Administrator");
     if (!current) {
-        current = {
-            name: "",
-            retainPrevious: false,
-            nullable: true,
-            track: [],
-            manageableBy: []
-        };
+        current = _.cloneDeep(defaultTrackData);
     }
 
     const roleSelect = new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(
@@ -292,7 +296,7 @@
                 }
                 case "reorder": {
                     out.deferUpdate();
-                    current.track = (await reorderTracks(out, message, roles, current.track))!;
+                    current.track = (await reorderTracks(interaction, out, message, roles, current.track))!;
                     break;
                 }
                 case "retainPrevious": {
@@ -382,7 +386,7 @@
                 .setCustomId("next")
                 .setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji)
                 .setStyle(ButtonStyle.Primary)
-                .setDisabled(page === tracks.length - 1),
+                .setDisabled(page === tracks.length - 1 || noTracks),
             new ButtonBuilder()
                 .setCustomId("add")
                 .setLabel("New Track")
@@ -466,7 +470,7 @@
                 }
                 case "add": {
                     const newPage = await editTrack(i, m, roles);
-                    if (!newPage) break;
+                    if (_.isEqual(newPage, defaultTrackData)) break;
                     tracks.push();
                     page = tracks.length - 1;
                     break;
diff --git a/src/utils/database.ts b/src/utils/database.ts
index a5d5149..75a79d9 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -221,7 +221,7 @@
 interface findDocSchema {
     channelID: string;
     messageID: string;
-    transcript: string;
+    code: string;
 }
 
 export class Transcript {
@@ -284,16 +284,20 @@
     async deleteAll(guild: string) {
         // console.log("Transcript delete")
         const filteredDocs = await this.transcripts.find({ guild: guild }).toArray();
+        const filteredDocs1  = await this.messageToTranscript.find({ guild: guild }).toArray();
         for (const doc of filteredDocs) {
             await this.transcripts.deleteOne({ code: doc.code });
         }
+        for (const doc of filteredDocs1) {
+            await this.messageToTranscript.deleteOne({ code: doc.code });
+        }
     }
 
     async readEncrypted(code: string) {
         // console.log("Transcript read")
         let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code });
         let findDoc: findDocSchema | null = null;
-        if (!doc) findDoc = await this.messageToTranscript.findOne({ transcript: code });
+        if (!doc) findDoc = await this.messageToTranscript.findOne({ code: code });
         if (findDoc) {
             const message = await (
                 client.channels.cache.get(findDoc.channelID) as Discord.TextBasedChannel | null
@@ -330,7 +334,7 @@
         let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code });
         let findDoc: findDocSchema | null = null;
         console.log(doc);
-        if (!doc) findDoc = await this.messageToTranscript.findOne({ transcript: code });
+        if (!doc) findDoc = await this.messageToTranscript.findOne({ code: code });
         if (findDoc) {
             const message = await (
                 client.channels.cache.get(findDoc.channelID) as Discord.TextBasedChannel | null
@@ -412,7 +416,7 @@
                     topRole: {
                         color: message.member ? message.member.roles.highest.color : 0x000000
                     },
-                    iconURL: (message.member?.user || message.author).displayAvatarURL({ forceStatic: true }),
+                    iconURL: (message.member?.user ?? message.author).displayAvatarURL({ forceStatic: true }),
                     bot: message.author.bot || false
                 },
                 createdTimestamp: message.createdTimestamp
diff --git a/src/utils/eventScheduler.ts b/src/utils/eventScheduler.ts
index 2ef5fb4..34bb28e 100644
--- a/src/utils/eventScheduler.ts
+++ b/src/utils/eventScheduler.ts
@@ -40,7 +40,7 @@
             await client.database.transcripts.upload({
                 channelID: channel.id,
                 messageID: m.id,
-                transcript: job.attrs.data.transcript
+                code: job.attrs.data.transcript
             });
             await job.remove();
         });