diff --git a/package.json b/package.json index a36735a..772b0f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "muffinbot", - "version": "4.0.0-pudding.d240928e", + "version": "4.0.0-pudding.e241001a", "main": "dist/index.js", "private": true, "dependencies": { diff --git a/src/Client.ts b/src/Client.ts index 80c9289..074b4a8 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -22,7 +22,7 @@ container.version = version container.database = new PrismaClient() container.dokdoAliases = ['dokdo', 'dok', 'Dokdo', 'Dok', '테스트'] container.chatBot = new ChatBot(container.database) -container.lastUpdated = new Date('2024-09-28') +container.lastUpdated = new Date('2024-10-01') if (release.startsWith('e')) { container.channel = 'EXPERIMENTAL' diff --git a/src/Commands/deleteLearn.ts b/src/Commands/deleteLearn.ts index f1748e1..6d893ad 100644 --- a/src/Commands/deleteLearn.ts +++ b/src/Commands/deleteLearn.ts @@ -1,16 +1,18 @@ +import { ApplyOptions } from '@sapphire/decorators' +import { + type SelectMenuComponentOptionData, + type User, + ChatInputCommandInteraction, + ComponentType, + codeBlock, + Message, +} from 'discord.js' import { Args, Command, container, DetailedDescriptionCommandObject, } from '@sapphire/framework' -import { - type SelectMenuComponentOptionData, - type Message, - ComponentType, - codeBlock, -} from 'discord.js' -import { ApplyOptions } from '@sapphire/decorators' @ApplyOptions({ name: '삭제', @@ -22,13 +24,36 @@ import { ApplyOptions } from '@sapphire/decorators' }, }) class DeleteLearnCommand extends Command { - public async messageRun(msg: Message, args: Args) { + public registerApplicationCommands(registry: Command.Registry) { + registry.registerChatInputCommand(builder => + builder + .setName(this.name) + .setDescription(this.description) + .addStringOption(option => + option + .setRequired(true) + .setName('단어') + .setDescription('삭제할 단어를 입력해주세요.'), + ), + ) + } + + private async _run(ctx: Message | ChatInputCommandInteraction, args?: Args) { + let command: string | null + let user: User + if (ctx instanceof Message) { + command = await args!.rest('string').catch(() => null) + user = ctx.author + } else { + command = ctx.options.getString('단어', true) + user = ctx.user + } + const CUSTOM_ID = 'maa$deleteLearn' - const command = await args.rest('string').catch(() => null) const options: SelectMenuComponentOptionData[] = [] const deleteDataList: string[] = [] if (!command) { - return await msg.reply( + return await ctx.reply( `사용법: \n\`\`\`${(this.detailedDescription as DetailedDescriptionCommandObject).usage}\`\`\``, ) } @@ -36,12 +61,12 @@ class DeleteLearnCommand extends Command { const deleteDatas = await this.container.database.learn.findMany({ where: { command, - user_id: msg.author.id, + user_id: user.id, }, }) if (!deleteDatas) { - return await msg.reply('해당하는 걸 찾ㅈ을 수 없어요.') + return await ctx.reply('해당하는 걸 찾ㅈ을 수 없어요.') } for (let i = 1; i <= deleteDatas.length; i++) { @@ -53,7 +78,7 @@ class DeleteLearnCommand extends Command { }) } - await msg.reply({ + await ctx.reply({ embeds: [ { title: '삭제', @@ -67,7 +92,7 @@ class DeleteLearnCommand extends Command { components: [ { type: ComponentType.StringSelect, - customId: `${CUSTOM_ID}@${msg.author.id}`, + customId: `${CUSTOM_ID}@${user.id}`, placeholder: '지울 데이터를 선택해ㅈ주세요', options, }, @@ -76,6 +101,14 @@ class DeleteLearnCommand extends Command { ], }) } + + public async messageRun(msg: Message, args: Args) { + await this._run(msg, args) + } + + public async chatInputRun(interaction: ChatInputCommandInteraction) { + await this._run(interaction) + } } void container.stores.loadPiece({ diff --git a/src/Commands/help.ts b/src/Commands/help.ts index ea228a6..9ac7e35 100644 --- a/src/Commands/help.ts +++ b/src/Commands/help.ts @@ -1,6 +1,10 @@ import { Args, Command, container } from '@sapphire/framework' -import { codeBlock, type Message } from 'discord.js' import { ApplyOptions } from '@sapphire/decorators' +import { + type ChatInputCommandInteraction, + codeBlock, + Message, +} from 'discord.js' @ApplyOptions({ name: '도움말', @@ -12,8 +16,32 @@ import { ApplyOptions } from '@sapphire/decorators' }, }) class HelpCommand extends Command { - public async messageRun(msg: Message, args: Args) { - const commandName = await args.pick('string').catch(() => null) + public registerApplicationCommands(registry: Command.Registry) { + const commands = this.container.stores.get('commands').map(command => { + return { + name: command.name, + value: command.name, + } + }) + registry.registerChatInputCommand(builder => + builder + .setName(this.name) + .setDescription(this.description) + .addStringOption(option => + option + .setName('명령어') + .setDescription('해당 명령어에 대ㅎ한 도움말을 볼 수 있어요.') + .addChoices(commands), + ), + ) + } + private async _run(ctx: Message | ChatInputCommandInteraction, args?: Args) { + let commandName: string | null + if (ctx instanceof Message) { + commandName = await args!.pick('string').catch(() => null) + } else { + commandName = ctx.options.getString('명령어') + } if ( !commandName || !this.container.stores.get('commands').get(commandName) @@ -24,7 +52,7 @@ class HelpCommand extends Command { commandList.push(`${module.name} - ${module.description}`) }) - await msg.reply({ + await ctx.reply({ embeds: [ { title: `${this.container.client.user?.username}의 도움말`, @@ -44,7 +72,7 @@ class HelpCommand extends Command { this.container.stores.get('commands').get(commandName)! if (typeof detailedDescription === 'string') return - await msg.reply({ + await ctx.reply({ embeds: [ { title: `${this.container.client.user?.username}의 도움말`, @@ -86,6 +114,13 @@ class HelpCommand extends Command { }) } } + public async messageRun(msg: Message, args: Args) { + await this._run(msg, args) + } + + public async chatInputRun(interaction: ChatInputCommandInteraction) { + await this._run(interaction) + } } void container.stores.loadPiece({ diff --git a/src/Commands/information.ts b/src/Commands/information.ts index 5fce6f7..8061751 100644 --- a/src/Commands/information.ts +++ b/src/Commands/information.ts @@ -1,6 +1,6 @@ -import { ApplyOptions } from '@sapphire/decorators' +import type { ChatInputCommandInteraction, Message } from 'discord.js' import { Command, container } from '@sapphire/framework' -import { Message } from 'discord.js' +import { ApplyOptions } from '@sapphire/decorators' import { platform, arch } from 'os' @ApplyOptions({ @@ -11,8 +11,14 @@ import { platform, arch } from 'os' }, }) class InformationCommand extends Command { - public async messageRun(msg: Message) { - await msg.reply({ + public registerApplicationCommands(registry: Command.Registry) { + registry.registerChatInputCommand(builder => + builder.setName(this.name).setDescription(this.description), + ) + } + + private async _run(ctx: Message | ChatInputCommandInteraction) { + await ctx.reply({ embeds: [ { title: `${this.container.client.user?.username}의 정ㅂ보`, @@ -56,6 +62,14 @@ class InformationCommand extends Command { ], }) } + + public async messageRun(msg: Message) { + await this._run(msg) + } + + public async chatInputRun(interaction: ChatInputCommandInteraction) { + await this._run(interaction) + } } void container.stores.loadPiece({ diff --git a/src/Commands/learn.ts b/src/Commands/learn.ts index 1f0879d..2e2bc69 100644 --- a/src/Commands/learn.ts +++ b/src/Commands/learn.ts @@ -1,5 +1,5 @@ +import { ChatInputCommandInteraction, codeBlock, Message } from 'discord.js' import { type Args, Command, container } from '@sapphire/framework' -import { codeBlock, type Message } from 'discord.js' import { ApplyOptions } from '@sapphire/decorators' @ApplyOptions({ @@ -16,32 +16,69 @@ import { ApplyOptions } from '@sapphire/decorators' }, }) class LearnCommand extends Command { - public async messageRun(msg: Message, args: Args) { - if (typeof this.detailedDescription === 'string') return - const config = this.container.config - const command = (await args.pick('string').catch(() => null))?.replaceAll( - '_', - ' ', - ) - const result = (await args.pick('string').catch(() => null))?.replaceAll( - '_', - ' ', - ) - if (!command || !result) { - return await msg.reply( - codeBlock( - 'md', - `사용법: ${this.detailedDescription} - 예시: ${this.detailedDescription.examples?.map(example => example).join('\n')}`, + public registerApplicationCommands(registry: Command.Registry) { + registry.registerChatInputCommand(builder => + builder + .setName(this.name) + .setDescription(this.description) + .addStringOption(option => + option + .setRequired(true) + .setName('단어') + .setDescription('등록할 단어를 입력해주세요.'), + ) + .addStringOption(option => + option + .setRequired(true) + .setName('대답') + .setDescription('해당 단어의 대답을 입력해주세요.'), ), + ) + } + + private async _run(ctx: Message | ChatInputCommandInteraction, args?: Args) { + if (ctx instanceof ChatInputCommandInteraction) ctx.deferReply() + if (typeof this.detailedDescription === 'string') return + + const config = this.container.config + const IG_MSG = '해ㄷ당 단어는 배울ㄹ 수 없어요.' + const DI_MSG = '해당 단ㅇ어는 개발자님이 특별히 금지하였ㅇ어요.' + const SUCCESS_MSG = '을/를 배웠ㅇ어요.' + + let command: string + let result: string + + if (ctx instanceof Message) { + command = (await args!.pick('string').catch(() => null))!.replaceAll( + '_', + ' ', ) + result = (await args!.pick('string').catch(() => null))!.replaceAll( + '_', + ' ', + ) + + if (!command || !result) + return await ctx.reply( + codeBlock( + 'md', + `사용법: ${this.detailedDescription} + 예시: ${this.detailedDescription.examples?.map(example => example).join('\n')}`, + ), + ) + } else { + command = ctx.options.getString('단어', true) + result = ctx.options.getString('대답', true) } + const commands: string[] = [] const aliases: string[] = [] + this.container.stores.get('commands').forEach(module => { commands.push(module.name) module.aliases.forEach(alias => aliases.push(alias)) }) + const ignore = [ ...commands, ...aliases, @@ -52,27 +89,41 @@ class LearnCommand extends Command { '간미', ] const disallowed = ['@everyone', '@here', `<@${config.bot.owner_ID}>`] + const user = ctx instanceof Message ? ctx.author : ctx.user for (const ig of ignore) { - if (command.includes(ig)) { - return msg.reply('해ㄷ당 단어는 배울ㄹ 수 없어요.') - } + if (command.includes(ig)) + return ctx instanceof Message + ? await ctx.reply(IG_MSG) + : await ctx.editReply(IG_MSG) } for (const di of disallowed) { - if (result.includes(di)) { - return msg.reply('해당 단ㅇ어는 개발자님이 특별히 금지하였ㅇ어요.') - } + if (result.includes(di)) + return ctx instanceof Message + ? await ctx.reply(DI_MSG) + : await ctx.editReply(DI_MSG) } await this.container.database.learn.create({ data: { - user_id: msg.author.id, + user_id: user.id, command, result, }, }) - await msg.reply(`${command}을/를 배웠ㅇ어요.`) + + return ctx instanceof Message + ? await ctx.reply(command + SUCCESS_MSG) + : await ctx.editReply(command + SUCCESS_MSG) + } + + public async messageRun(msg: Message, args: Args) { + await this._run(msg, args) + } + + public async chatInputRun(interaction: ChatInputCommandInteraction) { + await this._run(interaction) } } diff --git a/src/Commands/learning_data.ts b/src/Commands/learning_data.ts index bc4e8fe..cd3c5f5 100644 --- a/src/Commands/learning_data.ts +++ b/src/Commands/learning_data.ts @@ -1,6 +1,6 @@ +import { ChatInputCommandInteraction, Message } from 'discord.js' import { Command, container } from '@sapphire/framework' import { ApplyOptions } from '@sapphire/decorators' -import { type Message } from 'discord.js' @ApplyOptions({ name: '데이터학습량', @@ -11,14 +11,21 @@ import { type Message } from 'discord.js' }, }) class LearnDataCommand extends Command { - public async messageRun(msg: Message) { + public registerApplicationCommands(registry: Command.Registry) { + registry.registerChatInputCommand(builder => + builder.setName(this.name).setDescription(this.description), + ) + } + + private async _run(ctx: Message | ChatInputCommandInteraction) { + const user = ctx instanceof Message ? ctx.author : ctx.user const db = this.container.database const data = await db.statement.findMany() const nsfwData = await db.nsfw_content.findMany() const learnData = await db.learn.findMany() const userData = await db.learn.findMany({ where: { - user_id: msg.author.id, + user_id: user.id, }, }) const muffin: any[] = [] @@ -27,10 +34,18 @@ class LearnDataCommand extends Command { else return }) - await msg.reply(`머핀 데이터: ${muffin.length}개 + await ctx.reply(`머핀 데이터: ${muffin.length}개 nsfw 데이터: ${nsfwData.length}개 지금까지 배운 단어: ${learnData.length}개 -${msg.author.username}님이 가르쳐준 단어: ${userData.length}개`) +${user.username}님이 가르쳐준 단어: ${userData.length}개`) + } + + public async messageRun(msg: Message) { + await this._run(msg) + } + + public async chatInputRun(interaction: ChatInputCommandInteraction) { + await this._run(interaction) } } diff --git a/src/Commands/list.ts b/src/Commands/list.ts index 4e18417..6f0eaa3 100644 --- a/src/Commands/list.ts +++ b/src/Commands/list.ts @@ -1,5 +1,5 @@ +import { ChatInputCommandInteraction, Message, codeBlock } from 'discord.js' import { ApplyOptions } from '@sapphire/decorators' -import { Message, codeBlock } from 'discord.js' import { Command, container } from '@sapphire/framework' @ApplyOptions({ @@ -11,27 +11,34 @@ import { Command, container } from '@sapphire/framework' }, }) class ListCommand extends Command { - public async messageRun(msg: Message) { + public registerApplicationCommands(registry: Command.Registry) { + registry.registerChatInputCommand(builder => + builder.setName(this.name).setDescription(this.description), + ) + } + + private async _run(ctx: Message | ChatInputCommandInteraction) { + const user = ctx instanceof Message ? ctx.author : ctx.user const db = this.container.database const data = await db.learn.findMany({ where: { - user_id: msg.author.id, + user_id: user.id, }, }) const list: string[] = [] if (!data[0]) { - return await msg.reply('당신ㄴ은 단어를 가르쳐준 기억이 없ㅅ는데요.') + return await ctx.reply('당신ㄴ은 단어를 가르쳐준 기억이 없ㅅ는데요.') } for (const listData of data) { list.push(listData.command) } - await msg.reply({ + await ctx.reply({ embeds: [ { - title: `${msg.author.username}님의 지식`, + title: `${user.username}님의 지식`, description: `총합: ${data.length}개\n${codeBlock( 'md', list.map(item => `- ${item}`).join('\n'), @@ -42,6 +49,14 @@ class ListCommand extends Command { ], }) } + + public async messageRun(msg: Message) { + await this._run(msg) + } + + public async chatInputRun(interaction: ChatInputCommandInteraction) { + await this._run(interaction) + } } void container.stores.loadPiece({