yees

Co-authored-by: PineappleFan <PineaFan@users.noreply.github.com>
Co-authored-by: Skyler <skyler3665@gmail.com>
diff --git a/src/actions/tickets/delete.ts b/src/actions/tickets/delete.ts
index 43e9dd2..a1b4b1a 100644
--- a/src/actions/tickets/delete.ts
+++ b/src/actions/tickets/delete.ts
@@ -9,7 +9,6 @@
     if (!interaction.guild) return;
     const config = await client.database.guilds.read(interaction.guild.id);
     const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger;
-    //FIXME
     const ticketChannel = config.tickets.category;
     if (!("parent" in interaction.channel!)) {
         return await interaction.reply({
@@ -84,7 +83,8 @@
             embeds: [
                 new EmojiEmbed()
                     .setTitle("Archived Ticket")
-                    .setDescription(`This ticket has been Archived. Type ${getCommandMentionByName("ticket/close")} to delete it.` +
+                    .setDescription(`This ticket has been Archived. Type ${getCommandMentionByName("ticket/close")} to delete it.\n` +
+                        "Creating a transcript will delete all messages in this ticket" +
                         await client.database.premium.hasPremium(interaction.guild.id) ?
                         `\n\nFor more info on transcripts, check ${getCommandMentionByName("privacy")}` :
                         "")
diff --git a/src/commands/nucleus/premium.ts b/src/commands/nucleus/premium.ts
index ed4f225..b31accb 100644
--- a/src/commands/nucleus/premium.ts
+++ b/src/commands/nucleus/premium.ts
@@ -1,4 +1,4 @@
-import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, CommandInteraction, GuildMember, StringSelectMenuBuilder } from "discord.js";
+import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, CommandInteraction, StringSelectMenuBuilder } from "discord.js";
 import type { SlashCommandSubcommandBuilder } from "discord.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import client from "../../utils/client.js";
@@ -10,7 +10,7 @@
     builder.setName("premium").setDescription("Information about Nucleus Premium");
 //TODO: Allow User to remove Premium
 
-const dmcallback = async (interaction: CommandInteraction, member: GuildMember, dbUser: PremiumSchema | null, firstDescription: string): Promise<void> => {
+const dmcallback = async (interaction: CommandInteraction, dbUser: PremiumSchema | null, firstDescription: string): Promise<void> => {
 
     if(!dbUser) {
         await interaction.editReply({embeds: [
@@ -103,7 +103,7 @@
         premium = `**You can't give servers premium anymore because your subscription ended or was cancelled.** To get premium again please subscribe in the Clicks server`
         count = 0;
     }
-    if(!interaction.guild) return await dmcallback(interaction, member, dbMember, firstDescription);
+    if(!interaction.guild) return await dmcallback(interaction, dbMember, firstDescription);
     const hasPremium = await client.database.premium.hasPremium(interaction.guild!.id);
     let premiumGuild = ""
     if (hasPremium) {
diff --git a/src/premium/createTranscript.ts b/src/premium/createTranscript.ts
index acd641b..20b790a 100644
--- a/src/premium/createTranscript.ts
+++ b/src/premium/createTranscript.ts
@@ -8,7 +8,8 @@
     TextChannel,
     ButtonStyle,
     User,
-    ComponentType
+    ComponentType,
+    ThreadChannel
 } from "discord.js";
 import EmojiEmbed from "../utils/generateEmojiEmbed.js";
 import getEmojiByName from "../utils/getEmojiByName.js";
@@ -78,14 +79,21 @@
     type: "ticket" | "purge"
     guild: string;
     channel: string;
+    for: TranscriptAuthor
     messages: TranscriptMessage[];
     createdTimestamp: number;
     createdBy: TranscriptAuthor;
 }
 
+const noTopic = new EmojiEmbed()
+    .setTitle("User not found")
+    .setDescription("There is no user associated with this ticket.")
+    .setStatus("Danger")
+    .setEmoji("CONTROL.BLOCKCROSS")
+
 export default async function (interaction: CommandInteraction | MessageComponentInteraction) {
     if (interaction.channel === null) return;
-    if (!(interaction.channel instanceof TextChannel)) return;
+    if (!(interaction.channel instanceof TextChannel || interaction.channel instanceof ThreadChannel)) return;
     const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger;
 
     let messages: Message[] = [];
@@ -98,6 +106,13 @@
         messages = messages.concat(Array.from(deleted.values() as Iterable<Message>));
         if (messages.length === 1) messageException(interaction.guild!.id, interaction.channel.id, messages[0]!.id)
     } while (deletedCount === 100);
+    messages = messages.filter(message => !(
+        message.components.some(
+            component => component.components.some(
+                child => child.customId?.includes("transcript") ?? false
+            )
+        )
+    ));
 
     let out = "";
     messages.reverse().forEach((message) => {
@@ -116,8 +131,30 @@
 
     const interactionMember = await interaction.guild?.members.fetch(interaction.user.id)
 
+    let topic
+    let member: GuildMember | null = null;
+    if (interaction.channel instanceof TextChannel) {
+        topic = interaction.channel.topic;
+        if (topic === null) return await interaction.reply({ embeds: [noTopic] });
+        member = interaction.guild!.members.cache.get(topic.split(" ")[1]!) ?? null;
+    } else {
+        topic = interaction.channel.name;
+        const split = topic.split("-").map(p => p.trim()) as [string, string, string];
+        member = interaction.guild!.members.cache.get(split[1]) ?? null;
+        if (member === null) return await interaction.reply({ embeds: [noTopic] });
+    }
+
+
     const newOut: Transcript = {
         type: "ticket",
+        for: {
+            username: member!.user.username,
+            discriminator: parseInt(member!.user.discriminator),
+            id: member!.user.id,
+            topRole: {
+                color: member!.roles.highest.color
+            }
+        },
         guild: interaction.guild!.id,
         channel: interaction.channel!.id,
         messages: [],
@@ -184,14 +221,8 @@
         newOut.messages.push(msg);
     });
 
-    console.log(newOut);
-
-    const topic = interaction.channel.topic;
-    let member: GuildMember | null = null;
-    if (topic !== null) {
-        const part = topic.split(" ")[0] ?? null;
-        if (part !== null) member = interaction.guild!.members.cache.get(part) ?? null;
-    }
+    const code = await client.database.transcripts.create(newOut);
+    if(!code) return await interaction.reply({embeds: [new EmojiEmbed().setTitle("Error").setDescription("An error occurred while creating the transcript.").setStatus("Danger").setEmoji("CONTROL.BLOCKCROSS")]})
     let m: Message;
     if (out !== "") {
         const url = await pbClient.createPaste({
@@ -200,7 +231,7 @@
             name:
                 `Ticket Transcript ${
                     member ? "for " + member.user.username + "#" + member.user.discriminator + " " : ""
-                }` + `(Created at ${new Date(interaction.channel.createdTimestamp).toDateString()})`,
+                }` + `(Created at ${new Date(interaction.channel.createdTimestamp!).toDateString()})`,
             publicity: Publicity.Unlisted
         });
         const guildConfig = await client.database.guilds.read(interaction.guild!.id);
diff --git a/src/utils/client.ts b/src/utils/client.ts
index 857fb1d..00f8c05 100644
--- a/src/utils/client.ts
+++ b/src/utils/client.ts
@@ -2,7 +2,7 @@
 import { Logger } from "../utils/log.js";
 import Memory from "../utils/memory.js";
 import type { VerifySchema } from "../reflex/verify.js";
-import { Guilds, History, ModNotes, Premium, PerformanceTest, ScanCache } from "../utils/database.js";
+import { Guilds, History, ModNotes, Premium, PerformanceTest, ScanCache, Transcript } from "../utils/database.js";
 import EventScheduler from "../utils/eventScheduler.js";
 import type { RoleMenuSchema } from "../actions/roleMenu.js";
 import config from "../config/main.js";
@@ -23,6 +23,7 @@
         eventScheduler: EventScheduler;
         performanceTest: PerformanceTest;
         scanCache: ScanCache;
+        transcripts: Transcript
     };
     preloadPage: Record<string, {command: string, argument: string}> = {};  // e.g. { channelID: { command: privacy, page: 3}}
     commands: Record<string, [{
@@ -46,7 +47,8 @@
     premium: new Premium(),
     eventScheduler: new EventScheduler(),
     performanceTest: new PerformanceTest(),
-    scanCache: new ScanCache()
+    scanCache: new ScanCache(),
+    transcripts: new Transcript()
 });
 
 export default client;
diff --git a/src/utils/database.ts b/src/utils/database.ts
index 48d0077..7e80f96 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -1,4 +1,4 @@
-import type { GuildMember } from "discord.js";
+import type { ButtonStyle, GuildMember } from "discord.js";
 import type Discord from "discord.js";
 import { Collection, MongoClient } from "mongodb";
 import config from "../config/main.js";
@@ -101,6 +101,96 @@
     }
 }
 
+interface TranscriptEmbed {
+    title?: string;
+    description?: string;
+    fields?: {
+        name: string;
+        value: string;
+        inline: boolean;
+    }[];
+    footer?: {
+        text: string;
+        iconURL?: string;
+    };
+}
+
+interface TranscriptComponent {
+    type: number;
+    style?: ButtonStyle;
+    label?: string;
+    description?: string;
+    placeholder?: string;
+    emojiURL?: string;
+}
+
+interface TranscriptAuthor {
+    username: string;
+    discriminator: number;
+    nickname?: string;
+    id: string;
+    iconURL?: string;
+    topRole: {
+        color: number;
+        badgeURL?: string;
+    }
+}
+
+interface TranscriptAttachment {
+    url: string;
+    filename: string;
+    size: number;
+    log?: string;
+}
+
+interface TranscriptMessage {
+    id: string;
+    author: TranscriptAuthor;
+    content?: string;
+    embeds?: TranscriptEmbed[];
+    components?: TranscriptComponent[][];
+    editedTimestamp?: number;
+    createdTimestamp: number;
+    flags?: string[];
+    attachments?: TranscriptAttachment[];
+    stickerURLs?: string[];
+    referencedMessage?: string | [string, string, string];
+}
+
+interface TranscriptSchema {
+    code: string;
+    for: TranscriptAuthor;
+    type: "ticket" | "purge"
+    guild: string;
+    channel: string;
+    messages: TranscriptMessage[];
+    createdTimestamp: number;
+    createdBy: TranscriptAuthor;
+}
+
+export class Transcript {
+    transcripts: Collection<TranscriptSchema>;
+
+    constructor() {
+        this.transcripts = database.collection<TranscriptSchema>("transcripts");
+    }
+
+    async create(transcript: Omit<TranscriptSchema, "code">) {
+        let code;
+        do {
+            code = Math.random().toString(36).substring(2, 16) + Math.random().toString(36).substring(2, 16);
+        } while (await this.transcripts.findOne({ code: code }));
+
+        const doc = await this.transcripts.insertOne(Object.assign(transcript, { code: code }));
+        if(doc.acknowledged) return code;
+        else return null;
+    }
+
+    async read(code: string) {
+        return await this.transcripts.findOne({ code: code });
+    }
+}
+
 export class History {
     histories: Collection<HistorySchema>;