feat: Add deleteLearnData

This commit is contained in:
Siwoo Jeon 2025-03-07 21:27:08 +09:00
parent c5211c06d4
commit 78f2da5646
Signed by: migan
GPG key ID: 036E9A8C5E8E48DA
3 changed files with 242 additions and 0 deletions

View file

@ -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<Command.Options>({
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<true>, args: Args) {
return await this._run(msg, args)
}
public async chatInputRun(
interaction: ChatInputCommandInteraction<'cached'>,
) {
return await this._run(interaction)
}
private async _run(
ctx: Message<true> | 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<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder()
.setCustomId(MuffinCustomId.DeleteLearnedDataUserId + userId)
.addOptions(options)
.setPlaceholder('ㅈ지울 응답을 선택해주세요.'),
),
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId(MuffinCustomId.DeleteLearnedDataCancel + userId)
.setLabel('취소하기')
.setStyle(ButtonStyle.Danger),
),
],
})
}
}

View file

@ -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<InteractionHandler.Options>({
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: [],
})
}
}

5
src/lib/customId.ts Normal file
View file

@ -0,0 +1,5 @@
export enum MuffinCustomId {
DeleteLearnedData = '#muffin/deleteLearnedData$',
DeleteLearnedDataUserId = '#muffin/deleteLearnedData@',
DeleteLearnedDataCancel = '#muffin/deleteLearnedData/cancel@',
}