From c4b21275f985bdeeb536bf48333b917506909ffc Mon Sep 17 00:00:00 2001 From: xHyroM Date: Thu, 24 Aug 2023 16:03:48 +0200 Subject: [PATCH] feat: make autocomplete options working --- src/commands/docs.ts | 14 +++++-- src/commands/index.ts | 3 ++ src/commands/version.ts | 1 + src/listeners/interaction_create.ts | 45 +++++++++++++++++++--- src/loaders/commands.ts | 20 ++++++++++ src/structs/Command.ts | 5 ++- src/structs/Tag.ts | 1 - src/structs/context/AutocompleteContext.ts | 12 ++++-- 8 files changed, 86 insertions(+), 15 deletions(-) diff --git a/src/commands/docs.ts b/src/commands/docs.ts index 3143b7f..63f8e55 100644 --- a/src/commands/docs.ts +++ b/src/commands/docs.ts @@ -1,10 +1,11 @@ import { SlashCommandStringOption } from "discord.js"; import { defineCommand } from "../loaders/commands"; import { AutocompleteContext } from "../structs/context/AutocompleteContext"; -import { Option } from "../structs/Command"; +import { InteractionCommandContext } from "../structs/context/CommandContext"; defineCommand({ name: "docs", + description: "Search at docs", options: [ { ...new SlashCommandStringOption() @@ -13,9 +14,14 @@ defineCommand({ .setAutocomplete(true) .setDescription("Select query") .toJSON(), - run: (_: Option, context: AutocompleteContext) => { - return context.interaction.respond([{ name: "heh", value: "heh" }]); + run: (context: AutocompleteContext) => { + return context.respond([{ name: "heh", value: "heh" }]); } } - ] + ], + run: (context: InteractionCommandContext) => { + context.reply({ + content: "ccc" + }); + } }) diff --git a/src/commands/index.ts b/src/commands/index.ts index e18875e..4f34f75 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,2 +1,5 @@ import "./version.ts"; import "./docs.ts"; + +import { registerCommands } from "../loaders/commands.ts"; +await registerCommands(); diff --git a/src/commands/version.ts b/src/commands/version.ts index cc729f9..f0f3723 100644 --- a/src/commands/version.ts +++ b/src/commands/version.ts @@ -4,6 +4,7 @@ import { InteractionCommandContext } from "../structs/context/CommandContext.ts" export default defineCommand({ name: "version", + description: "Show version", options: [], run: (context: InteractionCommandContext) => { context.interaction.reply({ diff --git a/src/listeners/interaction_create.ts b/src/listeners/interaction_create.ts index e4839e0..2b06756 100644 --- a/src/listeners/interaction_create.ts +++ b/src/listeners/interaction_create.ts @@ -1,18 +1,21 @@ -import { Events, Interaction, ChatInputCommandInteraction } from "discord.js"; +import { Events, Interaction, ChatInputCommandInteraction, AutocompleteInteraction } from "discord.js"; import { COMMANDS } from "../loaders/commands.ts"; import { defineListener } from "../loaders/listeners.ts"; import { InteractionCommandContext } from "../structs/context/CommandContext.ts"; +import { AutocompleteContext } from "../structs/context/AutocompleteContext.ts"; +import { StringOption } from "../structs/Command.ts"; defineListener({ event: Events.InteractionCreate, - run: (interaction: Interaction) => { - if (interaction.isChatInputCommand()) return handleCommand(interaction); + run: async(interaction: Interaction) => { + if (interaction.isChatInputCommand()) return await handleCommand(interaction); + if (interaction.isAutocomplete()) return await handleAutocomplete(interaction); return; } }) -function handleCommand(interaction: ChatInputCommandInteraction) { +async function handleCommand(interaction: ChatInputCommandInteraction) { const command = COMMANDS.get(interaction.commandName); if (!command || !command.run) { @@ -23,5 +26,37 @@ function handleCommand(interaction: ChatInputCommandInteraction) { return; } - command.run(new InteractionCommandContext(command, interaction)); + const context = new InteractionCommandContext(command, interaction); + await Promise.resolve(command.run(context)) + .catch(async error => { + context.reply({ + content: `Something went wrong... ${error.message} (${error.code})` + }); + }); +} + +async function handleAutocomplete(interaction: AutocompleteInteraction) { + const command = COMMANDS.get(interaction.commandName); + if (!command) return; + + let options = command.options; + + if (interaction.options.getSubcommandGroup(false)) + options = options.find(o => o.name === interaction.options.getSubcommandGroup())?.options; + + if (interaction.options.getSubcommand(false)) + options = options.find(o => o.name === interaction.options.getSubcommand())?.options; + + const focused = interaction.options.getFocused(true); + const option = options.find(o => o.name === focused.name) as StringOption; + if (!option) return; + + + const context = new AutocompleteContext(option, command, interaction); + await Promise.resolve(command.run(context)) + .catch(async error => { + context.respond({ + content: `Something went wrong... ${error.message} (${error.code})` + }); + }); } diff --git a/src/loaders/commands.ts b/src/loaders/commands.ts index 8f972a5..5eb0de8 100644 --- a/src/loaders/commands.ts +++ b/src/loaders/commands.ts @@ -1,7 +1,27 @@ +import { REST, Routes, SlashCommandBuilder } from "discord.js"; +import { Bubu } from "../structs/Client.ts"; import type { Command } from "../structs/Command.ts"; export const COMMANDS: Map = new Map(); +export const REST_CLIENT = new REST().setToken(process.env.DISCORD_TOKEN); export function defineCommand(command: T) { COMMANDS.set(command.name, command); } + +export async function registerCommands() { + const commands = [...COMMANDS.values()]; + + await REST_CLIENT.put( + Routes.applicationCommands("995690041793839124"), + { + body: commands.map(d => ({ + ...new SlashCommandBuilder() + .setName(d.name) + .setDescription(d.description) + .toJSON(), + options: d.options + })) + } + ) +} diff --git a/src/structs/Command.ts b/src/structs/Command.ts index f3008e1..b5b3205 100644 --- a/src/structs/Command.ts +++ b/src/structs/Command.ts @@ -14,7 +14,7 @@ export type Option = APIApplicationCommandAttachmentOption | APIApplicationCommandSubcommandGroupOption | StringOption; -interface StringOption { +export interface StringOption { name: string; name_localizations?: LocalizationMap; description: string; @@ -25,11 +25,12 @@ interface StringOption { type: ApplicationCommandOptionType.String; autocomplete?: boolean; choices?: APIApplicationCommandOptionChoice[]; - run: (option: Option, interaction: AutocompleteContext) => any; + run: (interaction: AutocompleteContext) => any; } export interface Command { name: string; + description: string; options: Option[]; run?: ( context: CommandContext diff --git a/src/structs/Tag.ts b/src/structs/Tag.ts index c34f029..a355e9b 100644 --- a/src/structs/Tag.ts +++ b/src/structs/Tag.ts @@ -3,4 +3,3 @@ export interface Tag { answer: string; keywords: string[]; } - diff --git a/src/structs/context/AutocompleteContext.ts b/src/structs/context/AutocompleteContext.ts index f3864a7..bda7098 100644 --- a/src/structs/context/AutocompleteContext.ts +++ b/src/structs/context/AutocompleteContext.ts @@ -1,12 +1,14 @@ -import { AutocompleteInteraction } from "discord.js"; -import { Option } from "../Command.ts"; +import { ApplicationCommandOptionChoiceData, AutocompleteInteraction } from "discord.js"; +import { Command, Option } from "../Command.ts"; export class AutocompleteContext { public option: Option; + public command: Command; public interaction: AutocompleteInteraction; - public constructor(option: Option, interaction: AutocompleteInteraction) { + public constructor(option: Option, command: Command, interaction: AutocompleteInteraction) { this.option = option; + this.command = command; this.interaction = interaction; } @@ -21,4 +23,8 @@ export class AutocompleteContext { get options() { return this.interaction.options; } + + public respond(options: ApplicationCommandOptionChoiceData[]) { + return this.interaction.respond(options); + } }