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/commands/nucleus/_meta.ts b/src/commands/nucleus/_meta.ts
index 521b338..bd7fd14 100644
--- a/src/commands/nucleus/_meta.ts
+++ b/src/commands/nucleus/_meta.ts
@@ -3,8 +3,6 @@
 const name = "nucleus";
 const description = "Commands relating to Nucleus itself";
 
-const subcommand = await command(name, description, `nucleus`)
+const subcommand = await command(name, description, `nucleus`, undefined, undefined, undefined, undefined, true);
 
-const allowedInDMs = true;
-
-export { name, description, subcommand as command, allowedInDMs };
+export { name, description, subcommand as command };
diff --git a/src/commands/nucleus/guide.ts b/src/commands/nucleus/guide.ts
index d3370ba..270ee62 100644
--- a/src/commands/nucleus/guide.ts
+++ b/src/commands/nucleus/guide.ts
@@ -1,5 +1,5 @@
 import type { CommandInteraction } from 'discord.js';
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
 import guide from "../../reflex/guide.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
@@ -9,10 +9,5 @@
     guide(interaction.guild!, interaction);
 };
 
-const check = () => {
-    return true;
-};
-
 export { command };
 export { callback };
-export { check };
diff --git a/src/commands/nucleus/invite.ts b/src/commands/nucleus/invite.ts
index fd65e51..b89425a 100644
--- a/src/commands/nucleus/invite.ts
+++ b/src/commands/nucleus/invite.ts
@@ -1,5 +1,5 @@
 import { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import client from "../../utils/client.js";
 
@@ -29,10 +29,5 @@
     });
 };
 
-const check = () => {
-    return true;
-};
-
 export { command };
 export { callback };
-export { check };
diff --git a/src/commands/nucleus/ping.ts b/src/commands/nucleus/ping.ts
index 12f1c6b..3e02a8f 100644
--- a/src/commands/nucleus/ping.ts
+++ b/src/commands/nucleus/ping.ts
@@ -1,6 +1,6 @@
 import { LoadingEmbed } from "../../utils/defaults.js";
 import type { CommandInteraction } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import client from "../../utils/client.js";
 
