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/premium/attachmentLogs.ts b/src/premium/attachmentLogs.ts
index 156503a..b2c8391 100644
--- a/src/premium/attachmentLogs.ts
+++ b/src/premium/attachmentLogs.ts
@@ -1,4 +1,4 @@
-import { getCommandMentionByName } from './../utils/getCommandMentionByName.js';
+import { getCommandMentionByName } from './../utils/getCommandDataByName.js';
 import client from "../utils/client.js";
 import keyValueList from "../utils/generateKeyValueList.js";
 import singleNotify from "../utils/singleNotify.js";
@@ -8,12 +8,13 @@
 import type { GuildTextBasedChannel, Message } from "discord.js";
 
 export default async function logAttachment(message: Message): Promise<AttachmentLogSchema> {
+    if (message.guild) client.database.premium.hasPremium(message.guild.id).finally(() => {});
     if (!message.guild) throw new Error("Tried to log an attachment in a non-guild message");
     const { renderUser, renderChannel, renderDelta } = client.logger;
     const attachments = [];
     for (const attachment of message.attachments.values()) {
         attachments.push({
-            local: await saveAttachment(attachment.url),
+            local: (await saveAttachment(attachment.url))[0],
             url: attachment.url,
             height: attachment.height,
             width: attachment.width,
@@ -24,7 +25,7 @@
     for (const link of links) {
         if (link.toLowerCase().match(/\.(jpg|jpeg|png|gif|gifv|webm|webp|mp4|wav|mp3|ogg)$/gi)) {
             attachments.push({
-                local: await saveAttachment(link),
+                local: (await saveAttachment(link))[0],
                 url: link,
                 height: null,
                 width: null
@@ -38,17 +39,17 @@
             singleNotify(
                 "noAttachmentLogChannel",
                 message.guild.id,
-                `No channel set for attachment logging. You can set one with ${await getCommandMentionByName("settings/logs/attachments")}`,
+                `No channel set for attachment logging. You can set one with ${getCommandMentionByName("settings/logs/attachments")}`,
                 "Warning"
             );
             return { files: attachments };
         }
-        const channelObj = await client.channels.fetch(channel);
+        const channelObj = await message.guild.channels.fetch(channel);
         if (!channelObj) {
             singleNotify(
                 "attachmentLogChannelDeleted",
                 message.guild.id,
-                `Your attachment history channel was deleted or is not longer accessible. You can set a new one with ${await getCommandMentionByName("settings/logs/attachments")}`,
+                `Your attachment history channel was deleted or is not longer accessible. You can set a new one with ${getCommandMentionByName("settings/logs/attachments")}`,
                 "Warning"
             );
             return { files: attachments };
@@ -70,7 +71,6 @@
             ],
             files: attachments.map((file) => file.local)
         });
-        // await client.database.guilds.write(interaction.guild.id, {[`tags.${name}`]: value});
         client.database.guilds.write(message.guild.id, {
             [`logging.attachments.saved.${message.channel.id}${message.id}`]: m.url
         });
diff --git a/src/premium/createTranscript.ts b/src/premium/createTranscript.ts
index 04fdc08..67aed04 100644
--- a/src/premium/createTranscript.ts
+++ b/src/premium/createTranscript.ts
@@ -7,18 +7,23 @@
     MessageComponentInteraction,
     TextChannel,
     ButtonStyle,
-    User
+    User,
+    ThreadChannel
 } from "discord.js";
 import EmojiEmbed from "../utils/generateEmojiEmbed.js";
 import getEmojiByName from "../utils/getEmojiByName.js";
-import { PasteClient, Publicity, ExpireDate } from "pastebin-api";
 import client from "../utils/client.js";
+import { messageException } from '../utils/createTemporaryStorage.js';
 
-const pbClient = new PasteClient(client.config.pastebinApiKey);
+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[] = [];
@@ -29,95 +34,75 @@
         const deleted = await (interaction.channel as TextChannel).bulkDelete(fetched, true);
         deletedCount = deleted.size;
         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) => {
-        if (!message.author.bot) {
-            const sentDate = new Date(message.createdTimestamp);
-            out += `${message.author.username}#${message.author.discriminator} (${
-                message.author.id
-            }) [${sentDate.toUTCString()}]\n`;
-            const lines = message.content.split("\n");
-            lines.forEach((line) => {
-                out += `> ${line}\n`;
-            });
-            out += "\n\n";
-        }
-    });
-    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;
-    }
-    let m: Message;
-    if (out !== "") {
-        const url = await pbClient.createPaste({
-            code: out,
-            expireDate: ExpireDate.Never,
-            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);
-        m = (await interaction.reply({
-            embeds: [
-                new EmojiEmbed()
-                    .setTitle("Transcript")
-                    .setDescription(
-                        "You can view the transcript using the link below. You can save the link for later" +
-                            (guildConfig.logging.logs.channel
-                                ? ` or find it in <#${guildConfig.logging.logs.channel}> once you press delete below. After this the channel will be deleted.`
-                                : ".")
-                    )
-                    .setStatus("Success")
-                    .setEmoji("CONTROL.DOWNLOAD")
-            ],
-            components: [
-                new ActionRowBuilder<ButtonBuilder>().addComponents([
-                    new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(url),
-                    new ButtonBuilder()
-                        .setLabel("Delete")
-                        .setStyle(ButtonStyle.Danger)
-                        .setCustomId("close")
-                        .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-                ])
-            ],
-            fetchReply: true
-        })) as Message;
+    let topic
+    let member: GuildMember = interaction.guild?.members.me!;
+    if (interaction.channel instanceof TextChannel) {
+        topic = interaction.channel.topic;
+        if (topic === null) return await interaction.reply({ embeds: [noTopic] });
+        const mem = interaction.guild!.members.cache.get(topic.split(" ")[1]!);
+        if (mem) member = mem;
     } else {
-        m = (await interaction.reply({
-            embeds: [
-                new EmojiEmbed()
-                    .setTitle("Transcript")
-                    .setDescription(
-                        "The transcript was empty, so no changes were made. To delete this ticket, press the delete button below."
-                    )
-                    .setStatus("Success")
-                    .setEmoji("CONTROL.DOWNLOAD")
-            ],
-            components: [
-                new ActionRowBuilder<ButtonBuilder>().addComponents([
-                    new ButtonBuilder()
-                        .setLabel("Delete")
-                        .setStyle(ButtonStyle.Danger)
-                        .setCustomId("close")
-                        .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
-                ])
-            ],
-            fetchReply: true
-        })) as Message;
+        topic = interaction.channel.name;
+        const split = topic.split("-").map(p => p.trim()) as [string, string, string];
+        const mem = interaction.guild!.members.cache.get(split[1])
+        if (mem) member = mem;
     }
+
+    const newOut = await client.database.transcripts.createTranscript(messages, interaction, member);
+
+    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")
+        ]
+    })
+    const guildConfig = await client.database.guilds.read(interaction.guild!.id);
+    const m: Message = (await interaction.reply({
+        embeds: [
+            new EmojiEmbed()
+                .setTitle("Transcript")
+                .setDescription(
+                    "You can view the transcript using the link below. You can save the link for later" +
+                        (guildConfig.logging.logs.channel
+                            ? ` or find it in <#${guildConfig.logging.logs.channel}> once you press delete below. After this the channel will be deleted.`
+                            : ".")
+                )
+                .setStatus("Success")
+                .setEmoji("CONTROL.DOWNLOAD")
+        ],
+        components: [
+            new ActionRowBuilder<ButtonBuilder>().addComponents([
+                new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(`https://clicks.codes/nucleus/transcript/${code}`),
+                new ButtonBuilder()
+                    .setLabel("Delete")
+                    .setStyle(ButtonStyle.Danger)
+                    .setCustomId("close")
+                    .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+            ])
+        ],
+        fetchReply: true
+    })) as Message;
     let i;
     try {
         i = await m.awaitMessageComponent({
             time: 300000,
-            filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+            filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.message.id === m.id }
         });
-        i.deferUpdate();
+        await i.deferUpdate();
     } catch {
         return;
     }
@@ -128,12 +113,13 @@
             calculateType: "ticketUpdate",
             color: NucleusColors.red,
             emoji: "GUILD.TICKET.CLOSE",
-            timestamp: new Date().getTime()
+            timestamp: Date.now()
         },
         list: {
-            ticketFor: member ? entry(member.id, renderUser(member.user)) : entry(null, "*Unknown*"),
+            ticketFor: entry(member.id, renderUser(member.user)),
             deletedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as User)),
-            deleted: entry(new Date().getTime().toString(), renderDelta(new Date().getTime()))
+            deleted: entry(Date.now().toString(), renderDelta(Date.now())),
+            transcript: entry(code, `https://clicks.codes/nucleus/transcript/${code}`)
         },
         hidden: {
             guild: interaction.guild!.id