From f881af5e82e7c9b88fe4aeb71d4356d4395903e3 Mon Sep 17 00:00:00 2001 From: Gorciu Date: Wed, 1 Jul 2026 20:04:17 +0200 Subject: [PATCH] refactor(plugins): add new plugins functionality (very simplified) for commands --- src/apis/gemini/ask.ts | 17 +- src/bot/command/api.ts | 3 +- src/cmd/list.ts | 59 ----- .../commands/helpers/argument-parser.ts | 4 +- .../commands/helpers/make-command-api.ts | 6 +- src/features/commands/prefix.ts | 4 +- src/features/commands/slash.ts | 202 +++++++++--------- src/features/ei/ask.ts | 63 ++---- src/main.ts | 8 +- .../4fun/4fun/commands}/cdecl.ts | 0 .../4fun/4fun/commands}/compile.ts | 0 .../4fun/4fun/commands}/eas.ts | 0 .../4fun/4fun/commands}/figlet.ts | 0 .../4fun/4fun/commands}/pokedex.ts | 0 .../4fun/4fun/commands}/random.ts | 0 .../4fun/4fun/commands}/tfwiki.ts | 0 .../4fun/4fun/commands}/topprestige.ts | 0 .../4fun/4fun/commands}/votepin.ts | 0 .../leveling => plugins/4fun/levels}/lvl.ts | 0 .../4fun/levels}/toplvl.ts | 0 .../leveling => plugins/4fun/levels}/xpmod.ts | 0 .../dev/dev/commands}/configuration.ts | 0 .../dev/dev/commands}/disable-cmd.ts | 0 .../dev => plugins/dev/dev/commands}/echo.ts | 0 .../dev/dev/commands}/enable-cmd.ts | 0 .../dev => plugins/dev/dev/commands}/eval.ts | 0 .../dev => plugins/dev/dev/commands}/reset.ts | 0 .../dev/dev/commands}/restart.ts | 0 .../economy/economy/commands}/bal.ts | 0 .../economy/economy/commands}/blackjack.ts | 0 .../economy/economy/commands}/buy.ts | 0 .../economy/commands}/collect-income.ts | 0 .../economy/economy/commands}/crime.ts | 0 .../economy/economy/commands}/deposit.ts | 0 .../economy/economy/commands}/ecomod.ts | 0 .../economy/economy/commands}/iteminfo.ts | 0 .../economy/economy/commands}/pay.ts | 0 .../economy/economy/commands}/rob.ts | 0 .../economy/economy/commands}/shop.ts | 0 .../economy/economy/commands}/slut.ts | 0 .../economy/economy/commands}/topeco.ts | 0 .../economy/economy/commands}/use.ts | 0 .../economy/economy/commands}/withdraw.ts | 0 .../economy/economy/commands}/work.ts | 0 .../email/email/commands}/email-blacklist.ts | 0 .../email/commands}/email-default-title.ts | 0 .../email/email/commands}/email-signature.ts | 0 .../email/email/commands}/email.ts | 0 .../accounts/commands}/add-primary-account.ts | 0 .../general/accounts/commands}/banner.ts | 0 .../accounts/commands}/manage-accounts.ts | 0 .../general/accounts/commands}/pfp.ts | 0 .../general/accounts/commands}/rank.ts | 0 .../general/help/commands}/commands.ts | 0 .../general/help/commands}/help.ts | 0 .../general/help/commands}/man.ts | 0 src/plugins/index.ts | 79 +++++++ .../mod => plugins/mod/mod/commands}/ban.ts | 0 .../mod => plugins/mod/mod/commands}/clear.ts | 0 .../mod/mod/commands}/cmd-block.ts | 0 .../mod/mod/commands}/cooldown-bypass.ts | 0 .../mod => plugins/mod/mod/commands}/kick.ts | 0 .../mod => plugins/mod/mod/commands}/mute.ts | 0 .../mod/mod/commands}/refresh.ts | 0 .../mod/mod/commands}/unmute.ts | 0 .../mod/mod/commands}/warn-clear.ts | 0 .../mod => plugins/mod/mod/commands}/warn.ts | 0 .../mod/mod/commands}/warnlist.ts | 2 +- .../utilities/utilities/commands}/ask.ts | 2 +- .../utilities/utilities/commands}/remindme.ts | 0 .../utilities/utilities/commands}/search.ts | 0 .../utilities/commands}/translate.ts | 0 .../utilities/utilities/commands}/weather.ts | 0 .../utilities/utilities/commands}/wiki.ts | 0 src/util/cmd/find-command.ts | 20 +- 75 files changed, 225 insertions(+), 244 deletions(-) delete mode 100644 src/cmd/list.ts rename src/{cmd/4fun => plugins/4fun/4fun/commands}/cdecl.ts (100%) rename src/{cmd/4fun => plugins/4fun/4fun/commands}/compile.ts (100%) rename src/{cmd/4fun => plugins/4fun/4fun/commands}/eas.ts (100%) rename src/{cmd/4fun => plugins/4fun/4fun/commands}/figlet.ts (100%) rename src/{cmd/4fun => plugins/4fun/4fun/commands}/pokedex.ts (100%) rename src/{cmd/4fun => plugins/4fun/4fun/commands}/random.ts (100%) rename src/{cmd/4fun => plugins/4fun/4fun/commands}/tfwiki.ts (100%) rename src/{cmd/4fun => plugins/4fun/4fun/commands}/topprestige.ts (100%) rename src/{cmd/4fun => plugins/4fun/4fun/commands}/votepin.ts (100%) rename src/{cmd/leveling => plugins/4fun/levels}/lvl.ts (100%) rename src/{cmd/leveling => plugins/4fun/levels}/toplvl.ts (100%) rename src/{cmd/leveling => plugins/4fun/levels}/xpmod.ts (100%) rename src/{cmd/dev => plugins/dev/dev/commands}/configuration.ts (100%) rename src/{cmd/dev => plugins/dev/dev/commands}/disable-cmd.ts (100%) rename src/{cmd/dev => plugins/dev/dev/commands}/echo.ts (100%) rename src/{cmd/dev => plugins/dev/dev/commands}/enable-cmd.ts (100%) rename src/{cmd/dev => plugins/dev/dev/commands}/eval.ts (100%) rename src/{cmd/dev => plugins/dev/dev/commands}/reset.ts (100%) rename src/{cmd/dev => plugins/dev/dev/commands}/restart.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/bal.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/blackjack.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/buy.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/collect-income.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/crime.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/deposit.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/ecomod.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/iteminfo.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/pay.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/rob.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/shop.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/slut.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/topeco.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/use.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/withdraw.ts (100%) rename src/{cmd/economy => plugins/economy/economy/commands}/work.ts (100%) rename src/{cmd/email => plugins/email/email/commands}/email-blacklist.ts (100%) rename src/{cmd/email => plugins/email/email/commands}/email-default-title.ts (100%) rename src/{cmd/email => plugins/email/email/commands}/email-signature.ts (100%) rename src/{cmd/email => plugins/email/email/commands}/email.ts (100%) rename src/{cmd/general => plugins/general/accounts/commands}/add-primary-account.ts (100%) rename src/{cmd/general => plugins/general/accounts/commands}/banner.ts (100%) rename src/{cmd/general => plugins/general/accounts/commands}/manage-accounts.ts (100%) rename src/{cmd/general => plugins/general/accounts/commands}/pfp.ts (100%) rename src/{cmd/general => plugins/general/accounts/commands}/rank.ts (100%) rename src/{cmd/general => plugins/general/help/commands}/commands.ts (100%) rename src/{cmd/general => plugins/general/help/commands}/help.ts (100%) rename src/{cmd/general => plugins/general/help/commands}/man.ts (100%) create mode 100644 src/plugins/index.ts rename src/{cmd/mod => plugins/mod/mod/commands}/ban.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/clear.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/cmd-block.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/cooldown-bypass.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/kick.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/mute.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/refresh.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/unmute.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/warn-clear.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/warn.ts (100%) rename src/{cmd/mod => plugins/mod/mod/commands}/warnlist.ts (99%) rename src/{cmd/utilities => plugins/utilities/utilities/commands}/ask.ts (96%) rename src/{cmd/utilities => plugins/utilities/utilities/commands}/remindme.ts (100%) rename src/{cmd/utilities => plugins/utilities/utilities/commands}/search.ts (100%) rename src/{cmd/utilities => plugins/utilities/utilities/commands}/translate.ts (100%) rename src/{cmd/utilities => plugins/utilities/utilities/commands}/weather.ts (100%) rename src/{cmd/utilities => plugins/utilities/utilities/commands}/wiki.ts (100%) diff --git a/src/apis/gemini/ask.ts b/src/apis/gemini/ask.ts index b74fd44..a585f0f 100644 --- a/src/apis/gemini/ask.ts +++ b/src/apis/gemini/ask.ts @@ -3,26 +3,13 @@ import * as gemini from './model.ts'; export const toolDeclarations: gemini.Tool[] = [ { functionDeclarations: [ - { - name: 'list_categories', - description: 'Zwraca listę wszystkich kategorii komend bota.', - parameters: { - type: gemini.SchemaType.OBJECT, - properties: {}, - }, - }, { name: 'list_commands', description: 'Zwraca listę wszystkich dostępnych komend bota w danej kategorii wraz z ich krótkimi opisami.', parameters: { type: gemini.SchemaType.OBJECT, - properties: { - category: { - type: gemini.SchemaType.STRING, - description: "Kategoria do filtrowania komend (np. 'economy', 'mod', 'general').", - }, - }, - required: ['category'], + properties: {}, + required: [], }, }, { diff --git a/src/bot/command/api.ts b/src/bot/command/api.ts index 7611d84..18ff9c1 100644 --- a/src/bot/command/api.ts +++ b/src/bot/command/api.ts @@ -5,7 +5,6 @@ import User, { CooldownCheckResult } from '@/apis/db/user.ts'; import { CommandArgBaseType, CommandArgType, CommandValuableArgument, PreciseValuableArgument } from './arguments.ts'; -import { Category } from '@/bot/categories.ts'; import { Command } from './cmd.ts'; import { EconomyExecutor } from '@/apis/economy/action.ts'; @@ -40,7 +39,7 @@ export interface CommandAPI { economy: EconomyExecutor; // ---- EXTERNAL DATA ---- - commands: Map; + commands: Command[]; log: typeof log; // ---- EXEC LOCATION ---- diff --git a/src/cmd/list.ts b/src/cmd/list.ts deleted file mode 100644 index 7a596b0..0000000 --- a/src/cmd/list.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Category, Command } from '@/bot/command.ts'; -import { CommandPermissions } from '@/bot/command/permissions.ts'; -import { deepEqual } from '@/util/objects/objects.ts'; -import { getCommandConfig } from '@/util/cmd/get-command-config.ts'; - -import { output } from '@/bot/logging.ts'; -import logError from '@/util/log-error.ts'; - -export const commands: Map = new Map(); - -export async function registerCommands() { - for (const cat of Deno.readDirSync('./src/cmd')) { - if (cat.isFile) continue; - - const category = Category.fromString(cat.name); - if (!category) { - output.warn(`Skipping unknown category: ${cat.name}`); - continue; - } - - const categoryCmds: Command[] = []; - - for (const cmd of Deno.readDirSync(`./src/cmd/${cat.name}`)) { - if (!cmd.name.endsWith('.ts')) { - output.warn('Invalid file (wrong file extension) inside commands directory: ' + cmd.name); - continue; - } - - try { - const module = await import(`@/cmd/${cat.name}/${cmd.name}`); - - if (!module.default) { - output.warn('No default export found in command ' + cmd.name); - continue; - } - - const command: Command = module.default; - const cmdCfg = getCommandConfig(command); - - if (cmdCfg.enabled === false) { - if (deepEqual(command.permissions, CommandPermissions.devOnly()) || command.name == 'configuration') { - output.warn('Dev-only command ' + command.name + ' should not be disabled. Leaving enabled.'); - } else { - continue; - } - } - - categoryCmds.push(command); - } catch (e) { - logError('stdwarn', e, 'Command importer'); - } - } - - commands.set( - Category.fromString(cat.name)!, - categoryCmds, - ); - } -} diff --git a/src/features/commands/helpers/argument-parser.ts b/src/features/commands/helpers/argument-parser.ts index 6ee1cf5..b22d2c2 100644 --- a/src/features/commands/helpers/argument-parser.ts +++ b/src/features/commands/helpers/argument-parser.ts @@ -1,4 +1,4 @@ -import { Category, Command, CommandArgType, CommandArgument, CommandValuableArgument } from '@/bot/command.ts'; +import { Command, CommandArgType, CommandArgument, CommandValuableArgument } from '@/bot/command.ts'; import * as dsc from 'discord.js'; @@ -88,7 +88,7 @@ export interface ParserContext { guild?: dsc.Guild; interaction?: dsc.CommandInteraction; cmd?: Command; - commands?: Map; + commands?: Command[]; } async function tryParseArg( diff --git a/src/features/commands/helpers/make-command-api.ts b/src/features/commands/helpers/make-command-api.ts index 1b52ad0..bb03f0e 100644 --- a/src/features/commands/helpers/make-command-api.ts +++ b/src/features/commands/helpers/make-command-api.ts @@ -7,10 +7,10 @@ import { Command, CommandAPI, CommandArgType } from '@/bot/command.ts'; import { parseArgs, ParsedRawArgument } from './argument-parser.ts'; import { t } from '@/apis/translations/translate.ts'; import { deepMerge } from '@/util/objects/objects.ts'; -import { commands } from '@/cmd/list.ts'; import { EconomyExecutor } from '@/apis/economy/action.ts'; import { flatTypesToUnion } from './flat-types.ts'; import { getCommandConfig } from '@/util/cmd/get-command-config.ts'; +import { pluginManager } from '@/plugins/index.ts'; type FirstArg = T extends { (...args: infer A): unknown } ? A extends [infer F, ...unknown[]] ? F : never : T extends { call(this: unknown, ...args: infer A): unknown } ? A extends [unknown, infer F, ...unknown[]] ? F : never @@ -44,7 +44,7 @@ function makeOptions(options: FirstArg): object { } export async function makeCommandApi(commandObj: Command, argsRaw: ParsedRawArgument[], context: { msg?: dsc.Message; guild?: dsc.Guild; interaction?: dsc.CommandInteraction; cmd?: Command; invokedviaalias: string }): Promise { - const parsedArgs = await parseArgs(argsRaw, commandObj.expectedArgs, { ...context, commands }); + const parsedArgs = await parseArgs(argsRaw, commandObj.expectedArgs, { ...context, commands: pluginManager.getAllCommands() }); const rawMember = context.msg?.member ?? (context.interaction?.member as dsc.GuildMember) ?? null; @@ -74,7 +74,7 @@ export async function makeCommandApi(commandObj: Command, argsRaw: ParsedRawArgu // misc reply: context.interaction ? ((options) => context.interaction!.editReply(makeOptions(options))) : ((options) => context.msg!.reply(makeOptions(options))), - commands: commands, + commands: pluginManager.getAllCommands(), log, executor: user, guild: context.interaction?.guild ?? context.msg?.guild ?? undefined, diff --git a/src/features/commands/prefix.ts b/src/features/commands/prefix.ts index c4a52ed..6d728ec 100644 --- a/src/features/commands/prefix.ts +++ b/src/features/commands/prefix.ts @@ -4,7 +4,6 @@ import { output } from '@/bot/logging.ts'; import { cfg } from '@/bot/cfg.ts'; import { CommandFlags } from '@/bot/command/misc.ts'; -import { commands } from '@/cmd/list.ts'; import canExecuteCmd from '@/util/cmd/can-execute.ts'; import findCommand from '@/util/cmd/find-command.ts'; @@ -21,6 +20,7 @@ import { ReplyEmbed } from '@/apis/translations/reply-embed.ts'; import { CommandTokenizer } from './helpers/tokenizer.ts'; import sleep from '@/util/sleep.ts'; +import { pluginManager } from '@/plugins/index.ts'; function waitForButton(interaction: dsc.Message, buttonId: string, time = 15000) { return new Promise((resolve, reject) => { @@ -74,7 +74,7 @@ async function prefixCommandsMessageHandler(msg: dsc.OmitPartialGroupDMChannel { @@ -48,7 +48,7 @@ function waitForButton(int: dsc.ChatInputCommandInteraction, buttonId: string, t client.on('interactionCreate', async (int: Interaction) => { if (int.isAutocomplete()) { - const result = findCommand(int.commandName, commands); + const result = findCommand(int.commandName, pluginManager.getAllCommands()); if (!result) return; const { command } = result; @@ -59,11 +59,9 @@ client.on('interactionCreate', async (int: Interaction) => { const type = fixType(arg.type); if (type.base == 'command-ref') { const allCommands = new Set(); - for (const [, cmds] of commands) { - for (const c of cmds) { - allCommands.add(c.name); - for (const a of c.aliases) allCommands.add(a); - } + for (const c of pluginManager.getAllCommands()) { + allCommands.add(c.name); + for (const a of c.aliases) allCommands.add(a); } const filtered = Array.from(allCommands) .filter((c) => c.toLowerCase().includes(focusedOption.value.toLowerCase())) @@ -80,7 +78,7 @@ client.on('interactionCreate', async (int: Interaction) => { if (!int.isChatInputCommand()) return; - const result = findCommand(int.commandName, commands); + const result = findCommand(int.commandName, pluginManager.getAllCommands()); if (!result) { return int.reply({ content: 'Nie znam takiej komendy', flags: dsc.MessageFlags.Ephemeral }); } @@ -201,106 +199,104 @@ export async function init() { const rest = new dsc.REST({ version: '10' }) .setToken(Deno.env.get('JB_TOKEN')!); - for (const [, cmds] of commands) { - for (const cmd of cmds) { - const scb = new dsc.SlashCommandBuilder() - .setName(cmd.name) - .setDescription(makeSlashCommandDesc(cmd)) - .setContexts((cmd.flags & CommandFlags.WorksInDM) ? dsc.InteractionContextType.BotDM : dsc.InteractionContextType.Guild); + for (const cmd of pluginManager.getAllCommands()) { + const scb = new dsc.SlashCommandBuilder() + .setName(cmd.name) + .setDescription(makeSlashCommandDesc(cmd)) + .setContexts((cmd.flags & CommandFlags.WorksInDM) ? dsc.InteractionContextType.BotDM : dsc.InteractionContextType.Guild); - const sortedArgs = []; - for (const arg of cmd.expectedArgs) { - if (!arg.optional) sortedArgs.push(arg); - } - for (const arg of cmd.expectedArgs) { - if (arg.optional) sortedArgs.push(arg); - } + const sortedArgs = []; + for (const arg of cmd.expectedArgs) { + if (!arg.optional) sortedArgs.push(arg); + } + for (const arg of cmd.expectedArgs) { + if (arg.optional) sortedArgs.push(arg); + } - for (const arg of sortedArgs) { - const type = fixType(arg.type); - - switch (type.base) { - case 'timestamp': - case 'string': - case 'code': { - const defaultDesc = type.base == 'timestamp' ? 'Podaj czas (timestamp jak np. 10s, 15m)' : 'Podaj tekst jakiś'; - - scb.addStringOption((option) => - option - .setName(arg.name) - .setDescription(makeSlashCommandOptionDesc(arg, defaultDesc)) - .setRequired(!arg.optional) - ); - break; - } - - case 'money': - case 'float': - case 'int': { - const defaultDesc = type.base == 'money' ? 'Podaj ilość pieniędzy' : 'Podaj liczbę'; - scb.addNumberOption((option) => - option - .setName(arg.name) - .setDescription(makeSlashCommandOptionDesc(arg, defaultDesc)) - .setRequired(!arg.optional) - ); - break; - } - - case 'user-mention': - scb.addUserOption((option) => - option - .setName(arg.name) - .setDescription(makeSlashCommandOptionDesc(arg, 'Wskaż użytkownika')) - .setRequired(!arg.optional) - ); - break; - case 'role-mention': - scb.addRoleOption((option) => - option - .setName(arg.name) - .setDescription(makeSlashCommandOptionDesc(arg, 'Wskaż rolę')) - .setRequired(!arg.optional) - ); - break; - case 'channel-mention': - scb.addChannelOption((option) => - option - .setName(arg.name) - .setDescription(makeSlashCommandOptionDesc(arg, 'Wskaż kanał')) - .setRequired(!arg.optional) - ); - break; - - case 'command-ref': - scb.addStringOption((option) => - option - .setName(arg.name) - .setDescription(makeSlashCommandOptionDesc(arg, 'Wskaż komendę')) - .setRequired(!arg.optional) - .setAutocomplete(true) - ); - break; - case 'enum': - scb.addStringOption((option) => { - option - .setName(arg.name) - .setDescription(makeSlashCommandOptionDesc(arg, 'Wybierz opcję, ' + formatArgType(type))) - .setRequired(!arg.optional); - - if (type.options.length <= 25) { - option.addChoices(type.options.map((o) => ({ name: o, value: o }))); - } else { - option.setAutocomplete(true); - } - return option; - }); - break; + for (const arg of sortedArgs) { + const type = fixType(arg.type); + + switch (type.base) { + case 'timestamp': + case 'string': + case 'code': { + const defaultDesc = type.base == 'timestamp' ? 'Podaj czas (timestamp jak np. 10s, 15m)' : 'Podaj tekst jakiś'; + + scb.addStringOption((option) => + option + .setName(arg.name) + .setDescription(makeSlashCommandOptionDesc(arg, defaultDesc)) + .setRequired(!arg.optional) + ); + break; + } + + case 'money': + case 'float': + case 'int': { + const defaultDesc = type.base == 'money' ? 'Podaj ilość pieniędzy' : 'Podaj liczbę'; + scb.addNumberOption((option) => + option + .setName(arg.name) + .setDescription(makeSlashCommandOptionDesc(arg, defaultDesc)) + .setRequired(!arg.optional) + ); + break; } - } - commandsArray.push(scb.toJSON()); + case 'user-mention': + scb.addUserOption((option) => + option + .setName(arg.name) + .setDescription(makeSlashCommandOptionDesc(arg, 'Wskaż użytkownika')) + .setRequired(!arg.optional) + ); + break; + case 'role-mention': + scb.addRoleOption((option) => + option + .setName(arg.name) + .setDescription(makeSlashCommandOptionDesc(arg, 'Wskaż rolę')) + .setRequired(!arg.optional) + ); + break; + case 'channel-mention': + scb.addChannelOption((option) => + option + .setName(arg.name) + .setDescription(makeSlashCommandOptionDesc(arg, 'Wskaż kanał')) + .setRequired(!arg.optional) + ); + break; + + case 'command-ref': + scb.addStringOption((option) => + option + .setName(arg.name) + .setDescription(makeSlashCommandOptionDesc(arg, 'Wskaż komendę')) + .setRequired(!arg.optional) + .setAutocomplete(true) + ); + break; + case 'enum': + scb.addStringOption((option) => { + option + .setName(arg.name) + .setDescription(makeSlashCommandOptionDesc(arg, 'Wybierz opcję, ' + formatArgType(type))) + .setRequired(!arg.optional); + + if (type.options.length <= 25) { + option.addChoices(type.options.map((o) => ({ name: o, value: o }))); + } else { + option.setAutocomplete(true); + } + return option; + }); + break; + } } + + commandsArray.push(scb.toJSON()); } try { diff --git a/src/features/ei/ask.ts b/src/features/ei/ask.ts index 16b7c71..a8ebdf0 100644 --- a/src/features/ei/ask.ts +++ b/src/features/ei/ask.ts @@ -8,7 +8,6 @@ import { Buffer } from 'node:buffer'; import { SystemPrompt } from '@/features/ei/models.ts'; import { toolDeclarations } from '@/apis/gemini/ask.ts'; -import { commands } from '@/cmd/list.ts'; import { output } from '@/bot/logging.ts'; import { cfg } from '@/bot/cfg.ts'; import { client } from '@/client.ts'; @@ -18,8 +17,9 @@ import logError from '@/util/log-error.ts'; import { getCommandConfig } from '@/util/cmd/get-command-config.ts'; import { Hour } from '@/util/parse-timestamp.ts'; -import askCmd from '@/cmd/utilities/ask.ts'; +import askCmd from '@/plugins/utilities/utilities/commands/ask.ts'; import User, { CooldownWaiting } from '@/apis/db/user.ts'; +import { pluginManager } from '@/plugins/index.ts'; // NOTE: duplicated logic with src/features/.../make-command-api.ts // i'm too lazy to move it to some kind of helper so sorry @@ -104,21 +104,8 @@ export async function executeAsk(msg: dsc.Message, question: string, contextMsgs } const toolHandlers = { - list_categories: () => { - const categories = Array.from(commands.keys()); - return { - categories: categories.map((c) => ({ - id: c.stringId(), - name: c.name, - description: c.shortDesc, - })), - }; - }, - list_commands: (args: { category: string }) => { - const category = args.category; - const cat = Array.from(commands.keys()).find((c) => c.stringId() === category || c.name.toLowerCase() === category.toLowerCase()); - if (!cat) return { error: `Nie znaleziono kategorii: ${category}` }; - const cmds = commands.get(cat) || []; + list_commands: () => { + const cmds = pluginManager.getAllCommands(); return { commands: cmds.map((c) => ({ name: c.name, @@ -128,21 +115,18 @@ export async function executeAsk(msg: dsc.Message, question: string, contextMsgs }, get_command_help: (args: { command_name: string }) => { const command_name = args.command_name; - for (const [_, cmds] of commands.entries()) { - const cmd = cmds.find((c) => c.name === command_name || c.aliases.includes(command_name)); - if (cmd) { - return { - name: cmd.name, - aliases: cmd.aliases, - description: cmd.description.main, - args: cmd.expectedArgs.map((a) => ({ - name: a.name, - description: a.description, - type: JSON.stringify(a.type), - optional: a.optional, - })), - }; - } + for (const cmd of pluginManager.getAllCommands()) { + return { + name: cmd.name, + aliases: cmd.aliases, + description: cmd.description.main, + args: cmd.expectedArgs.map((a) => ({ + name: a.name, + description: a.description, + type: JSON.stringify(a.type), + optional: a.optional, + })), + }; } return { error: `Nie znaleziono komendy: ${command_name}` }; }, @@ -151,15 +135,12 @@ export async function executeAsk(msg: dsc.Message, question: string, contextMsgs // deno-lint-ignore no-explicit-any const results: any[] = []; - for (const [cat, cmds] of commands.entries()) { - for (const cmd of cmds) { - if (cmd.name.toLowerCase().includes(query) || cmd.description.main.toLowerCase().includes(query) || cmd.description.short.toLowerCase().includes(query)) { - results.push({ - name: cmd.name, - category: cat.name, - description: cmd.description.short, - }); - } + for (const cmd of pluginManager.getAllCommands()) { + if (cmd.name.toLowerCase().includes(query) || cmd.description.main.toLowerCase().includes(query) || cmd.description.short.toLowerCase().includes(query)) { + results.push({ + name: cmd.name, + description: cmd.description.short, + }); } } diff --git a/src/main.ts b/src/main.ts index 636b3a7..4239ee1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,7 @@ console.log('Welcome to JustBOT!'); // preparation & basic imports import { client } from '@/client.ts'; -import { ft, output } from '@/bot/logging.ts'; +import { output } from '@/bot/logging.ts'; import process from 'node:process'; import logError from '@/util/log-error.ts'; @@ -64,13 +64,13 @@ import { initAskCmdModel, initWikiModel } from '@/features/ei/models.ts'; import { askAction } from '@/features/4fun/ask.ts'; import { addVoiceExperience } from '@/bot/level.ts'; import { addMusicAction } from '@/features/4fun/add-content.ts'; -import { registerCommands } from '@/cmd/list.ts'; import { reactionAddHandler, reactionRemoveHandler } from '@/features/4fun/reaction-handler.ts'; import { registerMsgEditDscEvents } from '@/features/logs/edit-message.ts'; import { deleteMessageAction } from '@/features/logs/delete-message.ts'; import { reminderHandler } from '@/features/reminders.ts'; import { autoUpdateAction } from './features/others/auto-update.ts'; import { pollsModerator } from '@/features/mod/polls-mod.ts'; +import { pluginManager } from '@/plugins/index.ts'; // --------------- INIT --------------- client.once('clientReady', async () => { @@ -87,8 +87,8 @@ client.once('clientReady', async () => { ); } - await registerCommands(); - output.verbose('Commands registered'); + pluginManager.loadPlugins(); + output.verbose('Plugins and associated commands registered'); await db.init(); output.verbose(`Database initialized.`); diff --git a/src/cmd/4fun/cdecl.ts b/src/plugins/4fun/4fun/commands/cdecl.ts similarity index 100% rename from src/cmd/4fun/cdecl.ts rename to src/plugins/4fun/4fun/commands/cdecl.ts diff --git a/src/cmd/4fun/compile.ts b/src/plugins/4fun/4fun/commands/compile.ts similarity index 100% rename from src/cmd/4fun/compile.ts rename to src/plugins/4fun/4fun/commands/compile.ts diff --git a/src/cmd/4fun/eas.ts b/src/plugins/4fun/4fun/commands/eas.ts similarity index 100% rename from src/cmd/4fun/eas.ts rename to src/plugins/4fun/4fun/commands/eas.ts diff --git a/src/cmd/4fun/figlet.ts b/src/plugins/4fun/4fun/commands/figlet.ts similarity index 100% rename from src/cmd/4fun/figlet.ts rename to src/plugins/4fun/4fun/commands/figlet.ts diff --git a/src/cmd/4fun/pokedex.ts b/src/plugins/4fun/4fun/commands/pokedex.ts similarity index 100% rename from src/cmd/4fun/pokedex.ts rename to src/plugins/4fun/4fun/commands/pokedex.ts diff --git a/src/cmd/4fun/random.ts b/src/plugins/4fun/4fun/commands/random.ts similarity index 100% rename from src/cmd/4fun/random.ts rename to src/plugins/4fun/4fun/commands/random.ts diff --git a/src/cmd/4fun/tfwiki.ts b/src/plugins/4fun/4fun/commands/tfwiki.ts similarity index 100% rename from src/cmd/4fun/tfwiki.ts rename to src/plugins/4fun/4fun/commands/tfwiki.ts diff --git a/src/cmd/4fun/topprestige.ts b/src/plugins/4fun/4fun/commands/topprestige.ts similarity index 100% rename from src/cmd/4fun/topprestige.ts rename to src/plugins/4fun/4fun/commands/topprestige.ts diff --git a/src/cmd/4fun/votepin.ts b/src/plugins/4fun/4fun/commands/votepin.ts similarity index 100% rename from src/cmd/4fun/votepin.ts rename to src/plugins/4fun/4fun/commands/votepin.ts diff --git a/src/cmd/leveling/lvl.ts b/src/plugins/4fun/levels/lvl.ts similarity index 100% rename from src/cmd/leveling/lvl.ts rename to src/plugins/4fun/levels/lvl.ts diff --git a/src/cmd/leveling/toplvl.ts b/src/plugins/4fun/levels/toplvl.ts similarity index 100% rename from src/cmd/leveling/toplvl.ts rename to src/plugins/4fun/levels/toplvl.ts diff --git a/src/cmd/leveling/xpmod.ts b/src/plugins/4fun/levels/xpmod.ts similarity index 100% rename from src/cmd/leveling/xpmod.ts rename to src/plugins/4fun/levels/xpmod.ts diff --git a/src/cmd/dev/configuration.ts b/src/plugins/dev/dev/commands/configuration.ts similarity index 100% rename from src/cmd/dev/configuration.ts rename to src/plugins/dev/dev/commands/configuration.ts diff --git a/src/cmd/dev/disable-cmd.ts b/src/plugins/dev/dev/commands/disable-cmd.ts similarity index 100% rename from src/cmd/dev/disable-cmd.ts rename to src/plugins/dev/dev/commands/disable-cmd.ts diff --git a/src/cmd/dev/echo.ts b/src/plugins/dev/dev/commands/echo.ts similarity index 100% rename from src/cmd/dev/echo.ts rename to src/plugins/dev/dev/commands/echo.ts diff --git a/src/cmd/dev/enable-cmd.ts b/src/plugins/dev/dev/commands/enable-cmd.ts similarity index 100% rename from src/cmd/dev/enable-cmd.ts rename to src/plugins/dev/dev/commands/enable-cmd.ts diff --git a/src/cmd/dev/eval.ts b/src/plugins/dev/dev/commands/eval.ts similarity index 100% rename from src/cmd/dev/eval.ts rename to src/plugins/dev/dev/commands/eval.ts diff --git a/src/cmd/dev/reset.ts b/src/plugins/dev/dev/commands/reset.ts similarity index 100% rename from src/cmd/dev/reset.ts rename to src/plugins/dev/dev/commands/reset.ts diff --git a/src/cmd/dev/restart.ts b/src/plugins/dev/dev/commands/restart.ts similarity index 100% rename from src/cmd/dev/restart.ts rename to src/plugins/dev/dev/commands/restart.ts diff --git a/src/cmd/economy/bal.ts b/src/plugins/economy/economy/commands/bal.ts similarity index 100% rename from src/cmd/economy/bal.ts rename to src/plugins/economy/economy/commands/bal.ts diff --git a/src/cmd/economy/blackjack.ts b/src/plugins/economy/economy/commands/blackjack.ts similarity index 100% rename from src/cmd/economy/blackjack.ts rename to src/plugins/economy/economy/commands/blackjack.ts diff --git a/src/cmd/economy/buy.ts b/src/plugins/economy/economy/commands/buy.ts similarity index 100% rename from src/cmd/economy/buy.ts rename to src/plugins/economy/economy/commands/buy.ts diff --git a/src/cmd/economy/collect-income.ts b/src/plugins/economy/economy/commands/collect-income.ts similarity index 100% rename from src/cmd/economy/collect-income.ts rename to src/plugins/economy/economy/commands/collect-income.ts diff --git a/src/cmd/economy/crime.ts b/src/plugins/economy/economy/commands/crime.ts similarity index 100% rename from src/cmd/economy/crime.ts rename to src/plugins/economy/economy/commands/crime.ts diff --git a/src/cmd/economy/deposit.ts b/src/plugins/economy/economy/commands/deposit.ts similarity index 100% rename from src/cmd/economy/deposit.ts rename to src/plugins/economy/economy/commands/deposit.ts diff --git a/src/cmd/economy/ecomod.ts b/src/plugins/economy/economy/commands/ecomod.ts similarity index 100% rename from src/cmd/economy/ecomod.ts rename to src/plugins/economy/economy/commands/ecomod.ts diff --git a/src/cmd/economy/iteminfo.ts b/src/plugins/economy/economy/commands/iteminfo.ts similarity index 100% rename from src/cmd/economy/iteminfo.ts rename to src/plugins/economy/economy/commands/iteminfo.ts diff --git a/src/cmd/economy/pay.ts b/src/plugins/economy/economy/commands/pay.ts similarity index 100% rename from src/cmd/economy/pay.ts rename to src/plugins/economy/economy/commands/pay.ts diff --git a/src/cmd/economy/rob.ts b/src/plugins/economy/economy/commands/rob.ts similarity index 100% rename from src/cmd/economy/rob.ts rename to src/plugins/economy/economy/commands/rob.ts diff --git a/src/cmd/economy/shop.ts b/src/plugins/economy/economy/commands/shop.ts similarity index 100% rename from src/cmd/economy/shop.ts rename to src/plugins/economy/economy/commands/shop.ts diff --git a/src/cmd/economy/slut.ts b/src/plugins/economy/economy/commands/slut.ts similarity index 100% rename from src/cmd/economy/slut.ts rename to src/plugins/economy/economy/commands/slut.ts diff --git a/src/cmd/economy/topeco.ts b/src/plugins/economy/economy/commands/topeco.ts similarity index 100% rename from src/cmd/economy/topeco.ts rename to src/plugins/economy/economy/commands/topeco.ts diff --git a/src/cmd/economy/use.ts b/src/plugins/economy/economy/commands/use.ts similarity index 100% rename from src/cmd/economy/use.ts rename to src/plugins/economy/economy/commands/use.ts diff --git a/src/cmd/economy/withdraw.ts b/src/plugins/economy/economy/commands/withdraw.ts similarity index 100% rename from src/cmd/economy/withdraw.ts rename to src/plugins/economy/economy/commands/withdraw.ts diff --git a/src/cmd/economy/work.ts b/src/plugins/economy/economy/commands/work.ts similarity index 100% rename from src/cmd/economy/work.ts rename to src/plugins/economy/economy/commands/work.ts diff --git a/src/cmd/email/email-blacklist.ts b/src/plugins/email/email/commands/email-blacklist.ts similarity index 100% rename from src/cmd/email/email-blacklist.ts rename to src/plugins/email/email/commands/email-blacklist.ts diff --git a/src/cmd/email/email-default-title.ts b/src/plugins/email/email/commands/email-default-title.ts similarity index 100% rename from src/cmd/email/email-default-title.ts rename to src/plugins/email/email/commands/email-default-title.ts diff --git a/src/cmd/email/email-signature.ts b/src/plugins/email/email/commands/email-signature.ts similarity index 100% rename from src/cmd/email/email-signature.ts rename to src/plugins/email/email/commands/email-signature.ts diff --git a/src/cmd/email/email.ts b/src/plugins/email/email/commands/email.ts similarity index 100% rename from src/cmd/email/email.ts rename to src/plugins/email/email/commands/email.ts diff --git a/src/cmd/general/add-primary-account.ts b/src/plugins/general/accounts/commands/add-primary-account.ts similarity index 100% rename from src/cmd/general/add-primary-account.ts rename to src/plugins/general/accounts/commands/add-primary-account.ts diff --git a/src/cmd/general/banner.ts b/src/plugins/general/accounts/commands/banner.ts similarity index 100% rename from src/cmd/general/banner.ts rename to src/plugins/general/accounts/commands/banner.ts diff --git a/src/cmd/general/manage-accounts.ts b/src/plugins/general/accounts/commands/manage-accounts.ts similarity index 100% rename from src/cmd/general/manage-accounts.ts rename to src/plugins/general/accounts/commands/manage-accounts.ts diff --git a/src/cmd/general/pfp.ts b/src/plugins/general/accounts/commands/pfp.ts similarity index 100% rename from src/cmd/general/pfp.ts rename to src/plugins/general/accounts/commands/pfp.ts diff --git a/src/cmd/general/rank.ts b/src/plugins/general/accounts/commands/rank.ts similarity index 100% rename from src/cmd/general/rank.ts rename to src/plugins/general/accounts/commands/rank.ts diff --git a/src/cmd/general/commands.ts b/src/plugins/general/help/commands/commands.ts similarity index 100% rename from src/cmd/general/commands.ts rename to src/plugins/general/help/commands/commands.ts diff --git a/src/cmd/general/help.ts b/src/plugins/general/help/commands/help.ts similarity index 100% rename from src/cmd/general/help.ts rename to src/plugins/general/help/commands/help.ts diff --git a/src/cmd/general/man.ts b/src/plugins/general/help/commands/man.ts similarity index 100% rename from src/cmd/general/man.ts rename to src/plugins/general/help/commands/man.ts diff --git a/src/plugins/index.ts b/src/plugins/index.ts new file mode 100644 index 0000000..c1f805f --- /dev/null +++ b/src/plugins/index.ts @@ -0,0 +1,79 @@ +import { Command } from '@/bot/command.ts'; +import { output } from '@/bot/logging.ts'; +import { getCommandConfig } from '@/util/cmd/get-command-config.ts'; +import { CommandPermissions } from '@/bot/command/permissions.ts'; +import { deepEqual } from '@/util/objects/objects.ts'; + +export class Plugin { + private commands: Command[] = []; + + constructor(private category: string) {} + + public loadCommands(...commands: Command[]) { + this.commands.push(...commands); + } + + public getCommands() { + return this.commands; + } + + public getCategory() { + return this.category; + } +}; + +export default class PluginManager { + private plugins: Plugin[] = []; + + public async loadPlugins() { + for (const categoryEntry of Deno.readDirSync('src/plugins')) { + if (!categoryEntry.isDirectory) continue; + + for (const pluginEntry of Deno.readDirSync('src/plugins/' + categoryEntry.name)) { + const plugin = new Plugin(categoryEntry.name); + + for (const typeEntry of Deno.readDirSync(`src/plugins/${categoryEntry.name}/${pluginEntry.name}`)) { + // commands + if (typeEntry.name == 'commands') { + for (const commandEntry of Deno.readDirSync(`src/plugins/${categoryEntry.name}/${pluginEntry.name}/commands`)) { + const module = await import(`@/plugins/${categoryEntry.name}/${pluginEntry.name}/commands/${commandEntry.name}`); + + if (!module.default) { + output.warn('No default export found in command ' + commandEntry.name); + continue; + } + const command: Command = module.default; + const cmdCfg = getCommandConfig(command); + + if (cmdCfg.enabled === false) { + if (deepEqual(command.permissions, CommandPermissions.devOnly()) || command.name == 'configuration') { + output.warn('Dev-only command ' + command.name + ' should not be disabled. Leaving enabled.'); + } else { + continue; + } + } + + plugin.loadCommands(command); + } + } + } + + this.plugins.push(plugin); + } + } + } + + public getPlugins(): Plugin[] { + return this.plugins; + } + + public getAllCommands(): Command[] { + const result = []; + for (const plugin of this.getPlugins()) { + result.push(...plugin.getCommands()); + } + return result; + } +} + +export const pluginManager = new PluginManager(); diff --git a/src/cmd/mod/ban.ts b/src/plugins/mod/mod/commands/ban.ts similarity index 100% rename from src/cmd/mod/ban.ts rename to src/plugins/mod/mod/commands/ban.ts diff --git a/src/cmd/mod/clear.ts b/src/plugins/mod/mod/commands/clear.ts similarity index 100% rename from src/cmd/mod/clear.ts rename to src/plugins/mod/mod/commands/clear.ts diff --git a/src/cmd/mod/cmd-block.ts b/src/plugins/mod/mod/commands/cmd-block.ts similarity index 100% rename from src/cmd/mod/cmd-block.ts rename to src/plugins/mod/mod/commands/cmd-block.ts diff --git a/src/cmd/mod/cooldown-bypass.ts b/src/plugins/mod/mod/commands/cooldown-bypass.ts similarity index 100% rename from src/cmd/mod/cooldown-bypass.ts rename to src/plugins/mod/mod/commands/cooldown-bypass.ts diff --git a/src/cmd/mod/kick.ts b/src/plugins/mod/mod/commands/kick.ts similarity index 100% rename from src/cmd/mod/kick.ts rename to src/plugins/mod/mod/commands/kick.ts diff --git a/src/cmd/mod/mute.ts b/src/plugins/mod/mod/commands/mute.ts similarity index 100% rename from src/cmd/mod/mute.ts rename to src/plugins/mod/mod/commands/mute.ts diff --git a/src/cmd/mod/refresh.ts b/src/plugins/mod/mod/commands/refresh.ts similarity index 100% rename from src/cmd/mod/refresh.ts rename to src/plugins/mod/mod/commands/refresh.ts diff --git a/src/cmd/mod/unmute.ts b/src/plugins/mod/mod/commands/unmute.ts similarity index 100% rename from src/cmd/mod/unmute.ts rename to src/plugins/mod/mod/commands/unmute.ts diff --git a/src/cmd/mod/warn-clear.ts b/src/plugins/mod/mod/commands/warn-clear.ts similarity index 100% rename from src/cmd/mod/warn-clear.ts rename to src/plugins/mod/mod/commands/warn-clear.ts diff --git a/src/cmd/mod/warn.ts b/src/plugins/mod/mod/commands/warn.ts similarity index 100% rename from src/cmd/mod/warn.ts rename to src/plugins/mod/mod/commands/warn.ts diff --git a/src/cmd/mod/warnlist.ts b/src/plugins/mod/mod/commands/warnlist.ts similarity index 99% rename from src/cmd/mod/warnlist.ts rename to src/plugins/mod/mod/commands/warnlist.ts index 8685e5d..2c76233 100644 --- a/src/cmd/mod/warnlist.ts +++ b/src/plugins/mod/mod/commands/warnlist.ts @@ -3,7 +3,7 @@ import { CommandFlags } from '@/bot/command/misc.ts'; import { db, WarnRaw } from '@/apis/db/bot-db.ts'; import * as dsc from 'discord.js'; import { PredefinedColors } from '@/util/color.ts'; -import { client as cl } from '../../client.ts'; +import { client as cl } from '@/client.ts'; import { CommandPermissions } from '@/bot/command/permissions.ts'; import User from '@/apis/db/user.ts'; diff --git a/src/cmd/utilities/ask.ts b/src/plugins/utilities/utilities/commands/ask.ts similarity index 96% rename from src/cmd/utilities/ask.ts rename to src/plugins/utilities/utilities/commands/ask.ts index cfa4798..f81b929 100644 --- a/src/cmd/utilities/ask.ts +++ b/src/plugins/utilities/utilities/commands/ask.ts @@ -2,7 +2,7 @@ import { CommandFlags } from '@/bot/command/misc.ts'; import { CommandPermissions } from '@/bot/command/permissions.ts'; import { cfg } from '@/bot/cfg.ts'; import { Command } from '@/bot/command.ts'; -import { executeAsk } from '../../features/ei/ask.ts'; +import { executeAsk } from '@/features/ei/ask.ts'; const askCmd: Command = { name: 'ask', diff --git a/src/cmd/utilities/remindme.ts b/src/plugins/utilities/utilities/commands/remindme.ts similarity index 100% rename from src/cmd/utilities/remindme.ts rename to src/plugins/utilities/utilities/commands/remindme.ts diff --git a/src/cmd/utilities/search.ts b/src/plugins/utilities/utilities/commands/search.ts similarity index 100% rename from src/cmd/utilities/search.ts rename to src/plugins/utilities/utilities/commands/search.ts diff --git a/src/cmd/utilities/translate.ts b/src/plugins/utilities/utilities/commands/translate.ts similarity index 100% rename from src/cmd/utilities/translate.ts rename to src/plugins/utilities/utilities/commands/translate.ts diff --git a/src/cmd/utilities/weather.ts b/src/plugins/utilities/utilities/commands/weather.ts similarity index 100% rename from src/cmd/utilities/weather.ts rename to src/plugins/utilities/utilities/commands/weather.ts diff --git a/src/cmd/utilities/wiki.ts b/src/plugins/utilities/utilities/commands/wiki.ts similarity index 100% rename from src/cmd/utilities/wiki.ts rename to src/plugins/utilities/utilities/commands/wiki.ts diff --git a/src/util/cmd/find-command.ts b/src/util/cmd/find-command.ts index fa1261b..e657702 100644 --- a/src/util/cmd/find-command.ts +++ b/src/util/cmd/find-command.ts @@ -1,19 +1,17 @@ -import { Category, Command } from '@/bot/command.ts'; +import { Command } from '@/bot/command.ts'; import { CommandOpts } from '@/bot/config/schema.ts'; import { cfg } from '@/bot/cfg.ts'; import { getCommandConfig } from './get-command-config.ts'; -export type FindResult = { command: Command; category: Category; config: CommandOpts }; +export type FindResult = { command: Command; config: CommandOpts }; -export default function findCommand(cmdName: string, cmdsMap: Map): FindResult | null { - for (const [cat, cmds] of cmdsMap.entries()) { - for (const cmd of cmds) { - const cmdCfg = getCommandConfig(cmd); - const customConf = cfg.commands.configuration?.[cmd.name]; - const aliases = [...cmd.aliases, ...(customConf?.aliases ?? [])]; - if (cmd.name == cmdName || aliases.includes(cmdName)) { - return { command: cmd, category: cat, config: cmdCfg }; - } +export default function findCommand(cmdName: string, cmds: Command[]): FindResult | null { + for (const cmd of cmds) { + const cmdCfg = getCommandConfig(cmd); + const customConf = cfg.commands.configuration?.[cmd.name]; + const aliases = [...cmd.aliases, ...(customConf?.aliases ?? [])]; + if (cmd.name == cmdName || aliases.includes(cmdName)) { + return { command: cmd, config: cmdCfg }; } } return null;