From 85cab30b5545fa590694766a236a205e5107cb81 Mon Sep 17 00:00:00 2001
From: William Harrison <87287585+wdhdev@users.noreply.github.com>
Date: Tue, 3 Dec 2024 12:24:00 +0800
Subject: [PATCH] fix formatting issues + add recurring reminders

---
 src/commands/reminders/info.ts      |  3 ++-
 src/commands/reminders/reminders.ts |  6 +++---
 src/commands/reminders/remindme.ts  | 26 +++++++++++++++++++++-----
 src/events/client/ready.ts          | 22 +++++++++++++---------
 src/models/Reminder.ts              |  7 ++++++-
 src/util/setReminder.ts             |  4 ++--
 6 files changed, 47 insertions(+), 21 deletions(-)

diff --git a/src/commands/reminders/info.ts b/src/commands/reminders/info.ts
index 37f8a2f..76ce4fd 100644
--- a/src/commands/reminders/info.ts
+++ b/src/commands/reminders/info.ts
@@ -47,7 +47,8 @@ const command: Command = {
                 .addFields(
                     { name: "Reason", value: reminder.reason },
                     { name: "Set", value: `<t:${reminder.reminder_set.toString().slice(0, -3)}:f>`, inline: true },
-                    { name: "Due", value: `<t:${(reminder.reminder_set + reminder.delay).toString().slice(0, -3)}:R>`, inline: true }
+                    { name: "Due", value: `<t:${(Number(reminder.reminder_set) + reminder.delay).toString().slice(0, -3)}:R>`, inline: true },
+                    { name: "Recurring", value: reminder?.recurring ? emoji.tick : emoji.cross, inline: true }
                 )
 
             await interaction.editReply({ embeds: [info] });
diff --git a/src/commands/reminders/reminders.ts b/src/commands/reminders/reminders.ts
index e4147bc..31c57c4 100644
--- a/src/commands/reminders/reminders.ts
+++ b/src/commands/reminders/reminders.ts
@@ -39,13 +39,13 @@ const command: Command = {
                 return;
             }
 
-            // Sort reminders by due date
-            reminders = reminders.sort((a: any, b: any) => a.due - b.due);
+            // Sort reminders by when they were set
+            reminders = reminders.sort((a: any, b: any) => Number(a.reminder_set) - Number(b.reminder_set));
 
             const list = new Discord.EmbedBuilder()
                 .setColor(client.config.embeds.default as ColorResolvable)
                 .setTitle("Your Reminders")
-                .setDescription(cap(reminders.map(r => `\`${r.reminder_id}\` (<t:${(r.reminder_set + r.delay).toString().slice(0, -3)}:R>):\n*${!fullReasons ? cap(r.reason, 100): r.reason}*`).join("\n"), 4000))
+                .setDescription(cap(reminders.map(r => `\`${r.reminder_id}\` (<t:${(Number(r.reminder_set) + r.delay).toString().slice(0, -3)}:R>):\n*${!fullReasons ? cap(r.reason, 100): r.reason}*`).join("\n"), 4000))
 
             await interaction.editReply({ embeds: [list] });
         } catch(err) {
diff --git a/src/commands/reminders/remindme.ts b/src/commands/reminders/remindme.ts
index 57323e7..d0fa4ce 100644
--- a/src/commands/reminders/remindme.ts
+++ b/src/commands/reminders/remindme.ts
@@ -23,7 +23,7 @@ const command: Command = {
             type: 3,
             name: "reason",
             description: "The reason for the reminder.",
-            max_length: 1000,
+            max_length: 512,
             required: true
         },
 
@@ -32,6 +32,13 @@ const command: Command = {
             name: "send_in_channel",
             description: "Send the reminder in this channel, instead of in a direct message.",
             required: false
+        },
+
+        {
+            type: 5,
+            name: "recurring",
+            description: "Set the reminder to automatically repeat.",
+            required: false
         }
     ],
     default_member_permissions: null,
