From 68ff1429e1d10bdd8a3a2f8772af363ac91eed94 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sat, 28 Sep 2024 17:05:10 +0900 Subject: [PATCH 1/6] chore: Add script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 51eade9..51ab30d 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,8 @@ "dev": "cross-env NODE_ENV=development tsup --watch --onSuccess \"node --enable-source-maps dist\"", "start": "cross-env NODE_ENV=production node dist", "db:pull": "pnpify prisma db pull", - "db:push": "pnpify prisma db push" + "db:push": "pnpify prisma db push", + "db:generate": "pnpify prisma generate" }, "resolutions": { "@types/ws": "^8.5.11", From f4b479a9ca9cd9bf0238491b75e069fd68df53dc Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sat, 28 Sep 2024 16:48:08 +0900 Subject: [PATCH 2/6] feat(experimental): Testing slash command feature --- package.json | 2 +- src/Commands/information.ts | 98 +++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 51ab30d..d5e008c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "muffinbot", - "version": "4.0.0-pudding.d240928c", + "version": "4.0.0-pudding.experimental_slash_test.1", "main": "dist/index.js", "private": true, "dependencies": { diff --git a/src/Commands/information.ts b/src/Commands/information.ts index 5fce6f7..c602ce3 100644 --- a/src/Commands/information.ts +++ b/src/Commands/information.ts @@ -1,6 +1,6 @@ -import { ApplyOptions } from '@sapphire/decorators' +import { type ChatInputCommandInteraction, APIEmbed, 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,49 +11,63 @@ import { platform, arch } from 'os' }, }) class InformationCommand extends Command { - public async messageRun(msg: Message) { - await msg.reply({ - embeds: [ + public registerApplicationCommands(registry: Command.Registry) { + registry.registerChatInputCommand(builder => + builder.setName(this.name).setDescription(this.description), + ) + } + + private async _embed(): Promise { + return { + title: `${this.container.client.user?.username}의 정ㅂ보`, + fields: [ { - title: `${this.container.client.user?.username}의 정ㅂ보`, - fields: [ - { - name: '구동ㅎ환경', - value: `${platform()} ${arch()}`, - inline: false, - }, - { - name: '버ㅈ전', - value: this.container.version, - inline: true, - }, - { - name: '채ㄴ널', - value: this.container.channel.toLowerCase(), - inline: true, - }, - { - name: '최근 업ㄷ데이트 날짜', - value: this.container.lastUpdated.toLocaleDateString('ko', { - dateStyle: 'long', - }), - inline: true, - }, - { - name: '개ㅂ발자', - value: ( - await this.container.client.users.fetch( - this.container.config.bot.owner_ID, - ) - ).username, - inline: false, - }, - ], - thumbnail: { - url: this.container.client.user!.displayAvatarURL()!, - }, + name: '구동ㅎ환경', + value: `${platform()} ${arch()}`, + inline: false, + }, + { + name: '버ㅈ전', + value: this.container.version, + inline: true, + }, + { + name: '채ㄴ널', + value: this.container.channel.toLowerCase(), + inline: true, + }, + { + name: '최근 업ㄷ데이트 날짜', + value: this.container.lastUpdated.toLocaleDateString('ko', { + dateStyle: 'long', + }), + inline: true, + }, + { + name: '개ㅂ발자', + value: ( + await this.container.client.users.fetch( + this.container.config.bot.owner_ID, + ) + ).username, + inline: false, }, ], + thumbnail: { + url: this.container.client.user!.displayAvatarURL()!, + }, + } + } + + public async messageRun(msg: Message) { + await msg.reply({ + embeds: [await this._embed()], + }) + } + + public async chatInputRun(interaction: ChatInputCommandInteraction) { + await interaction.reply({ + embeds: [await this._embed()], }) } } From 355fd0b1f3671b2dc2c84e8edc5c299b2021f212 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sat, 28 Sep 2024 20:50:56 +0900 Subject: [PATCH 3/6] fix: Edit logic --- package.json | 2 +- src/Commands/information.ts | 88 ++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/package.json b/package.json index d5e008c..2b1c14f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "muffinbot", - "version": "4.0.0-pudding.experimental_slash_test.1", + "version": "4.0.0-pudding.experimental_slash_test.2", "main": "dist/index.js", "private": true, "dependencies": { diff --git a/src/Commands/information.ts b/src/Commands/information.ts index c602ce3..8061751 100644 --- a/src/Commands/information.ts +++ b/src/Commands/information.ts @@ -1,4 +1,4 @@ -import { type ChatInputCommandInteraction, APIEmbed, Message } from 'discord.js' +import type { ChatInputCommandInteraction, Message } from 'discord.js' import { Command, container } from '@sapphire/framework' import { ApplyOptions } from '@sapphire/decorators' import { platform, arch } from 'os' @@ -17,58 +17,58 @@ class InformationCommand extends Command { ) } - private async _embed(): Promise { - return { - title: `${this.container.client.user?.username}의 정ㅂ보`, - fields: [ + private async _run(ctx: Message | ChatInputCommandInteraction) { + await ctx.reply({ + embeds: [ { - name: '구동ㅎ환경', - value: `${platform()} ${arch()}`, - inline: false, - }, - { - name: '버ㅈ전', - value: this.container.version, - inline: true, - }, - { - name: '채ㄴ널', - value: this.container.channel.toLowerCase(), - inline: true, - }, - { - name: '최근 업ㄷ데이트 날짜', - value: this.container.lastUpdated.toLocaleDateString('ko', { - dateStyle: 'long', - }), - inline: true, - }, - { - name: '개ㅂ발자', - value: ( - await this.container.client.users.fetch( - this.container.config.bot.owner_ID, - ) - ).username, - inline: false, + title: `${this.container.client.user?.username}의 정ㅂ보`, + fields: [ + { + name: '구동ㅎ환경', + value: `${platform()} ${arch()}`, + inline: false, + }, + { + name: '버ㅈ전', + value: this.container.version, + inline: true, + }, + { + name: '채ㄴ널', + value: this.container.channel.toLowerCase(), + inline: true, + }, + { + name: '최근 업ㄷ데이트 날짜', + value: this.container.lastUpdated.toLocaleDateString('ko', { + dateStyle: 'long', + }), + inline: true, + }, + { + name: '개ㅂ발자', + value: ( + await this.container.client.users.fetch( + this.container.config.bot.owner_ID, + ) + ).username, + inline: false, + }, + ], + thumbnail: { + url: this.container.client.user!.displayAvatarURL()!, + }, }, ], - thumbnail: { - url: this.container.client.user!.displayAvatarURL()!, - }, - } + }) } public async messageRun(msg: Message) { - await msg.reply({ - embeds: [await this._embed()], - }) + await this._run(msg) } public async chatInputRun(interaction: ChatInputCommandInteraction) { - await interaction.reply({ - embeds: [await this._embed()], - }) + await this._run(interaction) } } From 33102d64dc50b92af8881769d5db4bf9c1d37d61 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Sun, 29 Sep 2024 15:39:49 +0900 Subject: [PATCH 4/6] feat: slash command for help command --- package.json | 2 +- src/Client.ts | 2 +- src/Commands/help.ts | 45 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 2b1c14f..f4c7488 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "muffinbot", - "version": "4.0.0-pudding.experimental_slash_test.2", + "version": "4.0.0-pudding.experimental_slash_test.3", "main": "dist/index.js", "private": true, "dependencies": { diff --git a/src/Client.ts b/src/Client.ts index 80c9289..562966b 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-09-29') if (release.startsWith('e')) { container.channel = 'EXPERIMENTAL' 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({ From a0a7adff7944c940cdae27cd3561e969b8903618 Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Mon, 30 Sep 2024 21:22:32 +0900 Subject: [PATCH 5/6] feat: Support 3 commands slash command --- package.json | 2 +- src/Commands/deleteLearn.ts | 61 +++++++++++++++++++++++++++-------- src/Commands/learning_data.ts | 25 +++++++++++--- src/Commands/list.ts | 27 ++++++++++++---- 4 files changed, 89 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index f4c7488..f88e42e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "muffinbot", - "version": "4.0.0-pudding.experimental_slash_test.3", + "version": "4.0.0-pudding.experimental_slash_test.4", "main": "dist/index.js", "private": true, "dependencies": { 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/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({ From db701a77560c30aedf260182d546fa911c48c1be Mon Sep 17 00:00:00 2001 From: Siwoo Jeon Date: Tue, 1 Oct 2024 13:50:15 +0900 Subject: [PATCH 6/6] feat: Support slash command for learn command --- package.json | 2 +- src/Client.ts | 2 +- src/Commands/learn.ts | 103 +++++++++++++++++++++++++++++++----------- 3 files changed, 79 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index f88e42e..930fe1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "muffinbot", - "version": "4.0.0-pudding.experimental_slash_test.4", + "version": "4.0.0-pudding.experimental_slash_test.5", "main": "dist/index.js", "private": true, "dependencies": { diff --git a/src/Client.ts b/src/Client.ts index 562966b..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-29') +container.lastUpdated = new Date('2024-10-01') if (release.startsWith('e')) { container.channel = 'EXPERIMENTAL' 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) } }