From f9625db2c275aef6130194a9d2b3c6935740ad1a Mon Sep 17 00:00:00 2001 From: Mylloon Date: Sun, 15 Jan 2023 20:27:21 +0100 Subject: [PATCH] add reminder delete --- src/commands/misc/reminder.ts | 23 +++++-- src/events/client/ready.ts | 26 ++++---- src/locales/fr.json | 64 +++++++++--------- src/utils/client.ts | 3 +- src/utils/reminder.ts | 118 ++++++++++++++++++++++++++++++++-- 5 files changed, 175 insertions(+), 59 deletions(-) diff --git a/src/commands/misc/reminder.ts b/src/commands/misc/reminder.ts index 0326690..5f67f60 100644 --- a/src/commands/misc/reminder.ts +++ b/src/commands/misc/reminder.ts @@ -2,7 +2,7 @@ import { ModalActionRowComponentBuilder, SlashCommandBuilder } from '@discordjs/ import { ActionRowBuilder, ChatInputCommandInteraction, Client, ModalBuilder, TextInputBuilder, TextInputStyle } from 'discord.js'; import { getLocale, getLocalizations } from '../../utils/locales'; import { getFilename } from '../../utils/misc'; -import { newReminder } from '../../utils/reminder'; +import { checkOwnershipReminder, deleteReminder, getReminderInfo, newReminder } from '../../utils/reminder'; export default { data: (client: Client) => { @@ -207,11 +207,24 @@ export default { // Delete a reminder case loc_default?.get(`c_${filename}_sub3_name`) ?.toLowerCase() ?? '': { - // TODO: Message simple qui indique que l'on a supprimé - // le reminder. Penser à check l'appartenance du reminder. - // On donne l'ID du reminder en option. + const id = interaction.options.getInteger(loc_default?.get(`c_${filename}_sub3_opt1_name`) as string); + if (id === null) { + return interaction.reply({ content: loc?.get('c_reminder2'), ephemeral: true }); + } - return interaction.reply('TODO'); + // Check if the ID exists and belongs to the user + if (await checkOwnershipReminder(client, id, interaction.user.id, interaction.guildId ?? '0')) { + return interaction.reply({ content: loc?.get('c_reminder3'), ephemeral: true }); + } + + // Stop timeout + const reminderInfo = await getReminderInfo(client, id); + clearTimeout(reminderInfo.timeout_id); + + // Delete from database + deleteReminder(client, reminderInfo.creation_date, reminderInfo.user_id); + + return interaction.reply({ content: `Reminder **#${id}** supprimé !`, ephemeral: true }); } default: { console.error(`${__filename}: unknown subcommand (${subcommand})`); diff --git a/src/events/client/ready.ts b/src/events/client/ready.ts index e2b47d3..1a20b80 100644 --- a/src/events/client/ready.ts +++ b/src/events/client/ready.ts @@ -1,20 +1,8 @@ import { Client } from 'discord.js'; -import { deleteReminder, infoReminder, OptionReminder, sendReminder, setTimeoutReminder } from '../../utils/reminder'; +import { dbReminder, deleteReminder, infoReminder, OptionReminder, sendReminder, setTimeoutReminder, updateReminder } from '../../utils/reminder'; export const once = true; -type dbReminder = { - id: number, - data: string, - expiration_date: number, - option_id: number, - channel_id: number, - creation_date: number, - user_id: number, - guild_id: number, - locale: string -} - /** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-ready */ export default async (client: Client) => { console.log('Connected to Discord!'); @@ -49,11 +37,19 @@ export default async (client: Client) => { throw res; } }); - client.channels.fetch(`${info.channelId}`).then((val) => console.log(val?.url)); + sendReminder(client, info, element.option_id as OptionReminder); } else { // Restart timeout - setTimeoutReminder(client, info, element.option_id, (element.expiration_date - now) / 1000); + const timeoutId = setTimeoutReminder(client, info, element.option_id, (element.expiration_date - now) / 1000); + + // Update timeout in database + element.timeout_id = timeoutId; + updateReminder(client, element).then((res) => { + if (res != true) { + throw res; + } + }); } }); }).catch(err => { diff --git a/src/locales/fr.json b/src/locales/fr.json index 1a6d224..f98ff2c 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -1,37 +1,39 @@ { - "e_interacreate_no_command": "Désolé, la commande n'existe plus...", - "e_interacreate_no_modal": "Désolé, le modèle n'existe plus...", + "e_interacreate_no_command": "Désolé, la commande n'existe plus...", + "e_interacreate_no_modal": "Désolé, le modèle n'existe plus...", - "c_ping_name": "Ping", - "c_ping_desc": "Pong!", - "c_ping1": "Latence totale", - "c_ping2": "Latence du Websocket", + "c_ping_name": "Ping", + "c_ping_desc": "Pong!", + "c_ping1": "Latence totale", + "c_ping2": "Latence du Websocket", - "c_help_name": "Aide", - "c_help_desc": "Informations sur les commandes", - "c_help_opt1_name": "commande", - "c_help_opt1_desc": "Commande voulu en détail.", - "c_help1": "Liste des catégories et des commandes associées", - "c_help2": "`/help ` pour obtenir plus d'informations sur une commande.", - "c_help3": "Impossible de trouver :", + "c_help_name": "Aide", + "c_help_desc": "Informations sur les commandes", + "c_help_opt1_name": "commande", + "c_help_opt1_desc": "Commande voulu en détail.", + "c_help1": "Liste des catégories et des commandes associées", + "c_help2": "`/help ` pour obtenir plus d'informations sur une commande.", + "c_help3": "Impossible de trouver :", - "u_time_at": "à", + "u_time_at": "à", - "c_reminder_name": "rappel", - "c_reminder_desc": "Commande relative aux rappels", - "c_reminder_sub1_name": "nouveau", - "c_reminder_sub1_desc": "Met en place un rappel", - "c_reminder_sub1_opt1_name": "temps", - "c_reminder_sub1_opt1_desc": "Temps désiré avant le rappel, accolez un @ pour activer la mention ou un p pour envoyer en DM", - "c_reminder_sub1_opt2_name": "message", - "c_reminder_sub1_opt2_desc": "Message du rappel", - "c_reminder_sub2_name": "liste", - "c_reminder_sub2_desc": "Affiche la liste des rappels d'un utilisateur", - "c_reminder_sub2_opt1_name": "utilisateur", - "c_reminder_sub2_opt1_desc": "Affiche la liste de l'utilisateur en question", - "c_reminder_sub3_name": "efface", - "c_reminder_sub3_desc": "Supprime un rappel", - "c_reminder_sub3_opt1_name": "ID", - "c_reminder_sub3_opt1_desc": "Rappel à supprimé", - "c_reminder1": "Un rappel a été configuré pour dans" + "c_reminder_name": "rappel", + "c_reminder_desc": "Commande relative aux rappels", + "c_reminder_sub1_name": "nouveau", + "c_reminder_sub1_desc": "Met en place un rappel", + "c_reminder_sub1_opt1_name": "temps", + "c_reminder_sub1_opt1_desc": "Temps désiré avant le rappel, accolez un @ pour activer la mention ou un p pour envoyer en DM", + "c_reminder_sub1_opt2_name": "message", + "c_reminder_sub1_opt2_desc": "Message du rappel", + "c_reminder_sub2_name": "liste", + "c_reminder_sub2_desc": "Affiche la liste des rappels d'un utilisateur", + "c_reminder_sub2_opt1_name": "utilisateur", + "c_reminder_sub2_opt1_desc": "Affiche la liste de l'utilisateur en question", + "c_reminder_sub3_name": "efface", + "c_reminder_sub3_desc": "Supprime un rappel", + "c_reminder_sub3_opt1_name": "id", + "c_reminder_sub3_opt1_desc": "Rappel à supprimé", + "c_reminder1": "Un rappel a été configuré pour dans", + "c_reminder2": "L'ID renseigné n'est pas valide.", + "c_reminder3": "Rappel non trouvé, pas sur le bon serveur ou qui ne vous appartiens pas." } diff --git a/src/utils/client.ts b/src/utils/client.ts index 9c98aae..84bd16a 100644 --- a/src/utils/client.ts +++ b/src/utils/client.ts @@ -66,6 +66,7 @@ const initDatabase = (db: Database) => { creation_date TEXT, \ user_id TEXT, \ guild_id TEXT, \ - locale TEXT \ + locale TEXT, \ + timeout_id TEXT \ );'); }; diff --git a/src/utils/reminder.ts b/src/utils/reminder.ts index e2983c9..ad23b2c 100644 --- a/src/utils/reminder.ts +++ b/src/utils/reminder.ts @@ -26,6 +26,19 @@ export type infoReminder = { guildId: string | null } +export type dbReminder = { + id: number, + data: string | null, + expiration_date: number, + option_id: OptionReminder, + channel_id: string | null, + creation_date: number, + user_id: string, + guild_id: string | null, + locale: string, + timeout_id: number +} + /** * Split the time and the extra args `p` and `@` * @param time raw text from user @@ -52,12 +65,12 @@ export const newReminder = async (client: Client, time: string, info: infoRemind new Promise((ok, ko) => { const data = splitTime(time); const timeout = strToSeconds(data.time); - setTimeoutReminder(client, info, data.option, timeout); + const timeoutId = setTimeoutReminder(client, info, data.option, timeout); // Add the remind to the db client.db.run('INSERT INTO reminder ( \ - data, expiration_date, option_id, channel_id, creation_date, user_id, guild_id, locale \ - ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? );', [ + data, expiration_date, option_id, channel_id, creation_date, user_id, guild_id, locale, timeout_id \ + ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? );', [ info.message, `${info.createdAt + (timeout * 1000)}`, data.option.valueOf(), @@ -65,7 +78,8 @@ export const newReminder = async (client: Client, time: string, info: infoRemind `${info.createdAt}`, info.userId, info.guildId, - info.locale], (err) => { + info.locale, + timeoutId], (err) => { if (err) { ko(err); } @@ -77,7 +91,7 @@ export const newReminder = async (client: Client, time: string, info: infoRemind }); /** - * Delete a reminder + * Delete a reminder from the database * @param client Client * @param createdAt Creation of the reminder * @param userId User ID who created the reminder @@ -127,9 +141,10 @@ export const sendReminder = (client: Client, info: infoReminder, option: OptionR * @param info info about the reminder * @param option option used for this reminder (aka location of the response) * @param timeout Amout of time before the reminder ends + * @returns Timeout's ID */ export const setTimeoutReminder = (client: Client, info: infoReminder, option: OptionReminder, timeout: number) => { - setTimeout(() => { + return Number(setTimeout(() => { deleteReminder(client, info.createdAt, info.userId).then((val) => { if (val != true) { throw val; @@ -137,5 +152,94 @@ export const setTimeoutReminder = (client: Client, info: infoReminder, option: O sendReminder(client, info, option); }); - }, timeout * 1000); + }, timeout * 1000)); +}; + +/** + * Check the owernship of a reminder by a user + * @param client Client + * @param id ID of the reminder + * @param userId user ID to check + * @param guildId guild ID where the ownership request as been send, 0 if DM + */ +export const checkOwnershipReminder = async (client: Client, id: number, userId: string, guildId: string) => { + const data = await new Promise((ok, ko) => { + // Check the ownership + client.db.all('SELECT EXISTS ( \ + SELECT 1 FROM reminder \ + WHERE id = ? \ + AND user_id = ? \ + AND (guild_id = ? OR guild_id = 0) \ + )', [ + id, userId, guildId, + ], (err, row) => { + if (err) { + ko(err); + } + + // Send all the current reminders + ok(row[0]); + }); + }) as { [key: string]: number }; + return Object.keys(data).map((key) => data[key])[0] === 0 ? true : false; +}; + +/** + * Retrieve info about a reminder + * @param client Client + * @param id Reminder's ID + */ +export const getReminderInfo = async (client: Client, id: number) => { + return await new Promise((ok, ko) => { + // Check the ownership + client.db.all('SELECT * FROM reminder \ + WHERE id = ?', [ + id], (err, row) => { + if (err) { + ko(err); + } + + // Send all the current reminders + ok(row[0]); + }); + }) as dbReminder; +}; + +/** + * Update an entry of the database + * @param client Client + * @param data Data who will override the data in database + */ +export const updateReminder = (client: Client, data: dbReminder) => { + // Delete the reminder for the database + return new Promise((ok, ko) => { + // Update the db + client.db.run('UPDATE reminder \ + SET data = ?, \ + expiration_date = ?, \ + option_id = ?, \ + channel_id = ?, \ + creation_date = ?, \ + user_id = ?, \ + guild_id = ?, \ + locale = ?, \ + timeout_id = ? \ + WHERE ID = ?', [ + data.data, + data.expiration_date, + data.option_id, + data.channel_id, + data.creation_date, + data.user_id, + data.guild_id, + data.locale, + data.timeout_id, + data.id], (err) => { + if (err) { + ko(err); + } + + ok(true); + }); + }); };