From 1a3882a1118f0b3d72baf291fb0799d05464d6f6 Mon Sep 17 00:00:00 2001 From: Larissa-Chelius Date: Mon, 13 Oct 2025 18:08:36 -0400 Subject: [PATCH 1/7] /cancelremind with multiple ways to cancle a reminder --- build/config.gypi | 2 +- src/commands/reminders/cancelreminder.ts | 225 ++++++++++++++++++++++- 2 files changed, 219 insertions(+), 8 deletions(-) diff --git a/build/config.gypi b/build/config.gypi index b03c9a66..40018f6e 100644 --- a/build/config.gypi +++ b/build/config.gypi @@ -488,7 +488,7 @@ "v8_use_siphash": 1, "want_separate_host_toolset": 0, "xcode_version": "16.0", - "nodedir": "/Users/Larissa/Library/Caches/node-gyp/23.11.0", + "nodedir": "/Users/Larissa/Library/Caches/node-gyp/23.11.1", "python": "/Applications/Xcode.app/Contents/Developer/usr/bin/python3", "standalone_static_library": 1, "fallback_to_build": "true", diff --git a/src/commands/reminders/cancelreminder.ts b/src/commands/reminders/cancelreminder.ts index 5ec67c16..3afadc56 100644 --- a/src/commands/reminders/cancelreminder.ts +++ b/src/commands/reminders/cancelreminder.ts @@ -1,8 +1,10 @@ import { Reminder } from '@lib/types/Reminder'; import { DB } from '@root/config'; -import { ApplicationCommandOptionData, ApplicationCommandOptionType, ChatInputCommandInteraction, InteractionResponse } from 'discord.js'; +import { ActionRowBuilder, ApplicationCommandOptionData, ApplicationCommandOptionType, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction, ComponentType, InteractionResponse, StringSelectMenuBuilder } from 'discord.js'; import { Command } from '@lib/types/Command'; +import { ObjectId } from 'mongodb'; +const PAGE_SIZE = 25; export default class extends Command { description = 'Cancel any pending reminders you may have.'; @@ -12,13 +14,26 @@ export default class extends Command { { name: 'remindernumber', type: ApplicationCommandOptionType.Integer, - required: true, - description: 'ID of the reminder to cancel' + required: false, + description: 'List index (from /viewremind) of the reminder to cancel (1-based)' + }, + { + name: 'id', + type: ApplicationCommandOptionType.String, + required: false, + description: 'Advanced: reminder ObjectId (for power users)' + }, + { + name: 'page', + type: ApplicationCommandOptionType.Integer, + required: false, + description: 'Advanced: open the selection menu at a specific page', + min_value: 1, } - ] + ]; async run(interaction: ChatInputCommandInteraction): Promise | void> { - const remindNum = interaction.options.getInteger('remindernumber') - 1; + /*const remindNum = interaction.options.getInteger('remindernumber') - 1; const reminders: Array = await interaction.client.mongo.collection(DB.REMINDERS) .find({ owner: interaction.user.id }).toArray(); @@ -38,7 +53,203 @@ export default class extends Command { return interaction.reply({ content: `Canceled reminder: **${reminder.content}**`, ephemeral: hidden - }); - } + }); */ + const numberOpt = interaction.options.getInteger('remindernumber'); + const idOpt = interaction.options.getString('id'); + const pageOpt = interaction.options.getInteger('page') ?? 1; + + const coll = interaction.client.mongo.collection(DB.REMINDERS); + const reminders = await coll.find({ owner: interaction.user.id }).toArray(); + + // Sort by soonest first for a stable, predictable order + reminders.sort((a, b) => a.expires.valueOf() - b.expires.valueOf()); + + // Nothing to cancel? + if (reminders.length === 0) { + return interaction.reply({ + content: 'You have no pending reminders. 🎉', + ephemeral: true, + }); + } + + // 1) Power path: cancel by ObjectId string + if (idOpt) { + let objId: ObjectId | null = null; + try { + objId = new ObjectId(idOpt); + } catch { + return interaction.reply({ + content: 'That ID is not a valid reminder identifier.', + ephemeral: true, + }); + } + + const toDelete = reminders.find(r => (r as any)._id?.toString() === objId!.toString()); + if (!toDelete) { + return interaction.reply({ + content: 'I couldn’t find a reminder with that ID. Use `/viewremind` or the dropdown instead.', + ephemeral: true, + }); + } + const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); + if (delRes.deletedCount !== 1) { + return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', ephemeral: true }); + } + const hidden = toDelete.mode === 'private'; + return interaction.reply({ + content: `Canceled reminder: **${toDelete.content}**`, + ephemeral: hidden, + }); + } + + // 2) Power path: cancel by 1-based index + if (numberOpt !== null) { + const index = numberOpt - 1; + if (index < 0 || index >= reminders.length) { + return interaction.reply({ + content: `I couldn’t find reminder **#${numberOpt}**. Use \`/viewremind\` to see your list.`, + ephemeral: true, + }); + } + const reminder = reminders[index]; + const delRes = await coll.deleteOne({ + _id: (reminder as any)._id, + owner: interaction.user.id, + }); + if (delRes.deletedCount !== 1) { + return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', ephemeral: true }); + } + const hidden = reminder.mode === 'private'; + return interaction.reply({ + content: `Canceled reminder: **${reminder.content}**`, + ephemeral: hidden, + }); + } + + // 3) Friendly path: interactive select menu (paginated) + const totalPages = Math.max(1, Math.ceil(reminders.length / PAGE_SIZE)); + const page = Math.min(Math.max(1, pageOpt), totalPages); + const { menu, navRow } = this.buildSelect(reminders, page, totalPages); + + const msg = await interaction.reply({ + content: `Pick a reminder to cancel (page ${page}/${totalPages}):`, + components: navRow ? [menu, navRow] : [menu], + ephemeral: true, + fetchReply: true, + }); + + // Collect interactions from this user only, ephemeral + const collector = (msg as any).createMessageComponentCollector({ + componentType: ComponentType.StringSelect, + time: 60_000, + filter: (i: any) => i.user.id === interaction.user.id, + }); + + const buttonCollector = (msg as any).createMessageComponentCollector({ + componentType: ComponentType.Button, + time: 60_000, + filter: (i: any) => i.user.id === interaction.user.id, + }); + + collector.on('collect', async (i: any) => { + const chosenId = i.values[0]; // stringified ObjectId + const chosen = reminders.find(r => (r as any)._id?.toString() === chosenId); + if (!chosen) { + return i.reply({ content: 'That reminder no longer exists.', ephemeral: true }); + } + + const confirmRow = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId(`confirm:${chosenId}`) + .setLabel('Confirm cancel') + .setStyle(ButtonStyle.Danger), + new ButtonBuilder() + .setCustomId('cancel') + .setLabel('Never mind') + .setStyle(ButtonStyle.Secondary) + ); + + await i.reply({ + content: `Are you sure you want to cancel:\n> **${chosen.content}**`, + components: [confirmRow], + ephemeral: true, + }); + }); + + buttonCollector.on('collect', async (i: any) => { + try { + if (i.customId === 'cancel') { + return i.update({ content: 'Okay, leaving your reminders as-is.', components: [] }); + } + if (i.customId.startsWith('page:')) { + const nextPage = Number(i.customId.split(':')[1]); + const { menu: nextMenu, navRow: nextNav } = this.buildSelect(reminders, nextPage, totalPages); + return i.update({ + content: `Pick a reminder to cancel (page ${nextPage}/${totalPages}):`, + components: nextNav ? [nextMenu, nextNav] : [nextMenu], + }); + } + if (i.customId.startsWith('confirm:')) { + const idStr = i.customId.split(':')[1]; + const objId = new ObjectId(idStr); + const target = reminders.find(r => (r as any)._id?.toString() === idStr); + const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); + + if (delRes.deletedCount !== 1) { + return i.update({ + content: 'Something went wrong; I couldn’t delete that reminder. Try again.', + components: [], + }); + } + const hidden = target?.mode === 'private'; + return i.update({ + content: `Canceled reminder: **${target?.content ?? '(unknown)'}**`, + components: [], + // Parent interaction was already ephemeral; here we just update + }); + } + } catch (err) { + return i.reply({ content: 'Unexpected error while handling your action.', ephemeral: true }); + } + }); + } + + private buildSelect(reminders: Reminder[], page: number, totalPages: number) { + const start = (page - 1) * PAGE_SIZE; + const slice = reminders.slice(start, start + PAGE_SIZE); + + const menu = new ActionRowBuilder().addComponents( + new StringSelectMenuBuilder() + .setCustomId('reminder-select') + .setPlaceholder('Choose a reminder…') + .addOptions( + ...slice.map((r, idx) => ({ + label: this.truncate(r.content, 100), + description: `Due ${new Date(r.expires).toLocaleString()} • #${start + idx + 1}`, + value: (r as any)._id.toString(), + })) + ) + ); + + let navRow: ActionRowBuilder | null = null; + if (totalPages > 1) { + navRow = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId(`page:${Math.max(1, page - 1)}`) + .setLabel('Prev') + .setStyle(ButtonStyle.Secondary) + .setDisabled(page <= 1), + new ButtonBuilder() + .setCustomId(`page:${Math.min(totalPages, page + 1)}`) + .setLabel('Next') + .setStyle(ButtonStyle.Secondary) + .setDisabled(page >= totalPages) + ); + } + return { menu, navRow }; + } + private truncate(s: string, n: number) { + return s.length > n ? `${s.slice(0, n - 1)}…` : s; + } } From c33ae83791106a29551341d809bdaa640826751f Mon Sep 17 00:00:00 2001 From: Larissa-Chelius Date: Mon, 13 Oct 2025 22:42:04 -0400 Subject: [PATCH 2/7] fixed errors in new canceling --- src/commands/reminders/cancelreminder.ts | 60 +++++++++++++++--------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/commands/reminders/cancelreminder.ts b/src/commands/reminders/cancelreminder.ts index 3afadc56..30a620dc 100644 --- a/src/commands/reminders/cancelreminder.ts +++ b/src/commands/reminders/cancelreminder.ts @@ -152,29 +152,43 @@ export default class extends Command { }); collector.on('collect', async (i: any) => { - const chosenId = i.values[0]; // stringified ObjectId - const chosen = reminders.find(r => (r as any)._id?.toString() === chosenId); - if (!chosen) { - return i.reply({ content: 'That reminder no longer exists.', ephemeral: true }); - } - - const confirmRow = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setCustomId(`confirm:${chosenId}`) - .setLabel('Confirm cancel') - .setStyle(ButtonStyle.Danger), - new ButtonBuilder() - .setCustomId('cancel') - .setLabel('Never mind') - .setStyle(ButtonStyle.Secondary) - ); - - await i.reply({ - content: `Are you sure you want to cancel:\n> **${chosen.content}**`, - components: [confirmRow], - ephemeral: true, - }); - }); + const chosenId = i.values[0]; + const chosen = reminders.find(r => (r as any)._id?.toString() === chosenId); + if (!chosen) { + // since the collector is tied to this message, use update/deferUpdate patterns + return i.update({ content: 'That reminder no longer exists.', components: [] }); + } + + const confirmRow = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId(`confirm:${chosenId}`) + .setLabel('Confirm cancel') + .setStyle(ButtonStyle.Danger), + new ButtonBuilder() + .setCustomId('cancel') + .setLabel('Never mind') + .setStyle(ButtonStyle.Secondary) + ); + + // optionally disable the select so users can't pick twice + const disabledMenu = (i.message.components?.[0]) ?? null; + if (disabledMenu) { + // clone row & disable the select + const row = ActionRowBuilder.from(disabledMenu) as ActionRowBuilder; + const select = StringSelectMenuBuilder.from(row.components[0] as any).setDisabled(true); + row.setComponents(select); + return i.update({ + content: `Are you sure you want to cancel:\n> **${chosen.content}**`, + components: [row, ...(i.message.components?.slice(1) ?? []), confirmRow], + }); + } + + return i.update({ + content: `Are you sure you want to cancel:\n> **${chosen.content}**`, + components: [confirmRow], + }); + }); + buttonCollector.on('collect', async (i: any) => { try { From 06dde74b72ec920832511dd369c130544c7e5558 Mon Sep 17 00:00:00 2001 From: Matthew Meredith Date: Wed, 12 Nov 2025 14:14:20 -0500 Subject: [PATCH 3/7] fix most eslint errors --- src/commands/reminders/cancelreminder.ts | 391 ++++++++++++----------- 1 file changed, 196 insertions(+), 195 deletions(-) diff --git a/src/commands/reminders/cancelreminder.ts b/src/commands/reminders/cancelreminder.ts index 30a620dc..a79c2168 100644 --- a/src/commands/reminders/cancelreminder.ts +++ b/src/commands/reminders/cancelreminder.ts @@ -28,12 +28,12 @@ export default class extends Command { type: ApplicationCommandOptionType.Integer, required: false, description: 'Advanced: open the selection menu at a specific page', - min_value: 1, + minValue: 1 } ]; async run(interaction: ChatInputCommandInteraction): Promise | void> { - /*const remindNum = interaction.options.getInteger('remindernumber') - 1; + /* const remindNum = interaction.options.getInteger('remindernumber') - 1; const reminders: Array = await interaction.client.mongo.collection(DB.REMINDERS) .find({ owner: interaction.user.id }).toArray(); @@ -55,215 +55,216 @@ export default class extends Command { ephemeral: hidden }); */ const numberOpt = interaction.options.getInteger('remindernumber'); - const idOpt = interaction.options.getString('id'); - const pageOpt = interaction.options.getInteger('page') ?? 1; + const idOpt = interaction.options.getString('id'); + const pageOpt = interaction.options.getInteger('page') ?? 1; - const coll = interaction.client.mongo.collection(DB.REMINDERS); - const reminders = await coll.find({ owner: interaction.user.id }).toArray(); + const coll = interaction.client.mongo.collection(DB.REMINDERS); + const reminders = await coll.find({ owner: interaction.user.id }).toArray(); - // Sort by soonest first for a stable, predictable order - reminders.sort((a, b) => a.expires.valueOf() - b.expires.valueOf()); + // Sort by soonest first for a stable, predictable order + reminders.sort((a, b) => a.expires.valueOf() - b.expires.valueOf()); + + // Nothing to cancel? + if (reminders.length === 0) { + return interaction.reply({ + content: 'You have no pending reminders. 🎉', + ephemeral: true + }); + } + + // 1) Power path: cancel by ObjectId string + if (idOpt) { + let objId: ObjectId | null = null; + try { + objId = new ObjectId(idOpt); + } catch { + return interaction.reply({ + content: 'That ID is not a valid reminder identifier.', + ephemeral: true + }); + } - // Nothing to cancel? - if (reminders.length === 0) { - return interaction.reply({ - content: 'You have no pending reminders. 🎉', - ephemeral: true, - }); - } + const toDelete = reminders.find(r => (r as any)._id?.toString() === objId!.toString()); + if (!toDelete) { + return interaction.reply({ + content: 'I couldn’t find a reminder with that ID. Use `/viewremind` or the dropdown instead.', + ephemeral: true + }); + } + const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); + if (delRes.deletedCount !== 1) { + return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', ephemeral: true }); + } + const hidden = toDelete.mode === 'private'; + return interaction.reply({ + content: `Canceled reminder: **${toDelete.content}**`, + ephemeral: hidden + }); + } + + // 2) Power path: cancel by 1-based index + if (numberOpt !== null) { + const index = numberOpt - 1; + if (index < 0 || index >= reminders.length) { + return interaction.reply({ + content: `I couldn’t find reminder **#${numberOpt}**. Use \`/viewremind\` to see your list.`, + ephemeral: true + }); + } + const reminder = reminders[index]; + const delRes = await coll.deleteOne({ + _id: (reminder as any)._id, + owner: interaction.user.id + }); + if (delRes.deletedCount !== 1) { + return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', ephemeral: true }); + } + const hidden = reminder.mode === 'private'; + return interaction.reply({ + content: `Canceled reminder: **${reminder.content}**`, + ephemeral: hidden + }); + } - // 1) Power path: cancel by ObjectId string - if (idOpt) { - let objId: ObjectId | null = null; - try { - objId = new ObjectId(idOpt); - } catch { - return interaction.reply({ - content: 'That ID is not a valid reminder identifier.', - ephemeral: true, - }); - } + // 3) Friendly path: interactive select menu (paginated) + const totalPages = Math.max(1, Math.ceil(reminders.length / PAGE_SIZE)); + const page = Math.min(Math.max(1, pageOpt), totalPages); + const { menu, navRow } = this.buildSelect(reminders, page, totalPages); - const toDelete = reminders.find(r => (r as any)._id?.toString() === objId!.toString()); - if (!toDelete) { - return interaction.reply({ - content: 'I couldn’t find a reminder with that ID. Use `/viewremind` or the dropdown instead.', - ephemeral: true, - }); - } - const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); - if (delRes.deletedCount !== 1) { - return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', ephemeral: true }); - } - const hidden = toDelete.mode === 'private'; - return interaction.reply({ - content: `Canceled reminder: **${toDelete.content}**`, - ephemeral: hidden, - }); - } + const msg = await interaction.reply({ + content: `Pick a reminder to cancel (page ${page}/${totalPages}):`, + components: navRow ? [menu, navRow] : [menu], + ephemeral: true, + fetchReply: true + }); - // 2) Power path: cancel by 1-based index - if (numberOpt !== null) { - const index = numberOpt - 1; - if (index < 0 || index >= reminders.length) { - return interaction.reply({ - content: `I couldn’t find reminder **#${numberOpt}**. Use \`/viewremind\` to see your list.`, - ephemeral: true, - }); - } - const reminder = reminders[index]; - const delRes = await coll.deleteOne({ - _id: (reminder as any)._id, - owner: interaction.user.id, - }); - if (delRes.deletedCount !== 1) { - return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', ephemeral: true }); - } - const hidden = reminder.mode === 'private'; - return interaction.reply({ - content: `Canceled reminder: **${reminder.content}**`, - ephemeral: hidden, - }); - } + // Collect interactions from this user only, ephemeral + const collector = (msg as any).createMessageComponentCollector({ + componentType: ComponentType.StringSelect, + time: 60_000, + filter: (i: any) => i.user.id === interaction.user.id + }); - // 3) Friendly path: interactive select menu (paginated) - const totalPages = Math.max(1, Math.ceil(reminders.length / PAGE_SIZE)); - const page = Math.min(Math.max(1, pageOpt), totalPages); - const { menu, navRow } = this.buildSelect(reminders, page, totalPages); + const buttonCollector = (msg as any).createMessageComponentCollector({ + componentType: ComponentType.Button, + time: 60_000, + filter: (i: any) => i.user.id === interaction.user.id + }); - const msg = await interaction.reply({ - content: `Pick a reminder to cancel (page ${page}/${totalPages}):`, - components: navRow ? [menu, navRow] : [menu], - ephemeral: true, - fetchReply: true, - }); + collector.on('collect', async (i: any) => { + const chosenId = i.values[0]; + const chosen = reminders.find(r => (r as any)._id?.toString() === chosenId); + if (!chosen) { + // since the collector is tied to this message, use update/deferUpdate patterns + return i.update({ content: 'That reminder no longer exists.', components: [] }); + } - // Collect interactions from this user only, ephemeral - const collector = (msg as any).createMessageComponentCollector({ - componentType: ComponentType.StringSelect, - time: 60_000, - filter: (i: any) => i.user.id === interaction.user.id, - }); + const confirmRow = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId(`confirm:${chosenId}`) + .setLabel('Confirm cancel') + .setStyle(ButtonStyle.Danger), + new ButtonBuilder() + .setCustomId('cancel') + .setLabel('Never mind') + .setStyle(ButtonStyle.Secondary) + ); - const buttonCollector = (msg as any).createMessageComponentCollector({ - componentType: ComponentType.Button, - time: 60_000, - filter: (i: any) => i.user.id === interaction.user.id, - }); + // optionally disable the select so users can't pick twice + const disabledMenu = i.message.components?.[0] ?? null; + if (disabledMenu) { + // clone row & disable the select + const row = ActionRowBuilder.from(disabledMenu) as ActionRowBuilder; + const select = StringSelectMenuBuilder.from(row.components[0] as any).setDisabled(true); + row.setComponents(select); + return i.update({ + content: `Are you sure you want to cancel:\n> **${chosen.content}**`, + components: [row, ...(i.message.components?.slice(1) ?? []), confirmRow] + }); + } - collector.on('collect', async (i: any) => { - const chosenId = i.values[0]; - const chosen = reminders.find(r => (r as any)._id?.toString() === chosenId); - if (!chosen) { - // since the collector is tied to this message, use update/deferUpdate patterns - return i.update({ content: 'That reminder no longer exists.', components: [] }); - } - - const confirmRow = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setCustomId(`confirm:${chosenId}`) - .setLabel('Confirm cancel') - .setStyle(ButtonStyle.Danger), - new ButtonBuilder() - .setCustomId('cancel') - .setLabel('Never mind') - .setStyle(ButtonStyle.Secondary) - ); - - // optionally disable the select so users can't pick twice - const disabledMenu = (i.message.components?.[0]) ?? null; - if (disabledMenu) { - // clone row & disable the select - const row = ActionRowBuilder.from(disabledMenu) as ActionRowBuilder; - const select = StringSelectMenuBuilder.from(row.components[0] as any).setDisabled(true); - row.setComponents(select); - return i.update({ - content: `Are you sure you want to cancel:\n> **${chosen.content}**`, - components: [row, ...(i.message.components?.slice(1) ?? []), confirmRow], - }); - } - - return i.update({ - content: `Are you sure you want to cancel:\n> **${chosen.content}**`, - components: [confirmRow], + return i.update({ + content: `Are you sure you want to cancel:\n> **${chosen.content}**`, + components: [confirmRow] + }); }); - }); - - buttonCollector.on('collect', async (i: any) => { - try { - if (i.customId === 'cancel') { - return i.update({ content: 'Okay, leaving your reminders as-is.', components: [] }); - } - if (i.customId.startsWith('page:')) { - const nextPage = Number(i.customId.split(':')[1]); - const { menu: nextMenu, navRow: nextNav } = this.buildSelect(reminders, nextPage, totalPages); - return i.update({ - content: `Pick a reminder to cancel (page ${nextPage}/${totalPages}):`, - components: nextNav ? [nextMenu, nextNav] : [nextMenu], - }); - } - if (i.customId.startsWith('confirm:')) { - const idStr = i.customId.split(':')[1]; - const objId = new ObjectId(idStr); - const target = reminders.find(r => (r as any)._id?.toString() === idStr); - const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); - if (delRes.deletedCount !== 1) { - return i.update({ - content: 'Something went wrong; I couldn’t delete that reminder. Try again.', - components: [], - }); - } - const hidden = target?.mode === 'private'; - return i.update({ - content: `Canceled reminder: **${target?.content ?? '(unknown)'}**`, - components: [], - // Parent interaction was already ephemeral; here we just update - }); - } - } catch (err) { - return i.reply({ content: 'Unexpected error while handling your action.', ephemeral: true }); - } - }); - } + buttonCollector.on('collect', async (i: any) => { + try { + if (i.customId === 'cancel') { + return i.update({ content: 'Okay, leaving your reminders as-is.', components: [] }); + } + if (i.customId.startsWith('page:')) { + const nextPage = Number(i.customId.split(':')[1]); + const { menu: nextMenu, navRow: nextNav } = this.buildSelect(reminders, nextPage, totalPages); + return i.update({ + content: `Pick a reminder to cancel (page ${nextPage}/${totalPages}):`, + components: nextNav ? [nextMenu, nextNav] : [nextMenu] + }); + } + if (i.customId.startsWith('confirm:')) { + const idStr = i.customId.split(':')[1]; + const objId = new ObjectId(idStr); + const target = reminders.find(r => (r as any)._id?.toString() === idStr); + const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); + + if (delRes.deletedCount !== 1) { + return i.update({ + content: 'Something went wrong; I couldn’t delete that reminder. Try again.', + components: [] + }); + } + const hidden = target?.mode === 'private'; + return i.update({ + content: `Canceled reminder: **${target?.content ?? '(unknown)'}**`, + components: [] + // Parent interaction was already ephemeral; here we just update + }); + } + } catch (err) { + return i.reply({ content: 'Unexpected error while handling your action.', ephemeral: true }); + } + }); + } + + private buildSelect(reminders: Reminder[], page: number, totalPages: number) { + const start = (page - 1) * PAGE_SIZE; + const slice = reminders.slice(start, start + PAGE_SIZE); - private buildSelect(reminders: Reminder[], page: number, totalPages: number) { - const start = (page - 1) * PAGE_SIZE; - const slice = reminders.slice(start, start + PAGE_SIZE); + const menu = new ActionRowBuilder().addComponents( + new StringSelectMenuBuilder() + .setCustomId('reminder-select') + .setPlaceholder('Choose a reminder…') + .addOptions( + ...slice.map((r, idx) => ({ + label: this.truncate(r.content, 100), + description: `Due ${new Date(r.expires).toLocaleString()} • #${start + idx + 1}`, + value: (r as any)._id.toString() + })) + ) + ); - const menu = new ActionRowBuilder().addComponents( - new StringSelectMenuBuilder() - .setCustomId('reminder-select') - .setPlaceholder('Choose a reminder…') - .addOptions( - ...slice.map((r, idx) => ({ - label: this.truncate(r.content, 100), - description: `Due ${new Date(r.expires).toLocaleString()} • #${start + idx + 1}`, - value: (r as any)._id.toString(), - })) - ) - ); + let navRow: ActionRowBuilder | null = null; + if (totalPages > 1) { + navRow = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId(`page:${Math.max(1, page - 1)}`) + .setLabel('Prev') + .setStyle(ButtonStyle.Secondary) + .setDisabled(page <= 1), + new ButtonBuilder() + .setCustomId(`page:${Math.min(totalPages, page + 1)}`) + .setLabel('Next') + .setStyle(ButtonStyle.Secondary) + .setDisabled(page >= totalPages) + ); + } + return { menu, navRow }; + } - let navRow: ActionRowBuilder | null = null; - if (totalPages > 1) { - navRow = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setCustomId(`page:${Math.max(1, page - 1)}`) - .setLabel('Prev') - .setStyle(ButtonStyle.Secondary) - .setDisabled(page <= 1), - new ButtonBuilder() - .setCustomId(`page:${Math.min(totalPages, page + 1)}`) - .setLabel('Next') - .setStyle(ButtonStyle.Secondary) - .setDisabled(page >= totalPages) - ); - } - return { menu, navRow }; - } + private truncate(str: string, len: number) { + return str.length > len ? `${str.slice(0, len - 1)}…` : str; + } - private truncate(s: string, n: number) { - return s.length > n ? `${s.slice(0, n - 1)}…` : s; - } } From 413ab6779f0c5bcc63afe806d23a26292643c5ae Mon Sep 17 00:00:00 2001 From: Matthew Meredith Date: Wed, 12 Nov 2025 14:30:45 -0500 Subject: [PATCH 4/7] fix deprecated usage of `ephemeral` and `fetchReply`. Set eslint-disable where necessary. Mark todo on remaining issues from changing from `fetchReply` to `withResponse` --- src/commands/reminders/cancelreminder.ts | 58 ++++++++++++++---------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/src/commands/reminders/cancelreminder.ts b/src/commands/reminders/cancelreminder.ts index a79c2168..536ce3ff 100644 --- a/src/commands/reminders/cancelreminder.ts +++ b/src/commands/reminders/cancelreminder.ts @@ -1,6 +1,17 @@ import { Reminder } from '@lib/types/Reminder'; import { DB } from '@root/config'; -import { ActionRowBuilder, ApplicationCommandOptionData, ApplicationCommandOptionType, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction, ComponentType, InteractionResponse, StringSelectMenuBuilder } from 'discord.js'; +import { + ActionRowBuilder, + ApplicationCommandOptionData, + ApplicationCommandOptionType, + ButtonBuilder, + ButtonStyle, + ChatInputCommandInteraction, + ComponentType, + InteractionResponse, + MessageFlags, + StringSelectMenuBuilder +} from 'discord.js'; import { Command } from '@lib/types/Command'; import { ObjectId } from 'mongodb'; @@ -68,37 +79,37 @@ export default class extends Command { if (reminders.length === 0) { return interaction.reply({ content: 'You have no pending reminders. 🎉', - ephemeral: true + flags: MessageFlags.Ephemeral }); } // 1) Power path: cancel by ObjectId string if (idOpt) { - let objId: ObjectId | null = null; + let objId: ObjectId; try { objId = new ObjectId(idOpt); } catch { return interaction.reply({ content: 'That ID is not a valid reminder identifier.', - ephemeral: true + flags: MessageFlags.Ephemeral }); } - const toDelete = reminders.find(r => (r as any)._id?.toString() === objId!.toString()); + const toDelete = reminders.find(r => (r as any)._id?.toString() === objId.toString()); if (!toDelete) { return interaction.reply({ content: 'I couldn’t find a reminder with that ID. Use `/viewremind` or the dropdown instead.', - ephemeral: true + flags: MessageFlags.Ephemeral }); } const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); if (delRes.deletedCount !== 1) { - return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', ephemeral: true }); + return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', flags: MessageFlags.Ephemeral }); } const hidden = toDelete.mode === 'private'; return interaction.reply({ content: `Canceled reminder: **${toDelete.content}**`, - ephemeral: hidden + flags: hidden ? MessageFlags.Ephemeral : undefined }); } @@ -108,7 +119,7 @@ export default class extends Command { if (index < 0 || index >= reminders.length) { return interaction.reply({ content: `I couldn’t find reminder **#${numberOpt}**. Use \`/viewremind\` to see your list.`, - ephemeral: true + flags: MessageFlags.Ephemeral }); } const reminder = reminders[index]; @@ -117,12 +128,12 @@ export default class extends Command { owner: interaction.user.id }); if (delRes.deletedCount !== 1) { - return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', ephemeral: true }); + return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', flags: MessageFlags.Ephemeral }); } const hidden = reminder.mode === 'private'; return interaction.reply({ content: `Canceled reminder: **${reminder.content}**`, - ephemeral: hidden + flags: hidden ? MessageFlags.Ephemeral : undefined }); } @@ -131,27 +142,27 @@ export default class extends Command { const page = Math.min(Math.max(1, pageOpt), totalPages); const { menu, navRow } = this.buildSelect(reminders, page, totalPages); - const msg = await interaction.reply({ + const replyInteraction = await interaction.reply({ content: `Pick a reminder to cancel (page ${page}/${totalPages}):`, components: navRow ? [menu, navRow] : [menu], - ephemeral: true, - fetchReply: true + flags: MessageFlags.Ephemeral, + withResponse: true }); // Collect interactions from this user only, ephemeral - const collector = (msg as any).createMessageComponentCollector({ + const collector = replyInteraction.resource.message.createMessageComponentCollector({ componentType: ComponentType.StringSelect, time: 60_000, - filter: (i: any) => i.user.id === interaction.user.id + filter: (i) => i.user.id === interaction.user.id }); - const buttonCollector = (msg as any).createMessageComponentCollector({ + const buttonCollector = replyInteraction.resource.message.createMessageComponentCollector({ componentType: ComponentType.Button, time: 60_000, - filter: (i: any) => i.user.id === interaction.user.id + filter: (i) => i.user.id === interaction.user.id }); - collector.on('collect', async (i: any) => { + collector.on('collect', async (i) => { const chosenId = i.values[0]; const chosen = reminders.find(r => (r as any)._id?.toString() === chosenId); if (!chosen) { @@ -174,11 +185,12 @@ export default class extends Command { const disabledMenu = i.message.components?.[0] ?? null; if (disabledMenu) { // clone row & disable the select - const row = ActionRowBuilder.from(disabledMenu) as ActionRowBuilder; + const row = ActionRowBuilder.from(disabledMenu as any /* TODO fix this */) as ActionRowBuilder; const select = StringSelectMenuBuilder.from(row.components[0] as any).setDisabled(true); row.setComponents(select); return i.update({ content: `Are you sure you want to cancel:\n> **${chosen.content}**`, + // eslint-disable-next-line no-extra-parens -- nullish coalescing must occur before spread operator components: [row, ...(i.message.components?.slice(1) ?? []), confirmRow] }); } @@ -190,7 +202,7 @@ export default class extends Command { }); - buttonCollector.on('collect', async (i: any) => { + buttonCollector.on('collect', async (i) => { try { if (i.customId === 'cancel') { return i.update({ content: 'Okay, leaving your reminders as-is.', components: [] }); @@ -215,7 +227,7 @@ export default class extends Command { components: [] }); } - const hidden = target?.mode === 'private'; + // const hidden = target?.mode === 'private'; return i.update({ content: `Canceled reminder: **${target?.content ?? '(unknown)'}**`, components: [] @@ -223,7 +235,7 @@ export default class extends Command { }); } } catch (err) { - return i.reply({ content: 'Unexpected error while handling your action.', ephemeral: true }); + return i.reply({ content: 'Unexpected error while handling your action.', flags: MessageFlags.Ephemeral }); } }); } From 7f2352f7e1850735f5371863b91e8cff5f4e3036 Mon Sep 17 00:00:00 2001 From: Matthew Meredith Date: Mon, 1 Dec 2025 18:53:11 -0500 Subject: [PATCH 5/7] fix typing from DB since we expect the mongo _id field --- src/commands/reminders/cancelreminder.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/commands/reminders/cancelreminder.ts b/src/commands/reminders/cancelreminder.ts index 536ce3ff..5bbf716f 100644 --- a/src/commands/reminders/cancelreminder.ts +++ b/src/commands/reminders/cancelreminder.ts @@ -16,6 +16,7 @@ import { Command } from '@lib/types/Command'; import { ObjectId } from 'mongodb'; const PAGE_SIZE = 25; +type DbReminder = Reminder & { _id: ObjectId }; export default class extends Command { description = 'Cancel any pending reminders you may have.'; @@ -69,7 +70,7 @@ export default class extends Command { const idOpt = interaction.options.getString('id'); const pageOpt = interaction.options.getInteger('page') ?? 1; - const coll = interaction.client.mongo.collection(DB.REMINDERS); + const coll = interaction.client.mongo.collection(DB.REMINDERS); const reminders = await coll.find({ owner: interaction.user.id }).toArray(); // Sort by soonest first for a stable, predictable order @@ -95,7 +96,7 @@ export default class extends Command { }); } - const toDelete = reminders.find(r => (r as any)._id?.toString() === objId.toString()); + const toDelete = reminders.find(r => r._id.toString() === objId.toString()); if (!toDelete) { return interaction.reply({ content: 'I couldn’t find a reminder with that ID. Use `/viewremind` or the dropdown instead.', @@ -124,7 +125,7 @@ export default class extends Command { } const reminder = reminders[index]; const delRes = await coll.deleteOne({ - _id: (reminder as any)._id, + _id: reminder._id, owner: interaction.user.id }); if (delRes.deletedCount !== 1) { @@ -164,7 +165,7 @@ export default class extends Command { collector.on('collect', async (i) => { const chosenId = i.values[0]; - const chosen = reminders.find(r => (r as any)._id?.toString() === chosenId); + const chosen = reminders.find(r => r._id.toString() === chosenId); if (!chosen) { // since the collector is tied to this message, use update/deferUpdate patterns return i.update({ content: 'That reminder no longer exists.', components: [] }); @@ -218,7 +219,7 @@ export default class extends Command { if (i.customId.startsWith('confirm:')) { const idStr = i.customId.split(':')[1]; const objId = new ObjectId(idStr); - const target = reminders.find(r => (r as any)._id?.toString() === idStr); + const target = reminders.find(r => r._id.toString() === idStr); const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); if (delRes.deletedCount !== 1) { @@ -240,7 +241,7 @@ export default class extends Command { }); } - private buildSelect(reminders: Reminder[], page: number, totalPages: number) { + private buildSelect(reminders: DbReminder[], page: number, totalPages: number) { const start = (page - 1) * PAGE_SIZE; const slice = reminders.slice(start, start + PAGE_SIZE); @@ -252,7 +253,7 @@ export default class extends Command { ...slice.map((r, idx) => ({ label: this.truncate(r.content, 100), description: `Due ${new Date(r.expires).toLocaleString()} • #${start + idx + 1}`, - value: (r as any)._id.toString() + value: r._id.toString() })) ) ); From 281b6851e576bdd2dd5fa513f4bf4d51ca9ffbae Mon Sep 17 00:00:00 2001 From: Matthew Meredith Date: Mon, 8 Dec 2025 00:03:31 -0500 Subject: [PATCH 6/7] magic --- src/commands/jobs/interview.ts | 1 + src/commands/reminders/cancelreminder.ts | 82 +++++++++++------------- 2 files changed, 37 insertions(+), 46 deletions(-) diff --git a/src/commands/jobs/interview.ts b/src/commands/jobs/interview.ts index 85ce23ff..5a0f2bc4 100644 --- a/src/commands/jobs/interview.ts +++ b/src/commands/jobs/interview.ts @@ -142,6 +142,7 @@ export default class extends Command { export async function handleInterviewOptionSelect( i: StringSelectMenuInteraction ): Promise { + if (i.customId !== 'interview_job_select') return; // get the dropdown selection const choice = i.values?.[0]; if (!choice) { diff --git a/src/commands/reminders/cancelreminder.ts b/src/commands/reminders/cancelreminder.ts index 5bbf716f..0802addf 100644 --- a/src/commands/reminders/cancelreminder.ts +++ b/src/commands/reminders/cancelreminder.ts @@ -1,5 +1,5 @@ -import { Reminder } from '@lib/types/Reminder'; -import { DB } from '@root/config'; +import {Reminder} from '@lib/types/Reminder'; +import {DB} from '@root/config'; import { ActionRowBuilder, ApplicationCommandOptionData, @@ -12,8 +12,8 @@ import { MessageFlags, StringSelectMenuBuilder } from 'discord.js'; -import { Command } from '@lib/types/Command'; -import { ObjectId } from 'mongodb'; +import {Command} from '@lib/types/Command'; +import {ObjectId} from 'mongodb'; const PAGE_SIZE = 25; type DbReminder = Reminder & { _id: ObjectId }; @@ -45,33 +45,12 @@ export default class extends Command { ]; async run(interaction: ChatInputCommandInteraction): Promise | void> { - /* const remindNum = interaction.options.getInteger('remindernumber') - 1; - - const reminders: Array = await interaction.client.mongo.collection(DB.REMINDERS) - .find({ owner: interaction.user.id }).toArray(); - reminders.sort((a, b) => a.expires.valueOf() - b.expires.valueOf()); - const reminder = reminders[remindNum]; - - if (!reminder) { - interaction.reply({ - content: `I couldn't find reminder **${remindNum}**. Use the \`viewremind\` command to see your current reminders.`, - ephemeral: true - }); - } - - interaction.client.mongo.collection(DB.REMINDERS).findOneAndDelete(reminder); - - const hidden = reminder.mode === 'private'; - return interaction.reply({ - content: `Canceled reminder: **${reminder.content}**`, - ephemeral: hidden - }); */ const numberOpt = interaction.options.getInteger('remindernumber'); const idOpt = interaction.options.getString('id'); const pageOpt = interaction.options.getInteger('page') ?? 1; const coll = interaction.client.mongo.collection(DB.REMINDERS); - const reminders = await coll.find({ owner: interaction.user.id }).toArray(); + const reminders = await coll.find({owner: interaction.user.id}).toArray(); // Sort by soonest first for a stable, predictable order reminders.sort((a, b) => a.expires.valueOf() - b.expires.valueOf()); @@ -103,9 +82,12 @@ export default class extends Command { flags: MessageFlags.Ephemeral }); } - const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); + const delRes = await coll.deleteOne({_id: objId, owner: interaction.user.id}); if (delRes.deletedCount !== 1) { - return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', flags: MessageFlags.Ephemeral }); + return interaction.reply({ + content: 'Hmm, I couldn’t delete that. Try again.', + flags: MessageFlags.Ephemeral + }); } const hidden = toDelete.mode === 'private'; return interaction.reply({ @@ -129,7 +111,10 @@ export default class extends Command { owner: interaction.user.id }); if (delRes.deletedCount !== 1) { - return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', flags: MessageFlags.Ephemeral }); + return interaction.reply({ + content: 'Hmm, I couldn’t delete that. Try again.', + flags: MessageFlags.Ephemeral + }); } const hidden = reminder.mode === 'private'; return interaction.reply({ @@ -141,7 +126,7 @@ export default class extends Command { // 3) Friendly path: interactive select menu (paginated) const totalPages = Math.max(1, Math.ceil(reminders.length / PAGE_SIZE)); const page = Math.min(Math.max(1, pageOpt), totalPages); - const { menu, navRow } = this.buildSelect(reminders, page, totalPages); + const {menu, navRow} = this.buildSelect(reminders, page, totalPages); const replyInteraction = await interaction.reply({ content: `Pick a reminder to cancel (page ${page}/${totalPages}):`, @@ -168,7 +153,7 @@ export default class extends Command { const chosen = reminders.find(r => r._id.toString() === chosenId); if (!chosen) { // since the collector is tied to this message, use update/deferUpdate patterns - return i.update({ content: 'That reminder no longer exists.', components: [] }); + return i.update({ content: 'That reminder no longer exists.', components: []}); } const confirmRow = new ActionRowBuilder().addComponents( @@ -184,16 +169,18 @@ export default class extends Command { // optionally disable the select so users can't pick twice const disabledMenu = i.message.components?.[0] ?? null; - if (disabledMenu) { - // clone row & disable the select - const row = ActionRowBuilder.from(disabledMenu as any /* TODO fix this */) as ActionRowBuilder; - const select = StringSelectMenuBuilder.from(row.components[0] as any).setDisabled(true); - row.setComponents(select); - return i.update({ - content: `Are you sure you want to cancel:\n> **${chosen.content}**`, - // eslint-disable-next-line no-extra-parens -- nullish coalescing must occur before spread operator - components: [row, ...(i.message.components?.slice(1) ?? []), confirmRow] - }); + if (disabledMenu && disabledMenu.type === ComponentType.ActionRow && disabledMenu.components.length > 0) { + const select = disabledMenu.components[0]; + if (select.customId === 'reminder-select' && select.type === ComponentType.StringSelect) { + const row = new ActionRowBuilder().addComponents( + StringSelectMenuBuilder.from(select).setDisabled(true) + ); + return i.update({ + content: `Are you sure you want to cancel:\n> **${chosen.content}**`, + // eslint-disable-next-line no-extra-parens -- nullish coalescing must occur before spread operator + components: [row, ...(i.message.components?.slice(1) ?? []), confirmRow] + }); + } } return i.update({ @@ -206,11 +193,11 @@ export default class extends Command { buttonCollector.on('collect', async (i) => { try { if (i.customId === 'cancel') { - return i.update({ content: 'Okay, leaving your reminders as-is.', components: [] }); + return i.update({content: 'Okay, leaving your reminders as-is.', components: []}); } if (i.customId.startsWith('page:')) { const nextPage = Number(i.customId.split(':')[1]); - const { menu: nextMenu, navRow: nextNav } = this.buildSelect(reminders, nextPage, totalPages); + const {menu: nextMenu, navRow: nextNav} = this.buildSelect(reminders, nextPage, totalPages); return i.update({ content: `Pick a reminder to cancel (page ${nextPage}/${totalPages}):`, components: nextNav ? [nextMenu, nextNav] : [nextMenu] @@ -220,7 +207,7 @@ export default class extends Command { const idStr = i.customId.split(':')[1]; const objId = new ObjectId(idStr); const target = reminders.find(r => r._id.toString() === idStr); - const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); + const delRes = await coll.deleteOne({_id: objId, owner: interaction.user.id}); if (delRes.deletedCount !== 1) { return i.update({ @@ -236,7 +223,10 @@ export default class extends Command { }); } } catch (err) { - return i.reply({ content: 'Unexpected error while handling your action.', flags: MessageFlags.Ephemeral }); + return i.reply({ + content: 'Unexpected error while handling your action.', + flags: MessageFlags.Ephemeral + }); } }); } @@ -273,7 +263,7 @@ export default class extends Command { .setDisabled(page >= totalPages) ); } - return { menu, navRow }; + return {menu, navRow}; } private truncate(str: string, len: number) { From c936b1412e0b99ecfb5898da58dcc6b483ae0fa3 Mon Sep 17 00:00:00 2001 From: Matthew Meredith Date: Mon, 8 Dec 2025 00:06:44 -0500 Subject: [PATCH 7/7] mmmmm eslint --- src/commands/reminders/cancelreminder.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/commands/reminders/cancelreminder.ts b/src/commands/reminders/cancelreminder.ts index 0802addf..0cb29f44 100644 --- a/src/commands/reminders/cancelreminder.ts +++ b/src/commands/reminders/cancelreminder.ts @@ -1,5 +1,5 @@ -import {Reminder} from '@lib/types/Reminder'; -import {DB} from '@root/config'; +import { Reminder } from '@lib/types/Reminder'; +import { DB } from '@root/config'; import { ActionRowBuilder, ApplicationCommandOptionData, @@ -12,8 +12,8 @@ import { MessageFlags, StringSelectMenuBuilder } from 'discord.js'; -import {Command} from '@lib/types/Command'; -import {ObjectId} from 'mongodb'; +import { Command } from '@lib/types/Command'; +import { ObjectId } from 'mongodb'; const PAGE_SIZE = 25; type DbReminder = Reminder & { _id: ObjectId }; @@ -50,7 +50,7 @@ export default class extends Command { const pageOpt = interaction.options.getInteger('page') ?? 1; const coll = interaction.client.mongo.collection(DB.REMINDERS); - const reminders = await coll.find({owner: interaction.user.id}).toArray(); + const reminders = await coll.find({ owner: interaction.user.id }).toArray(); // Sort by soonest first for a stable, predictable order reminders.sort((a, b) => a.expires.valueOf() - b.expires.valueOf()); @@ -82,7 +82,7 @@ export default class extends Command { flags: MessageFlags.Ephemeral }); } - const delRes = await coll.deleteOne({_id: objId, owner: interaction.user.id}); + const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); if (delRes.deletedCount !== 1) { return interaction.reply({ content: 'Hmm, I couldn’t delete that. Try again.', @@ -126,7 +126,7 @@ export default class extends Command { // 3) Friendly path: interactive select menu (paginated) const totalPages = Math.max(1, Math.ceil(reminders.length / PAGE_SIZE)); const page = Math.min(Math.max(1, pageOpt), totalPages); - const {menu, navRow} = this.buildSelect(reminders, page, totalPages); + const { menu, navRow } = this.buildSelect(reminders, page, totalPages); const replyInteraction = await interaction.reply({ content: `Pick a reminder to cancel (page ${page}/${totalPages}):`, @@ -153,7 +153,7 @@ export default class extends Command { const chosen = reminders.find(r => r._id.toString() === chosenId); if (!chosen) { // since the collector is tied to this message, use update/deferUpdate patterns - return i.update({ content: 'That reminder no longer exists.', components: []}); + return i.update({ content: 'That reminder no longer exists.', components: [] }); } const confirmRow = new ActionRowBuilder().addComponents( @@ -193,11 +193,11 @@ export default class extends Command { buttonCollector.on('collect', async (i) => { try { if (i.customId === 'cancel') { - return i.update({content: 'Okay, leaving your reminders as-is.', components: []}); + return i.update({ content: 'Okay, leaving your reminders as-is.', components: [] }); } if (i.customId.startsWith('page:')) { const nextPage = Number(i.customId.split(':')[1]); - const {menu: nextMenu, navRow: nextNav} = this.buildSelect(reminders, nextPage, totalPages); + const { menu: nextMenu, navRow: nextNav } = this.buildSelect(reminders, nextPage, totalPages); return i.update({ content: `Pick a reminder to cancel (page ${nextPage}/${totalPages}):`, components: nextNav ? [nextMenu, nextNav] : [nextMenu] @@ -207,7 +207,7 @@ export default class extends Command { const idStr = i.customId.split(':')[1]; const objId = new ObjectId(idStr); const target = reminders.find(r => r._id.toString() === idStr); - const delRes = await coll.deleteOne({_id: objId, owner: interaction.user.id}); + const delRes = await coll.deleteOne({ _id: objId, owner: interaction.user.id }); if (delRes.deletedCount !== 1) { return i.update({ @@ -263,7 +263,7 @@ export default class extends Command { .setDisabled(page >= totalPages) ); } - return {menu, navRow}; + return { menu, navRow }; } private truncate(str: string, len: number) {