diff --git a/src/commands/deleteLearnedData.ts b/src/commands/deleteLearnedData.ts new file mode 100644 index 0000000..4dc3c87 --- /dev/null +++ b/src/commands/deleteLearnedData.ts @@ -0,0 +1,148 @@ +import { MuffinCustomId } from '../lib/customId' +import { Learn } from '../lib/databases' +import { ApplyOptions } from '@sapphire/decorators' +import { Args, Command } from '@sapphire/framework' +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + ChatInputCommandInteraction, + codeBlock, + EmbedBuilder, + Message, + MessageFlags, + StringSelectMenuBuilder, + StringSelectMenuOptionBuilder, +} from 'discord.js' + +@ApplyOptions({ + name: '삭제', + aliases: ['잊어', '지워'], + description: '당신이 가르쳐준 단ㅇ어를 삭제해요.', + detailedDescription: { + usage: '머핀아 삭제 (삭제할 단어)', + examples: ['머핀아 삭제 머핀'], + }, +}) +export default class DeleteLearnedData extends Command { + public registerApplicationCommand(registry: Command.Registry) { + registry.registerChatInputCommand(builder => + builder + .setName(this.name) + .setDescription(this.description) + .addStringOption(option => + option + .setName('단어') + .setDescription('삭제할 단어를 입ㄹ력해주세요.') + .setRequired(true), + ), + ) + } + + public async messageRun(msg: Message, args: Args) { + return await this._run(msg, args) + } + + public async chatInputRun( + interaction: ChatInputCommandInteraction<'cached'>, + ) { + return await this._run(interaction) + } + + private async _run( + ctx: Message | ChatInputCommandInteraction<'cached'>, + args?: Args, + ) { + let command: string | undefined + let userId: string + + if (typeof this.detailedDescription === 'string') return + if (ctx instanceof Message) { + if (!args) return + command = await args.rest('string').catch(() => undefined) + userId = ctx.author.id + } else { + command = ctx.options.getString('단어', true) + userId = ctx.user.id + } + + if (!command) { + // 오직 메세지 커맨드로 사용하였을 때만 표출되는 오류 메세지 + return await ctx.reply({ + embeds: [ + new EmbedBuilder() + .setTitle('❌ 오류') + .setDescription('올바르지 않ㅇ은 용법이에요.') + .addFields( + { + name: '사용법', + value: `\`${this.detailedDescription.usage}\``, + }, + { + name: '예시', + value: this.detailedDescription + .examples!.map(example => `\`${example}\``) + .join('\n'), + }, + ) + .setColor(this.container.embedColors.fail), + ], + }) + } + + const datas = await Learn.find({ + command, + user_id: userId, + }) + + if (!datas.length) + return await ctx.reply({ + embeds: [ + new EmbedBuilder() + .setTitle('❌ 오류') + .setDescription('해당 하는 지식ㅇ을 찾을 수 없어요.') + .setColor(this.container.embedColors.fail), + ], + }) + + const options: StringSelectMenuOptionBuilder[] = [] + let description = '' + + for (let i = 1; i <= datas.length; i++) { + const data = datas[i - 1] + options.push( + new StringSelectMenuOptionBuilder() + .setLabel(`${i}번 지식`) + .setDescription(data.result) + .setValue(MuffinCustomId.DeleteLearnedData + data.id + `&No.${i}`), + ) + description += `${i}. ${data.result}\n` + } + + return await ctx.reply({ + embeds: [ + new EmbedBuilder() + .setTitle(`${command} 삭제`) + .setDescription( + `${command}에 대한 대답 중 하나를 선ㅌ택하여 삭제해주세요.\n` + + codeBlock('md', description), + ) + .setColor(this.container.embedColors.default), + ], + components: [ + new ActionRowBuilder().addComponents( + new StringSelectMenuBuilder() + .setCustomId(MuffinCustomId.DeleteLearnedDataUserId + userId) + .addOptions(options) + .setPlaceholder('ㅈ지울 응답을 선택해주세요.'), + ), + new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId(MuffinCustomId.DeleteLearnedDataCancel + userId) + .setLabel('취소하기') + .setStyle(ButtonStyle.Danger), + ), + ], + }) + } +} diff --git a/src/interaction-handlers/deleteLearnedData.ts b/src/interaction-handlers/deleteLearnedData.ts new file mode 100644 index 0000000..6442fa2 --- /dev/null +++ b/src/interaction-handlers/deleteLearnedData.ts @@ -0,0 +1,89 @@ +import { MuffinCustomId } from '../lib/customId' +import { Learn } from '../lib/databases' +import { ApplyOptions } from '@sapphire/decorators' +import { + InteractionHandler, + InteractionHandlerTypes, +} from '@sapphire/framework' +import { + EmbedBuilder, + MessageFlags, + type ButtonInteraction, + type StringSelectMenuInteraction, +} from 'discord.js' + +@ApplyOptions({ + interactionHandlerType: InteractionHandlerTypes.MessageComponent, +}) +export default class DeleteLearnedDataHandler extends InteractionHandler { + public async parse( + interaction: StringSelectMenuInteraction | ButtonInteraction, + ) { + const userId = interaction.isButton() + ? interaction.customId.slice( + MuffinCustomId.DeleteLearnedDataCancel.length, + ) + : interaction.customId.slice( + MuffinCustomId.DeleteLearnedDataUserId.length, + ) + + if (interaction.user.id !== userId) { + await interaction.reply({ + flags: [MessageFlags.Ephemeral], + embeds: [ + new EmbedBuilder() + .setTitle('❌ 오류') + .setDescription('당신은 해당 권한이 없ㅇ어요.') + .setColor(this.container.embedColors.fail), + ], + }) + } + if (interaction.isButton()) { + if ( + !interaction.customId.startsWith(MuffinCustomId.DeleteLearnedDataCancel) + ) + return this.none() + await interaction.update({ + embeds: [ + new EmbedBuilder() + .setTitle('❌ 취소') + .setDescription(`지식 삭제 작업ㅇ을 취소했어요.`) + .setColor(this.container.embedColors.fail), + ], + components: [], + }) + return this.none() + } + + if ( + !interaction.customId.startsWith(MuffinCustomId.DeleteLearnedDataUserId) + ) + return this.none() + + return this.some() + } + + public async run(interaction: StringSelectMenuInteraction) { + await interaction.deferUpdate() + + const itemIdRegexp = /No.\d+/ + + const id = interaction.values[0] + .slice(MuffinCustomId.DeleteLearnedData.length) + .replace(itemIdRegexp, '') + .replaceAll('&', '') + const itemId = interaction.values[0].match(itemIdRegexp)!.join() + + await Learn.findByIdAndDelete(id) + + return await interaction.editReply({ + embeds: [ + new EmbedBuilder() + .setTitle('✅ 삭제 완료') + .setDescription(`${itemId.slice(3)}번을 삭ㅈ제했어요.`) + .setColor(this.container.embedColors.success), + ], + components: [], + }) + } +} diff --git a/src/lib/customId.ts b/src/lib/customId.ts new file mode 100644 index 0000000..e9a6ca5 --- /dev/null +++ b/src/lib/customId.ts @@ -0,0 +1,5 @@ +export enum MuffinCustomId { + DeleteLearnedData = '#muffin/deleteLearnedData$', + DeleteLearnedDataUserId = '#muffin/deleteLearnedData@', + DeleteLearnedDataCancel = '#muffin/deleteLearnedData/cancel@', +}