@@ -10,9 +10,9 @@
 const callback = async (interaction: CommandInteraction): Promise<void> => {
     // WEBSOCKET | Nucleus -> Discord
     // EDITING   | Nucleus -> discord -> nucleus | edit time / 2
-    const initial = new Date().getTime();
+    const initial = Date.now();
     await interaction.reply({ embeds: LoadingEmbed, ephemeral: true });
-    const ping = new Date().getTime() - initial;
+    const ping = Date.now() - initial;
     interaction.editReply({
         embeds: [
             new EmojiEmbed()
@@ -28,10 +28,5 @@
     });
 };
 
-const check = () => {
-    return true;
-};
-
 export { command };
 export { callback };
-export { check };
diff --git a/src/commands/nucleus/premium.ts b/src/commands/nucleus/premium.ts
index 745f167..c431c8e 100644
--- a/src/commands/nucleus/premium.ts
+++ b/src/commands/nucleus/premium.ts
@@ -1,32 +1,211 @@
-import type { CommandInteraction } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, CommandInteraction, ComponentType, Message, StringSelectMenuBuilder, StringSelectMenuInteraction } from "discord.js";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import client from "../../utils/client.js";
+import { LoadingEmbed } from "../../utils/defaults.js";
+import getEmojiByName from "../../utils/getEmojiByName.js";
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder.setName("premium").setDescription("Information about Nucleus Premium");
+//TODO: Allow User to remove Premium
+
+const dmcallback = async (interaction: CommandInteraction, firstDescription: string, msg: Message): Promise<void> => {
+    let closed = false;
+    do {
+        const dbUser = await client.database.premium.fetchUser(interaction.user.id);
+        if(!dbUser) {
+            await interaction.editReply({embeds: [
+                new EmojiEmbed()
+                    .setTitle("Premium")
+                    .setDescription(`*You do not have premium! You can't activate premium on any servers.*` + firstDescription)
+                    .setEmoji("NUCLEUS.LOGO")
+                    .setStatus("Danger")
+            ]});
+            return;
+        }
+        const premiumGuilds = dbUser.appliesTo.map((guildID) => {
+            const guild = client.guilds.cache.get(guildID);
+            if(!guild) return undefined;
+            return guild.name;
+        });
+
+        const options = premiumGuilds.filter((guild) => guild !== undefined) as string[];
+
+        const removeRow = new ActionRowBuilder<StringSelectMenuBuilder>()
+            .addComponents(
+                new StringSelectMenuBuilder()
+                    .setCustomId("currentPremium")
+                    .setPlaceholder("Select a server to remove premium from")
+                    .setDisabled(premiumGuilds.length === 0)
+                    .addOptions(options.slice(0, Math.min(options.length, 24)).map((guild) => {
+                        return {label: guild, value: guild}
+                    }))
+            );
+        const cancel = new ActionRowBuilder<ButtonBuilder>()
+            .addComponents(
+                new ButtonBuilder()
+                    .setCustomId("cancel")
+                    .setLabel("Close")
+                    .setStyle(ButtonStyle.Danger)
+            );
+
+        const components: ActionRowBuilder<StringSelectMenuBuilder | ButtonBuilder>[] = [cancel];
+        if(options.length > 0) components.unshift(removeRow);
+        await interaction.editReply(
+        {
+            embeds: [
+                new EmojiEmbed()
+                    .setTitle("Premium")
+                    .setDescription(
+                        `*You have premium on the following servers:*\n\n` +
+                        (options.length > 0 ? options.join(', ') : `You have not activated premium in any guilds`) +
+                        firstDescription)
+                    .setEmoji("NUCLEUS.LOGO")
+                    .setStatus("Success")
+            ],
+            components: components
+        });
+
+        let i: StringSelectMenuInteraction | ButtonInteraction;
+        try {
+            const filter = (i: StringSelectMenuInteraction | ButtonInteraction) => i.user.id === interaction.user.id;
+            i = await msg.awaitMessageComponent<ComponentType.StringSelect | ComponentType.Button>({time: 300000, filter})
+        } catch (e) {
+            await interaction.deleteReply();
+            closed = true;
+            break;
+        }
+        await i.deferUpdate();
+        if(i.isButton()) {
+            closed = true;
+        } else {
+            const response = client.database.premium.removePremium(interaction.user.id, i.values[0]!);
+            console.log(response)
+        }
+    } while (!closed);
+    await interaction.deleteReply();
+}
 
 const callback = async (interaction: CommandInteraction): Promise<void> => {
-    interaction.reply({
+    if (interaction.guild) client.database.premium.hasPremium(interaction.guild.id).finally(() => {});
+    const m = await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true})
+    const member = await (await interaction.client.guilds.fetch("684492926528651336")).members.fetch(interaction.user.id).catch(() => {
+        interaction.editReply({ embeds: [
+            new EmojiEmbed()
+                .setTitle("Premium")
+                .setDescription(`*You are not currently in the Clicks Server. To gain access to premium please join.*` + firstDescription)
+                .setEmoji("NUCLEUS.LOGO")
+                .setStatus("Danger")
+        ], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder().setStyle(ButtonStyle.Link).setLabel("Join").setURL("https://discord.gg/bPaNnxe"))] });
+    })
+    if (!member) return;
+    const firstDescription = "\n\nPremium allows servers of your choice to get access to extra features for a fixed price per month.\nThis includes:\n" +
+        `${getEmojiByName("MOD.IMAGES.TOOSMALL")}  Attachment logs - Stores attachments so they can be viewed after a message is deleted.\n` +
+        `${getEmojiByName("GUILD.TICKET.ARCHIVED")}  Ticket Transcripts - Gives a link to view the history of a ticket after it has been closed.\n`
+    const dbMember = await client.database.premium.fetchUser(interaction.user.id)
+    let premium = `You do not have premium! You can't activate premium on any servers.`;
+    let count = 0;
+    const {level, appliesTo} = dbMember ?? {level: 0, appliesTo: []}
+    if (level === 99) {
+        premium = `You have Infinite Premium! You have been gifted this by the developers as a thank you. You can give premium to any and all servers you are in.`;
+        count = 200;
+    } else if (level === 1) {
+        premium = `You have Premium tier 1! You can give premium to ${1 - appliesTo.length} more server(s).`;
+        count = 1;
+    } else if (level === 2) {
+        premium = `You have Premium tier 2! You can give premium to ${3 - appliesTo.length} more server(s).`;
+        count = 3;
+    } else if (level === 3) {
+        premium = `You have Premium Mod! You can give premium to ${3 - appliesTo.length} more server(s), as well as automatically giving premium to all servers you have a "manage" permission in.`
+        count = 3;
+    }
+    if (dbMember?.expiresAt) {
+        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, firstDescription, m);
+    const hasPremium = await client.database.premium.hasPremium(interaction.guild!.id);
+    let premiumGuild = ""
+    if (hasPremium) {
+        premiumGuild = `**This server has premium! It was ${hasPremium[2] === 3 && hasPremium[3] ? `automatically applied by <@${hasPremium[1]}>` : `given by <@${hasPremium[1]}>`}**\n\n`
+    }
+
+    const components: ActionRowBuilder<ButtonBuilder>[] = []
+    if (level === 0 || dbMember?.expiresAt) {
+        components.push(
+            new ActionRowBuilder<ButtonBuilder>()
+                .addComponents(
+                    new ButtonBuilder()
+                    .setStyle(ButtonStyle.Link)
+                    .setLabel("Join Clicks")
+                    .setURL("https://discord.gg/bPaNnxe")
+                )
+        )
+    } else {
+        components.push(
+            new ActionRowBuilder<ButtonBuilder>()
+                .addComponents(
+                    new ButtonBuilder()
+                        .setStyle(premiumGuild.length > 0 ? ButtonStyle.Secondary : ButtonStyle.Success)
+                        .setLabel(premiumGuild.length > 0 ? "This server has premium" : "Activate premium here")
+                        .setCustomId("premiumActivate")
+                        .setDisabled(count <= 0 || (hasPremium ? hasPremium[0] : false))
+                )
+        )
+    }
+
+    interaction.editReply({
         embeds: [
             new EmojiEmbed()
                 .setTitle("Premium")
                 .setDescription(
-                    "*Nucleus Premium is currently not available.*\n\n" +
-                        "Premium allows your server to get access to extra features, for a fixed price per month.\nThis includes:\n" +
-                        "- Attachment logs - Stores attachments so they can be viewed after a message is deleted.\n" +
-                        "- Ticket Transcripts - Gives a link to view the history of a ticket after it has been closed.\n"
+                    premiumGuild + premium + firstDescription
                 )
                 .setEmoji("NUCLEUS.LOGO")
                 .setStatus("Danger")
+                .setImage("https://assets.clicks.codes/ads/ads/nucleus-premium.png")
         ],
-        ephemeral: true
+        components: components
     });
