diff --git a/bun.lock b/bun.lock index 911789b..a1f6b8b 100644 --- a/bun.lock +++ b/bun.lock @@ -11,6 +11,7 @@ "@sapphire/utilities": "^3.18.2", "discord-api-types": "^0.37.119", "discord.js": "^14.18.0", + "es-hangul": "^2.3.1", "mongoose": "^8.10.1", }, "devDependencies": { @@ -129,6 +130,8 @@ "discord.js": ["discord.js@14.18.0", "", { "dependencies": { "@discordjs/builders": "^1.10.1", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.0", "@discordjs/rest": "^2.4.3", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.1", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.37.119", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "tslib": "^2.6.3", "undici": "6.21.1" } }, "sha512-SvU5kVUvwunQhN2/+0t55QW/1EHfB1lp0TtLZUSXVHDmyHTrdOj5LRKdR0zLcybaA15F+NtdWuWmGOX9lE+CAw=="], + "es-hangul": ["es-hangul@2.3.1", "", {}, "sha512-somwJpQpVP5LLI6DquIvRnoTSqVyfIfT1a8/jxHRueWNiTse7/kJ0JZVpf4KrzhY8CoqEK0LUZXFhGkf1hBZKQ=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="], diff --git a/package.json b/package.json index b096b42..fcee70d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@sapphire/utilities": "^3.18.2", "discord-api-types": "^0.37.119", "discord.js": "^14.18.0", + "es-hangul": "^2.3.1", "mongoose": "^8.10.1" }, "scripts": { diff --git a/src/commands/learn.ts b/src/commands/learn.ts new file mode 100644 index 0000000..3131f3d --- /dev/null +++ b/src/commands/learn.ts @@ -0,0 +1,127 @@ +import { Learn } from '../lib/databases' +import { ApplyOptions } from '@sapphire/decorators' +import { Args, Command } from '@sapphire/framework' +import { ChatInputCommandInteraction, codeBlock, Message } from 'discord.js' +import { josa } from 'es-hangul' + +@ApplyOptions({ + name: '배워', + aliases: ['공부'], + description: '단어를 가르치는 명령ㅇ어에요.', + detailedDescription: { + usage: '머핀아 배워 (등록할 단어) (대답)', + examples: [ + '머핀아 배워 안녕 안녕!', + '머핀아 배워 "야 죽을래?" "아니요 ㅠㅠㅠ"', + '머핀아 배워 미간은_누구야? 이봇의_개발자요', + ], + }, +}) +export class LearnCommand extends Command { + 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('해당 단어의 대답을 입력해주세요.'), + ), + ) + } + + 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, + ) { + const userId = ctx instanceof Message ? ctx.author.id : ctx.user.id + let command: string | undefined + let result: string | undefined + + if (typeof this.detailedDescription === 'string') return + if (ctx instanceof ChatInputCommandInteraction) { + await ctx.deferReply() + + command = ctx.options.getString('단어', true) + result = ctx.options.getString('대답', true) + } else { + if (!args) return + command = (await args.pick('string').catch(() => undefined))?.replaceAll( + '_', + ' ', + ) + result = (await args.pick('string').catch(() => undefined))?.replaceAll( + '_', + ' ', + ) + } + + if (!command || !result) + return await ctx.reply( + codeBlock( + 'md', + `사용법: ${this.detailedDescription.usage}\n` + + `예시: ${this.detailedDescription.examples?.map(example => example).join('\n')}`, + ), + ) + + let commands: string[] = [] + let aliases: string[] = [] + + for (const [name, command] of this.container.stores.get('commands')) { + commands = [...commands, name] + aliases = [...aliases, ...command.aliases] + } + + const ignores = [...commands, ...aliases, '미간', 'Migan', 'migan', '간미'] + const disallows = [ + '@everyone', + '@here', + `<@${this.container.config.bot.ownerId}>`, + ] + + for (const ignore of ignores) { + if (command.includes(ignore)) + return ctx instanceof Message + ? await ctx.reply('해ㄷ당 단어는 배울ㄹ 수 없어요.') + : await ctx.editReply('해ㄷ당 단어는 배울ㄹ 수 없어요.') + } + + for (const disallowed of disallows) { + if (result.includes(disallowed)) + return ctx instanceof Message + ? await ctx.reply('해당 단ㅇ어는 개발자님이 특별히 금지하였ㅇ어요.') + : await ctx.editReply( + '해당 단ㅇ어는 개발자님이 특별히 금지하였ㅇ어요.', + ) + } + + await new Learn({ + command, + result, + user_id: userId, + }).save() + + return ctx instanceof Message + ? await ctx.reply(`${josa(command, '을/를')} 배웠ㅇ어요.`) + : await ctx.editReply(`${josa(command, '을/를')} 배웠ㅇ어요.`) + } +} diff --git a/src/init.ts b/src/init.ts index 020996e..3471114 100644 --- a/src/init.ts +++ b/src/init.ts @@ -11,6 +11,13 @@ declare module '@sapphire/pieces' { } } +declare module '@sapphire/framework' { + interface DetailedDescriptionCommandObject { + usage: string + examples?: string[] + } +} + container.dbDisconnect = async () => await disconnect() container.config = new Config() container.prefix = container.config.bot.prefix