Tickets! and a lot of bug fixes
diff --git a/src/utils/calculate.ts b/src/utils/calculate.ts
index 98d82c3..0bd5a9f 100644
--- a/src/utils/calculate.ts
+++ b/src/utils/calculate.ts
@@ -23,7 +23,7 @@
 
 const tickets = ["support", "report", "question", "issue", "suggestion", "other"];
 
-const toHexInteger = (permissions: string[], array?: string[]) => {
+const toHexInteger = (permissions: string[], array?: string[]): string => {
     if (!array) {
         array = logs;
     }
@@ -35,18 +35,18 @@
     return int.toString(16);
 };
 
-const toHexArray = (permissionsHex: string, array?: string[]) => {
+const toHexArray = (permissionsHex: string, array?: string[]): string[] => {
     if (!array) {
         array = logs;
     }
-    const permissions = [];
+    const permissions: string[] = [];
     const int = BigInt("0x" + permissionsHex)
         .toString(2)
         .split("")
         .reverse();
     for (const index in int) {
         if (int[index] === "1" && array.length > parseInt(index)) {
-            permissions.push(array[index]);
+            permissions.push(array[index]!);
         }
     }
     return permissions;
diff --git a/src/utils/client.ts b/src/utils/client.ts
index 43cbe11..46d9f92 100644
--- a/src/utils/client.ts
+++ b/src/utils/client.ts
@@ -23,6 +23,7 @@
         eventScheduler: EventScheduler;
         performanceTest: PerformanceTest;
     };
+    preloadPage: Record<string, {command: string, argument: string}> = {};  // e.g. { channelID: { command: privacy, page: 3}}
     commands: Record<string, {
         command: Discord.SlashCommandBuilder |
                 ((builder: Discord.SlashCommandBuilder) => Discord.SlashCommandBuilder) |
diff --git a/src/utils/commandRegistration/register.ts b/src/utils/commandRegistration/register.ts
index d96ca90..fdae167 100644
--- a/src/utils/commandRegistration/register.ts
+++ b/src/utils/commandRegistration/register.ts
@@ -1,8 +1,10 @@
+import type { CommandInteraction } from 'discord.js';
 import Discord, { Interaction, SlashCommandBuilder, ApplicationCommandType } from 'discord.js';
 import config from "../../config/main.json" assert { type: "json" };
 import client from "../client.js";
 import fs from "fs";
-
+import EmojiEmbed from '../generateEmojiEmbed.js';
+import getEmojiByName from '../getEmojiByName.js';
 
 const colours = {
     red: "\x1b[31m",
@@ -174,17 +176,25 @@
     });
 }
 
-async function execute(check: Function | undefined, callback: Function | undefined, data: Interaction) {
+async function execute(check: Function | undefined, callback: Function | undefined, data: CommandInteraction) {
     if (!callback) return;
     if (check) {
         let result;
         try {
             result = await check(data);
-        } catch (e) {
-            console.log(e);
+        } catch(e) {
             result = false;
         }
-        if (!result) return;
+        if (result === false) return;
+        if (typeof result === "string") {
+            const { NucleusColors } = client.logger
+            return data.reply({embeds: [new EmojiEmbed()
+                .setTitle("")
+                .setDescription(result)
+                .setColor(NucleusColors.red)
+                .setEmoji(getEmojiByName("CONTROL.BLOCKCROSS"))
+            ]});
+        };
     }
     callback(data);
 }
diff --git a/src/utils/confirmationMessage.ts b/src/utils/confirmationMessage.ts
index b3202e3..4d90676 100644
--- a/src/utils/confirmationMessage.ts
+++ b/src/utils/confirmationMessage.ts
@@ -183,7 +183,7 @@
             let component;
             try {
                 component = await m.awaitMessageComponent({
-                    filter: (m) => m.user.id === this.interaction.user.id,
+                    filter: (m) => m.user.id === this.interaction.user.id && m.channel!.id === this.interaction.channel!.id,
                     time: 300000
                 });
             } catch (e) {
diff --git a/src/utils/createLogException.ts b/src/utils/createLogException.ts
deleted file mode 100644
index 329f422..0000000
--- a/src/utils/createLogException.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import client from "./client.js";
-
-export default function (guild: string, channel: string, message: string) {
-    client.noLog.push(`${guild}/${channel}/${message}`);
-    setTimeout(() => {
-        client.noLog = client.noLog.filter((i: string) => {
-            return i !== `${guild}/${channel}/${message}`;
-        });
-    }, 500);
-}
diff --git a/src/utils/createTemporaryStorage.ts b/src/utils/createTemporaryStorage.ts
new file mode 100644
index 0000000..a684d9d
--- /dev/null
+++ b/src/utils/createTemporaryStorage.ts
@@ -0,0 +1,32 @@
+import client from "./client.js";
+
+function generalException(location: string) {
+    client.noLog.push(location);
+    setTimeout(() => {
+        client.noLog = client.noLog.filter((i: string) => {
+            return i !== location;
+        });
+    }, 1000);
+}
+
+export function messageException(guild: string, channel: string, message: string) {
+    generalException(`${guild}/${channel}/${message}`);
+}
+
+export function roleException(guild: string, user: string) {
+    generalException(`${guild}/${user}`);
+}
+
+export function preloadPage(target: string, command: string, message: string) {
+    client.preloadPage[target] = {
+        command: command,
+        argument: message
+    }
+    setTimeout(() => {
+        const object = Object.entries(client.preloadPage).filter((entry) => {
+            const [k, _] = entry
+            return k !== target;
+        })
+        client.preloadPage = Object.fromEntries(object);
+    }, 60 * 5 * 1000);
+}
\ No newline at end of file
diff --git a/src/utils/defaultEmbeds.ts b/src/utils/defaults.ts
similarity index 73%
rename from src/utils/defaultEmbeds.ts
rename to src/utils/defaults.ts
index a027c76..1331ce1 100644
--- a/src/utils/defaultEmbeds.ts
+++ b/src/utils/defaults.ts
@@ -1,3 +1,4 @@
+import type Discord from "discord.js";
 import EmojiEmbed from "./generateEmojiEmbed.js";
 import getEmojiByName from "./getEmojiByName.js";
 
@@ -15,6 +16,7 @@
     title: string = "";
     description = "";
     pageId = 0;
+    componentsToSet: Discord.ActionRowBuilder<Discord.ButtonBuilder | Discord.StringSelectMenuBuilder>[] = [];
 
     setEmbed(embed: EmojiEmbed) {
         this.embed = embed;
@@ -32,6 +34,12 @@
         this.pageId = pageId;
         return this;
     }
+    setComponents(components: Discord.ActionRowBuilder<Discord.ButtonBuilder | Discord.StringSelectMenuBuilder>[]) {
+        this.componentsToSet = components;
+        return this;
+    }
 }
 
 export { Embed };
+
+export const unknownServerIcon = "";
diff --git a/src/utils/getCommandMentionByName.ts b/src/utils/getCommandMentionByName.ts
new file mode 100644
index 0000000..b2b9937
--- /dev/null
+++ b/src/utils/getCommandMentionByName.ts
@@ -0,0 +1,22 @@
+import type Discord from "discord.js";
+import client from "./client.js";
+import config from "../config/main.json" assert { type: "json"};
+
+
+export const getCommandMentionByName = async (name: string): Promise<string> => {
+    const split = name.replaceAll("/", " ").split(" ")
+    const commandName: string = split[0]!;
+    let commandID: string;
+
+    const filterCommand = (command: Discord.ApplicationCommand) => command.name === commandName;
+
+    if (config.enableDevelopment) {
+        const developmentGuild = client.guilds.cache.get(config.developmentGuildID)!;
+        await developmentGuild.commands.fetch();
+        commandID = developmentGuild.commands.cache.filter(c => filterCommand(c)).first()!.id;
+    } else {
+        await client.application?.commands.fetch();
+        commandID = client.application?.commands.cache.filter(c => filterCommand(c)).first()!.id!;
+    }
+    return `</${split.join(" ")}:${commandID}>`;
+}
\ No newline at end of file
diff --git a/src/utils/log.ts b/src/utils/log.ts
index 7ab7903..3f46f86 100644
--- a/src/utils/log.ts
+++ b/src/utils/log.ts
@@ -26,6 +26,7 @@
         return `${num1} -> ${num2} (${delta > 0 ? "+" : ""}${delta})`;
     },
     entry(value: string | number | null, displayValue: string): { value: string | null; displayValue: string } {
+        if (typeof value === "number") value = value.toString();
         return { value: value, displayValue: displayValue };
     },
     renderChannel(channel: Discord.GuildChannel | Discord.ThreadChannel) {
diff --git a/src/utils/logTranscripts.ts b/src/utils/logTranscripts.ts
index d5ab0b2..0950664 100644
--- a/src/utils/logTranscripts.ts
+++ b/src/utils/logTranscripts.ts
@@ -1,3 +1,64 @@
-function JSONTranscriptFromMessageArray(messages: Discord.Message[]) {
-    
+import type Discord from 'discord.js';
+
+export interface JSONTranscriptSchema {
+    messages: {
+        content: string | null;
+        attachments: {
+            url: string;
+            name: string;
+            size: number;
+        }[];
+        authorID: string;
+        authorUsername: string;
+        authorUsernameColor: string;
+        timestamp: string;
+        id: string;
+        edited: boolean;
+    }[];
+    channel: string;
+    guild: string;
+    timestamp: string;
+}
+
+
+export const JSONTranscriptFromMessageArray = (messages: Discord.Message[]): JSONTranscriptSchema | null => {
+    if (messages.length === 0) return null;
+    return {
+        guild: messages[0]!.guild!.id,
+        channel: messages[0]!.channel.id,
+        timestamp: Date.now().toString(),
+        messages: messages.map((message: Discord.Message) => {
+            return {
+                content: message.content,
+                attachments: message.attachments.map((attachment: Discord.Attachment) => {
+                    return {
+                        url: attachment.url,
+                        name: attachment.name!,
+                        size: attachment.size,
+                    };
+                }),
+                authorID: message.author.id,
+                authorUsername: message.author.username + "#" + message.author.discriminator,
+                authorUsernameColor: message.member!.displayHexColor.toString(),
+                timestamp: message.createdTimestamp.toString(),
+                id: message.id,
+                edited: message.editedTimestamp ? true : false,
+            };
+        })
+    };
+}
+
+export const JSONTranscriptToHumanReadable = (data: JSONTranscriptSchema): string => {
+    let out = "";
+
+    for (const message of data.messages) {
+        const date = new Date(parseInt(message.timestamp));
+        out += `${message.authorUsername} (${message.authorID}) [${date}]`;
+        if (message.edited) out += " (edited)";
+        if (message.content) out += "\nContent:\n" + message.content.split("\n").map((line: string) => `\n> ${line}`).join("");
+        if (message.attachments.length > 0) out += "\nAttachments:\n" + message.attachments.map((attachment: { url: string; name: string; size: number; }) => `\n> [${attachment.name}](${attachment.url}) (${attachment.size} bytes)`).join("\n");
+
+        out += "\n\n";
+    }
+    return out;
 }
\ No newline at end of file
diff --git a/src/utils/performanceTesting/record.ts b/src/utils/performanceTesting/record.ts
index 9f840af..95761e9 100644
--- a/src/utils/performanceTesting/record.ts
+++ b/src/utils/performanceTesting/record.ts
@@ -29,6 +29,7 @@
 }
 
 const record = async () => {
+    if (config.enableDevelopment) return;
     const results = {
         discord: discordPing(),
         databaseRead: await databaseReadTime(),