feat: make autocomplete options working

This commit is contained in:
Jozef Steinhübl 2023-08-24 16:03:48 +02:00
parent 38acccfa2b
commit c4b21275f9
8 changed files with 86 additions and 15 deletions

View file

@ -1,10 +1,11 @@
import { SlashCommandStringOption } from "discord.js"; import { SlashCommandStringOption } from "discord.js";
import { defineCommand } from "../loaders/commands"; import { defineCommand } from "../loaders/commands";
import { AutocompleteContext } from "../structs/context/AutocompleteContext"; import { AutocompleteContext } from "../structs/context/AutocompleteContext";
import { Option } from "../structs/Command"; import { InteractionCommandContext } from "../structs/context/CommandContext";
defineCommand({ defineCommand({
name: "docs", name: "docs",
description: "Search at docs",
options: [ options: [
{ {
...new SlashCommandStringOption() ...new SlashCommandStringOption()
@ -13,9 +14,14 @@ defineCommand({
.setAutocomplete(true) .setAutocomplete(true)
.setDescription("Select query") .setDescription("Select query")
.toJSON(), .toJSON(),
run: (_: Option, context: AutocompleteContext) => { run: (context: AutocompleteContext) => {
return context.interaction.respond([{ name: "heh", value: "heh" }]); return context.respond([{ name: "heh", value: "heh" }]);
} }
} }
] ],
run: (context: InteractionCommandContext) => {
context.reply({
content: "ccc"
});
}
}) })

View file

@ -1,2 +1,5 @@
import "./version.ts"; import "./version.ts";
import "./docs.ts"; import "./docs.ts";
import { registerCommands } from "../loaders/commands.ts";
await registerCommands();

View file

@ -4,6 +4,7 @@ import { InteractionCommandContext } from "../structs/context/CommandContext.ts"
export default defineCommand({ export default defineCommand({
name: "version", name: "version",
description: "Show version",
options: [], options: [],
run: (context: InteractionCommandContext) => { run: (context: InteractionCommandContext) => {
context.interaction.reply({ context.interaction.reply({

View file

@ -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 { COMMANDS } from "../loaders/commands.ts";
import { defineListener } from "../loaders/listeners.ts"; import { defineListener } from "../loaders/listeners.ts";
import { InteractionCommandContext } from "../structs/context/CommandContext.ts"; import { InteractionCommandContext } from "../structs/context/CommandContext.ts";
import { AutocompleteContext } from "../structs/context/AutocompleteContext.ts";
import { StringOption } from "../structs/Command.ts";
defineListener({ defineListener({
event: Events.InteractionCreate, event: Events.InteractionCreate,
run: (interaction: Interaction) => { run: async(interaction: Interaction) => {
if (interaction.isChatInputCommand()) return handleCommand(interaction); if (interaction.isChatInputCommand()) return await handleCommand(interaction);
if (interaction.isAutocomplete()) return await handleAutocomplete(interaction);
return; return;
} }
}) })
function handleCommand(interaction: ChatInputCommandInteraction) { async function handleCommand(interaction: ChatInputCommandInteraction) {
const command = COMMANDS.get(interaction.commandName); const command = COMMANDS.get(interaction.commandName);
if (!command || !command.run) { if (!command || !command.run) {
@ -23,5 +26,37 @@ function handleCommand(interaction: ChatInputCommandInteraction) {
return; 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})`
});
});
} }

View file

@ -1,7 +1,27 @@
import { REST, Routes, SlashCommandBuilder } from "discord.js";
import { Bubu } from "../structs/Client.ts";
import type { Command } from "../structs/Command.ts"; import type { Command } from "../structs/Command.ts";
export const COMMANDS: Map<string, Command> = new Map(); export const COMMANDS: Map<string, Command> = new Map();
export const REST_CLIENT = new REST().setToken(process.env.DISCORD_TOKEN);
export function defineCommand<T extends Command>(command: T) { export function defineCommand<T extends Command>(command: T) {
COMMANDS.set(command.name, command); 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
}))
}
)
}

View file

@ -14,7 +14,7 @@ export type Option = APIApplicationCommandAttachmentOption |
APIApplicationCommandSubcommandGroupOption | APIApplicationCommandSubcommandGroupOption |
StringOption; StringOption;
interface StringOption { export interface StringOption {
name: string; name: string;
name_localizations?: LocalizationMap; name_localizations?: LocalizationMap;
description: string; description: string;
@ -25,11 +25,12 @@ interface StringOption {
type: ApplicationCommandOptionType.String; type: ApplicationCommandOptionType.String;
autocomplete?: boolean; autocomplete?: boolean;
choices?: APIApplicationCommandOptionChoice[]; choices?: APIApplicationCommandOptionChoice[];
run: (option: Option, interaction: AutocompleteContext) => any; run: (interaction: AutocompleteContext) => any;
} }
export interface Command { export interface Command {
name: string; name: string;
description: string;
options: Option[]; options: Option[];
run?: ( run?: (
context: CommandContext<true> context: CommandContext<true>

View file

@ -3,4 +3,3 @@ export interface Tag {
answer: string; answer: string;
keywords: string[]; keywords: string[];
} }

View file

@ -1,12 +1,14 @@
import { AutocompleteInteraction } from "discord.js"; import { ApplicationCommandOptionChoiceData, AutocompleteInteraction } from "discord.js";
import { Option } from "../Command.ts"; import { Command, Option } from "../Command.ts";
export class AutocompleteContext { export class AutocompleteContext {
public option: Option; public option: Option;
public command: Command;
public interaction: AutocompleteInteraction; public interaction: AutocompleteInteraction;
public constructor(option: Option, interaction: AutocompleteInteraction) { public constructor(option: Option, command: Command, interaction: AutocompleteInteraction) {
this.option = option; this.option = option;
this.command = command;
this.interaction = interaction; this.interaction = interaction;
} }
@ -21,4 +23,8 @@ export class AutocompleteContext {
get options() { get options() {
return this.interaction.options; return this.interaction.options;
} }
public respond(options: ApplicationCommandOptionChoiceData[]) {
return this.interaction.respond(options);
}
} }