Merge branch 'development' of github.com:clicksminuteper/nucleus into development
diff --git a/TODO b/TODO
index 579b574..d2cd1a2 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,7 @@
 Role all
 Server rules
 verificationRequired on welcome
+// TODO !IMPORTANT! URL + image hash + file hash database
 
 ROLE MENU SETTINGS
 
diff --git a/package.json b/package.json
index 57cf3c5..43843f8 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
         "force-dev": "rm -rf dist && eslint . --fix; tsc-suppress && node --experimental-json-modules --enable-source-maps dist/index.js",
         "lint": "echo 'Style checking...'; prettier --check .; echo 'Linting...'; eslint .; echo 'To auto-fix everything possible, please run `yarn lint-fix`'; true",
         "lint-fix": "echo 'Fixing eslint issues...'; eslint . --fix; echo 'Reformatting...'; prettier --write --loglevel warn --cache .; true",
-        "lint-list": "echo 'Style checking...'; prettier --check .; echo 'Linting...'; eslint .; echo 'To view errors in more detail, please run `yarn lint`; true",
+        "lint-list": "echo 'Style checking...'; prettier --check .; echo 'Linting...'; eslint .; echo 'To view errors in more detail, please run `yarn lint`'; true",
         "setup": "node Installer.js"
     },
     "repository": {
diff --git a/src/commands/settings/welcome.ts b/src/commands/settings/welcome.ts
index d1c2011..188e4b9 100644
--- a/src/commands/settings/welcome.ts
+++ b/src/commands/settings/welcome.ts
@@ -52,12 +52,13 @@
         interaction.options.getChannel("channel") ||
         interaction.options.getString("message")
     ) {
-        let role: Role;
-        let ping: Role;
-        const message = interaction.options.getString("message");
+        let role: Role | null;
+        let ping: Role | null;
+        let channel: Channel | null;
+        const message: string | null = interaction.options.getString("message");
         try {
-            role = interaction.options.getRole("role") as Role;
-            ping = interaction.options.getRole("ping") as Role;
+            role = interaction.options.getRole("role") as Role | null;
+            ping = interaction.options.getRole("ping") as Role | null;
         } catch {
             return await interaction.editReply({
                 embeds: [
@@ -69,9 +70,8 @@
                 ]
             });
         }
-        let channel: Channel;
         try {
-            channel = interaction.options.getChannel("channel") as Channel;
+            channel = interaction.options.getChannel("channel") as Channel | null;
         } catch {
             return await interaction.editReply({
                 embeds: [
@@ -83,7 +83,13 @@
                 ]
             });
         }
-        const options = {};
+        const options: {
+            role?: string;
+            ping?: string;
+            channel?: string;
+            message?: string;
+        } = {};
+
         if (role) options.role = renderRole(role);
         if (ping) options.ping = renderRole(ping);
         if (channel) options.channel = renderChannel(channel);
@@ -98,13 +104,25 @@
         if (confirmation.cancelled) return;
         if (confirmation.success) {
             try {
-                const toChange = {};
+                const toChange: {
+                    "welcome.role"?: string;
+                    "welcome.ping"?: string;
+                    "welcome.channel"?: string;
+                    "welcome.message"?: string;
+                } = {};
                 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);
-                const list = {
+                await client.database.guilds.write(interaction.guild!.id, toChange);
+                const list: {
+                    memberId: ReturnType<typeof entry>;
+                    changedBy: ReturnType<typeof entry>;
+                    role?: ReturnType<typeof entry>;
+                    ping?: ReturnType<typeof entry>;
+                    channel?: ReturnType<typeof entry>;
+                    message?: ReturnType<typeof entry>;
+                } = {
                     memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
                     changedBy: entry(interaction.user.id, renderUser(interaction.user))
                 };
@@ -123,7 +141,7 @@
                     },
                     list: list,
                     hidden: {
-                        guild: interaction.guild.id
+                        guild: interaction.guild!.id
                     }
                 };
                 log(data);
@@ -155,7 +173,7 @@
     }
     let lastClicked = null;
     while (true) {
-        const config = await client.database.guilds.read(interaction.guild.id);
+        const config = await client.database.guilds.read(interaction.guild!.id);
         m = (await interaction.editReply({
             embeds: [
                 new EmojiEmbed()
@@ -164,19 +182,19 @@
                         `**Message:** ${config.welcome.message ? `\n> ${config.welcome.message}` : "*None set*"}\n` +
                             `**Role:** ${
                                 config.welcome.role
-                                    ? renderRole(await interaction.guild.roles.fetch(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))
+                                    ? 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))
+                                        : renderChannel(await interaction.guild!.channels.fetch(config.welcome.channel))
                                     : "*None set*"
                             }`
                     )
@@ -226,7 +244,7 @@
         i.deferUpdate();
         if (i.customId == "clear-message") {
             if (lastClicked == "clear-message") {
-                await client.database.guilds.write(interaction.guild.id, {
+                await client.database.guilds.write(interaction.guild!.id, {
                     "welcome.message": null
                 });
                 lastClicked = null;
@@ -235,7 +253,7 @@
             }
         } else if (i.customId == "clear-role") {
             if (lastClicked == "clear-role") {
-                await client.database.guilds.write(interaction.guild.id, {
+                await client.database.guilds.write(interaction.guild!.id, {
                     "welcome.role": null
                 });
                 lastClicked = null;
@@ -244,7 +262,7 @@
             }
         } else if (i.customId == "clear-ping") {
             if (lastClicked == "clear-ping") {
-                await client.database.guilds.write(interaction.guild.id, {
+                await client.database.guilds.write(interaction.guild!.id, {
                     "welcome.ping": null
                 });
                 lastClicked = null;
@@ -253,7 +271,7 @@
             }
         } else if (i.customId == "clear-channel") {
             if (lastClicked == "clear-channel") {
-                await client.database.guilds.write(interaction.guild.id, {
+                await client.database.guilds.write(interaction.guild!.id, {
                     "welcome.channel": null
                 });
                 lastClicked = null;
@@ -261,14 +279,14 @@
                 lastClicked = "clear-channel";
             }
         } else if (i.customId == "set-channel-dm") {
-            await client.database.guilds.write(interaction.guild.id, {
+            await client.database.guilds.write(interaction.guild!.id, {
                 "welcome.channel": "dm"
             });
             lastClicked = null;
         }
     }
     await interaction.editReply({
-        embeds: [m.embeds[0].setFooter({ text: "Message closed" })],
+        embeds: [m.embeds[0]!.setFooter({ text: "Message closed" })],
         components: []
     });
 };
diff --git a/src/events/channelCreate.ts b/src/events/channelCreate.ts
index 19f5c9b..2aa8ec7 100644
--- a/src/events/channelCreate.ts
+++ b/src/events/channelCreate.ts
@@ -1,9 +1,13 @@
+import type { GuildAuditLogsEntry } from "discord.js";
+import type { GuildBasedChannel } from "discord.js";
+// @ts-expect-error
+import { HaikuClient } from "jshaiku";
 export const event = "channelCreate";
 
-export async function callback(client, channel) {
-    const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = channel.client.logger;
+export async function callback(client: HaikuClient, channel: GuildBasedChannel) {
+    const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = client.logger;
     const auditLog = await getAuditLog(channel.guild, "CHANNEL_CREATE");
-    const audit = auditLog.entries.filter((entry) => entry.target.id === channel.id).first();
+    const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === channel.id).first();
     if (audit.executor.id === client.user.id) return;
     let emoji;
     let readableType;
@@ -27,7 +31,7 @@
             displayName = "Voice Channel";
             break;
         }
-        case "GUILD_STAGE": {
+        case "GUILD_STAGE_VOICE": {
             emoji = "CHANNEL.VOICE.CREATE";
             readableType = "Stage";
             displayName = "Stage Channel";
diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts
index f39e7ed..d42a12a 100644
--- a/src/events/channelDelete.ts
+++ b/src/events/channelDelete.ts
@@ -1,7 +1,7 @@
 import {
     BaseGuildTextChannel,
-    BaseGuildVoiceChannel,
-    GuildChannel,
+    GuildAuditLogsEntry,
+    GuildBasedChannel,
     StageChannel,
     ThreadChannel,
     VoiceChannel
@@ -11,10 +11,12 @@
 import getEmojiByName from "../utils/getEmojiByName.js";
 
 export const event = "channelDelete";
+
+export async function callback(client: HaikuClient, channel: GuildBasedChannel) {
     const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser } = client.logger;
 
     const auditLog = await getAuditLog(channel.guild, "CHANNEL_DELETE");
-    const audit = auditLog.entries.filter((entry) => entry.target.id === channel.id).first();
+    const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === channel.id).first();
     if (audit.executor.id === client.user.id) return;
 
     let emoji;
@@ -46,15 +48,15 @@
         }
     }
     const list: {
-        channelId: string;
-        name: string;
-        topic?: string | null;
-        type: any;
-        category: any;
-        nsfw?: boolean | null;
-        created: any;
-        deleted: any;
-        deletedBy: any;
+        channelId: { value: string; displayValue: string };
+        name: { value: string; displayValue: string };
+        topic?: { value: string; displayValue: string } | null;
+        type: { value: string; displayValue: string };
+        category: { value: string; displayValue: string };
+        nsfw?: { value: string; displayValue: string } | null;
+        created: { value: string; displayValue: string };
+        deleted: { value: string; displayValue: string };
+        deletedBy: { value: string; displayValue: string };
     } = {
         channelId: entry(channel.id, `\`${channel.id}\``),
         name: entry(channel.id, `${channel.name}`),
diff --git a/src/premium/createTranscript.ts b/src/premium/createTranscript.ts
index 5fbb8ad..50bcbcf 100644
--- a/src/premium/createTranscript.ts
+++ b/src/premium/createTranscript.ts
@@ -4,7 +4,6 @@
     Message,
     MessageActionRow,
     MessageButton,
-    PartialGroupDMChannel,
     TextChannel
 } from "discord.js";
 import EmojiEmbed from "../utils/generateEmojiEmbed.js";
@@ -17,7 +16,7 @@
 
 export default async function (interaction: CommandInteraction) {
     if (interaction.channel === null) return;
-    if (interaction.channel instanceof DMChannel) return;
+    if (!(interaction.channel instanceof TextChannel)) return;
     const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger;
 
     let messages: Message[] = [];
@@ -44,18 +43,19 @@
             out += "\n\n";
         }
     });
-    const member = interaction.channel.guild.members.cache.get(interaction.channel.topic.split(" ")[0]);
+    const member = interaction.guild!.members.cache.get(interaction.channel.topic.split(" ")[0]);
     let m;
     if (out !== "") {
         const url = await pbClient.createPaste({
             code: out,
             expireDate: ExpireDate.Never,
-            name: `Ticket Transcript for ${member.user.username}#${member.user.discriminator} (Created at ${new Date(
+            name: `Ticket Transcript ${member ? ("for " + member.user.username + "#" + member.user.discriminator + " ") : ""}` +
+                `(Created at ${new Date(
                 interaction.channel.createdTimestamp
             ).toDateString()})`,
             publicity: Publicity.Unlisted
         });
-        const guildConfig = await client.database.guilds.read(interaction.guild.id);
+        const guildConfig = await client.database.guilds.read(interaction.guild!.id);
         m = await interaction.reply({
             embeds: [
                 new EmojiEmbed()
@@ -121,15 +121,15 @@
             timestamp: new Date().getTime()
         },
         list: {
-            ticketFor: entry(
-                interaction.channel.topic.split(" ")[0],
-                renderUser((await interaction.guild.members.fetch(interaction.channel.topic.split(" ")[0])).user)
-            ),
-            deletedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)),
+            ticketFor: member ? entry(
+                member.id,
+                renderUser(member.user)
+            ) : entry(null, "*Unknown*"),
+            deletedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user)),
             deleted: entry(new Date().getTime(), renderDelta(new Date().getTime()))
         },
         hidden: {
-            guild: interaction.guild.id
+            guild: interaction.guild!.id
         }
     };
     log(data);
diff --git a/src/utils/log.ts b/src/utils/log.ts
index fdf17ef..cd2f3c7 100644
--- a/src/utils/log.ts
+++ b/src/utils/log.ts
@@ -24,7 +24,7 @@
         const delta = num2 - num1;
         return `${num1} -> ${num2} (${delta > 0 ? "+" : ""}${delta})`;
     }
-    entry(value: string, displayValue: string) {
+    entry(value: string, displayValue: string): { value: string; displayValue: string } {
         return { value: value, displayValue: displayValue };
     }
     renderChannel(channel: Discord.GuildChannel | Discord.ThreadChannel) {