Fluxos de falha
Quando um prefix command falha em qualquer etapa, o framework gera uma exceção específica que você pode interceptar e formatar. Entender essa taxonomia de falhas ajuda a projetar respostas de erro amigáveis, em vez de deixar erros brutos vazarem para o chat. Cada tipo de falha corresponde a uma classe de exceção distinta com seu próprio hook de layout.
Layouts de exceção
Exception layouts permitem definir respostas personalizadas para tipos específicos de exceção. Em vez de escrever blocos try-catch em todo handler, você registra um layout uma vez e o framework o aplica automaticamente sempre que aquele tipo de exceção é lançado durante a execução de um prefix command. Isso mantém os handlers limpos e padroniza a formatação de erros em todo o bot.
Um layout recebe o Message original e a exceção lançada, então você pode montar embeds de erro ricos em contexto sobre o que deu errado. O helper defineExceptionLayout cria uma factory de layout que o framework descobre por meio do registro do módulo.
import { defineExceptionLayout } from '@spraxium/core';
import { ArgumentException } from '@spraxium/common';
export const ArgumentExceptionLayout = defineExceptionLayout(
ArgumentException,
async (message, exception) => {
await message.reply({
content: `Invalid argument: **${exception.argName}** - ${exception.message}`,
});
},
);Registre os layouts no módulo ao lado dos comandos e handlers. O framework os coleta durante a inicialização do módulo e os aplica automaticamente aos tipos de exceção corretos.
import { Module } from '@spraxium/common';
import { RollCommand } from './commands/roll.command';
import { RollHandler } from './handlers/roll.handler';
import { ArgumentExceptionLayout } from './layouts/argument-exception.layout';
import { CommandNotFoundLayout } from './layouts/not-found.layout';
@Module({
commands: [RollCommand],
handlers: [RollHandler],
layouts: [ArgumentExceptionLayout, CommandNotFoundLayout],
})
export class PrefixModule {}Inspeção do dispatcher
O PrefixDispatcher expõe métodos para inspecionar a árvore de comandos registrada em runtime. O método getCommandInfo retorna um array de objetos PrefixCommandInfo com nomes de comandos, descrições, listas de subcomandos e schemas de argumentos. Essa é a base para construir comandos de ajuda dinâmicos.
O dispatcher também expõe getResolved para os vínculos comando-handler já resolvidos, size para o número total de entradas registradas, commandCount para comandos de nível superior distintos e guildPrefixes para o gerenciador de prefixos por guild. Esses métodos de introspecção são úteis em painéis administrativos, health checks e ferramentas de depuração.
import { Ctx, PrefixCommandHandler } from '@spraxium/common';
import { PrefixDispatcher } from '@spraxium/core';
import type { Message } from 'discord.js';
import { HelpCommand } from '../commands/help.command';
@PrefixCommandHandler(HelpCommand)
export class HelpHandler {
constructor(private readonly dispatcher: PrefixDispatcher) {}
async handle(@Ctx() message: Message): Promise<void> {
const commands = this.dispatcher.getCommandInfo();
const lines = commands.map((cmd) => {
const subs = cmd.subcommands?.map((s) => s.name).join(', ');
return subs
? `**${cmd.name}** - subcommands: ${subs}`
: `**${cmd.name}**`;
});
await message.reply(lines.join('\n') || 'No commands registered.');
}
}Guards com prefix commands
Prefix commands aceitam o mesmo sistema de guards usado por slash commands e listeners. Decore uma classe handler com @UseGuards para anexar um ou mais guards que rodam antes da execução do handler. Se qualquer guard retornar false ou lançar uma exceção, o handler é ignorado. Guards são úteis para verificações de permissão, restrições por cargo e filtros por tipo de canal.
Guards recebem o contexto de execução, que para prefix commands inclui o objeto Message original. Você pode reutilizar as mesmas classes de guard em prefix commands, slash commands e listeners sem modificações. Esse é um dos benefícios da arquitetura unificada de guards.
import { CanActivate, ExecutionContext } from '@spraxium/common';
export class ModeratorGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const message = context.getMessage();
const member = message.member;
if (!member) return false;
return member.permissions.has('ModerateMembers');
}
}