@@ -45,6 +52,7 @@ const command: Command = {
             let time: number | string = interaction.options.get("time")?.value as string;
             const reason = interaction.options.get("reason")?.value as string;
             const sendInChannel = interaction.options.get("send_in_channel")?.value || false as boolean;
+            const recurring = interaction.options.get("recurring")?.value || false as boolean;
 
             const reminders = await Reminder.find({ user: interaction.user.id });
 
@@ -102,20 +110,28 @@ const command: Command = {
                 channel: interaction.channel?.id ? interaction.channel?.id : null,
                 delay: time,
                 reason: reason,
-                send_in_channel: sendInChannel
+                send_in_channel: sendInChannel,
+                recurring
             }).save()
 
             if(time < client.config.reminders.timeTillSet) {
                 client.reminders.set(`${interaction.user.id}-${reminder.reminder_id}`, setTimeout(async () => {
                     client.reminders.delete(`${interaction.user.id}-${reminder.reminder_id}`);
-                    await Reminder.findOneAndDelete({ reminder_id: reminder.reminder_id, user: interaction.user.id });
+
+                    if(reminder?.recurring) {
+                        reminder.reminder_set = Date.now().toString();
+                        await reminder.save();
+                    } else {
+                        await reminder.deleteOne();
+                    }
 
                     const embed = new Discord.EmbedBuilder()
                         .setColor(client.config.embeds.default as ColorResolvable)
                         .setTitle("Reminder")
                         .setDescription(reason)
                         .addFields (
-                            { name: "Set", value: `<t:${reminder.reminder_set.toString().slice(0, -3)}:f> (<t:${reminder.reminder_set.toString().slice(0, -3)}:R>)` }
+                            { name: "Set", value: `<t:${reminder.reminder_set.toString().slice(0, -3)}:f> (<t:${reminder.reminder_set.toString().slice(0, -3)}:R>)` },
+                            { name: "Recurring", value: reminder?.recurring ? emoji.tick : emoji.cross }
                         )
                         .setFooter({ text: `ID: ${reminder.reminder_id}` })
                         .setTimestamp()
@@ -149,7 +165,7 @@ const command: Command = {
 
             const reminderSet = new Discord.EmbedBuilder()
                 .setColor(client.config.embeds.default as ColorResolvable)
-                .setDescription(`${emoji.tick} Your reminder has been set for <t:${(reminder.reminder_set + reminder.delay).toString().slice(0, -3)}:f> with ID \`${reminder.reminder_id}\`!`)
+                .setDescription(`${emoji.tick} Your reminder has been set for <t:${(Number(reminder.reminder_set) + reminder.delay).toString().slice(0, -3)}:f> with ID \`${reminder.reminder_id}\`${reminder?.recurring ? " and will recur until cancelled." : "."}`)
 
             await interaction.editReply({ embeds: [reminderSet] });
         } catch(err) {
diff --git a/src/events/client/ready.ts b/src/events/client/ready.ts
index c30340d..58d3c7b 100644
--- a/src/events/client/ready.ts
+++ b/src/events/client/ready.ts
@@ -19,9 +19,9 @@ const event: Event = {
             await globalCommands(client);
 
             // Manage timeouts
-            async function manageExistingTimeouts() {
+            async function manageExistingReminders() {
                 let reminders = await Reminder.find({});
-                const dueReminders = reminders.filter(reminder => (reminder.reminder_set + reminder.delay) <= Date.now().toString());
+                const dueReminders = reminders.filter(reminder => (Number(reminder.reminder_set) + reminder.delay) <= Date.now());
 
                 for(const reminder of dueReminders) {
                     await reminder.deleteOne();
@@ -33,37 +33,41 @@ const event: Event = {
                         .setDescription(reminder.reason)
                         .addFields (
                             { name: "Set", value: `<t:${reminder.reminder_set.toString().slice(0, -3)}:f>`, inline: true },
-                            { name: "Overdue Since", value: `<t:${(reminder.reminder_set + reminder.delay).toString().slice(0, -3)}:R>`, inline: true }
+                            { name: "Overdue Since", value: `<t:${(Number(reminder.reminder_set) + reminder.delay).toString().slice(0, -3)}:R>`, inline: true }
                         )
                         .setFooter({ text: `ID: ${reminder.reminder_id}` })
                         .setTimestamp()
 
+                    const recurring = new EmbedBuilder()
+                        .setColor(client.config.embeds.default as ColorResolvable)
+                        .setDescription("This reminder was a recurring reminder, however since it was sent overdue, it has been reset, you will need to set it again if you want it to continue.")
+
                     if(reminder?.send_in_channel && reminder.channel) {
                         try {
                             const channel = client.channels.cache.get(reminder.channel) as TextChannel;
 
                             if(!channel) throw "Channel not found.";
 
-                            await channel.send({ content: `<@${reminder.user}>`, embeds: [embed] });
+                            await channel.send({ content: `<@${reminder.user}>`, embeds: reminder?.recurring ? [embed, recurring] : [embed] });
                         } catch {
                             try {
                                 const user = client.users.cache.get(reminder.user);
 
-                                await user?.send({ embeds: [embed] });
+                                await user?.send({ embeds: reminder?.recurring ? [embed, recurring] : [embed] });
                             } catch {}
                         }
                     } else {
                         try {
                             const user = client.users.cache.get(reminder.user);
 
-                            await user?.send({ embeds: [embed] });
+                            await user?.send({ embeds: reminder?.recurring ? [embed, recurring] : [embed] });
                         } catch {
                             try {
                                 const channel = client.channels.cache.get(reminder.channel) as TextChannel;
 
                                 if(!channel) return;
 
-                                await channel.send({ content: `<@${reminder.user}>`, embeds: [embed] });
+                                await channel.send({ content: `<@${reminder.user}>`, embeds: reminder?.recurring ? [embed, recurring] : [embed] });
                             } catch {}
                         }
                     }
@@ -74,11 +78,11 @@ const event: Event = {
                 }
             }
 
-            manageExistingTimeouts().then(async () => {
+            manageExistingReminders().then(async () => {
                 setInterval(async () => {
                     const reminders = await Reminder.find({});
 
-                    if(reminders.length === 0) return;
+                    if(!reminders.length) return;
 
                     for(const reminder of reminders) {
                         if(client.reminders.get(`${reminder.user}-${reminder.reminder_id}`)) continue;
diff --git a/src/models/Reminder.ts b/src/models/Reminder.ts
index 1aa8cc0..60eba2a 100644
--- a/src/models/Reminder.ts
+++ b/src/models/Reminder.ts
@@ -29,6 +29,7 @@ export interface Reminder extends Document {
     reminder_set: string;
     reason: string;
     send_in_channel?: boolean;
+    recurring?: boolean;
 }
 
 const schema = new Schema<Reminder>(
@@ -50,7 +51,7 @@ const schema = new Schema<Reminder>(
         },
         channel: {
             type: String,
-            required: false,
+            required: true,
             set: (value: string) => encrypt(value),
             get: (value: string) => decrypt(value)
         },
@@ -75,6 +76,10 @@ const schema = new Schema<Reminder>(
         send_in_channel: {
             type: Boolean,
             default: false
+        },
+        recurring: {
+            type: Boolean,
+            default: false
         }
     },
     { timestamps: true }
diff --git a/src/util/setReminder.ts b/src/util/setReminder.ts
index 2ecdc26..7a4f012 100644
--- a/src/util/setReminder.ts
+++ b/src/util/setReminder.ts
@@ -5,7 +5,7 @@ import ExtendedClient from "../classes/ExtendedClient";
 import { Reminder } from "../models/Reminder";
 
 export default async function (reminder: Reminder, client: ExtendedClient): Promise<Boolean> {
-    const delay = Number(reminder.reminder_set + reminder.delay) - Date.now();
+    const delay = Number(Number(reminder.reminder_set) + reminder.delay) - Date.now();
 
     if(delay > client.config.reminders.timeTillSet) return false;
 
@@ -23,7 +23,7 @@ export default async function (reminder: Reminder, client: ExtendedClient): Prom
             .setFooter({ text: `ID: ${reminder.reminder_id}` })
             .setTimestamp()
 
-        if(reminder?.send_in_channel && reminder?.channel) {
+        if(reminder?.send_in_channel && reminder.channel) {
             try {
                 const channel = client.channels.cache.get(reminder.channel) as TextChannel;