-};
 
-const check = () => {
-    return true;
+    const filter = (i: ButtonInteraction) => i.customId === "premiumActivate" && i.user.id === interaction.user.id;
+    let i;
+    try {
+        i = await interaction.channel!.awaitMessageComponent<2>({ filter, time: 60000 });
+    } catch (e) {
+        return;
+    }
+    i.deferUpdate();
+    const guild = i.guild!;
+    if (count - appliesTo.length <= 0) {
+        interaction.editReply({
+            embeds: [
+                new EmojiEmbed()
+                    .setTitle("Premium")
+                    .setDescription(
+                        `You have already activated premium on the maximum amount of servers!` + firstDescription
+                    )
+                    .setEmoji("NUCLEUS.PREMIUMACTIVATE")
+                    .setStatus("Danger")
+            ],
+            components: []
+        });
+    } else {
+        await client.database.premium.addPremium(interaction.user.id, guild.id);
+        interaction.editReply({
+            embeds: [
+                new EmojiEmbed()
+                    .setTitle("Premium")
+                    .setDescription(
+                        `You have activated premium on this server!` + firstDescription
+                    )
+                    .setEmoji("NUCLEUS.LOGO")
+                    .setStatus("Danger")
+            ],
+            components: []
+        });
+    }
 };
 
 export { command };
 export { callback };
-export { check };
diff --git a/src/commands/nucleus/stats.ts b/src/commands/nucleus/stats.ts
index d8b2807..19c0949 100644
--- a/src/commands/nucleus/stats.ts
+++ b/src/commands/nucleus/stats.ts
@@ -1,5 +1,5 @@
 import type { CommandInteraction } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import client from "../../utils/client.js";
 
@@ -13,16 +13,11 @@
                 .setTitle("Stats")
                 .setDescription(`**Servers:** ${client.guilds.cache.size}\n` + `**Ping:** \`${client.ws.ping * 2}ms\``)
                 .setStatus("Success")
-                .setEmoji("GUILD.GRAPHS")
+                .setEmoji("SETTINGS.STATS.GREEN")
         ],
         ephemeral: true
     });
 };
 
-const check = () => {
-    return true;
-};
-
 export { command };
 export { callback };
-export { check };
diff --git a/src/commands/nucleus/suggest.ts b/src/commands/nucleus/suggest.ts
index de0e69b..6ba3445 100644
--- a/src/commands/nucleus/suggest.ts
+++ b/src/commands/nucleus/suggest.ts
@@ -1,7 +1,7 @@
 import { LoadingEmbed } from '../../utils/defaults.js';
 import { ButtonStyle, CommandInteraction } from "discord.js";
 import Discord from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
 import confirmationMessage from "../../utils/confirmationMessage.js";
 import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
 import client from "../../utils/client.js";
@@ -66,10 +66,5 @@
     });
 };
 
-const check = () => {
-    return true;
-};
-
 export { command };
 export { callback };
-export { check };