Spraxium logoSpraxium

Comandos e Handlers

O decorator @PrefixCommand define a estrutura, enquanto @PrefixCommandHandler concentra a lógica de execução. Aprenda comandos simples, árvores de subcomandos e registro em módulos para prefix commands.

Separando estrutura e execução

No Spraxium, prefix commands seguem um padrão de duas classes. O decorator @PrefixCommand marca uma classe como definição de comando, descrevendo nome, aliases, schema de argumentos e metadados. O decorator @PrefixCommandHandler marca uma classe separada, que contém o método handle com a lógica real de execução. Essa separação mantém o contrato do comando estável e deixa a evolução do handler independente.

A classe de comando funciona como contêiner de metadados. Ela declara qual é o nome do comando, quais argumentos ele espera e como deve aparecer em saídas de ajuda. A classe handler é o lugar em que você injeta serviços, interage com o Discord e produz respostas. Essa divisão também habilita múltiplos handlers para o mesmo comando por meio do roteamento de subcomandos, em que cada subcomando ganha seu próprio handler focado.

As duas classes precisam ser registradas no mesmo módulo. Classes de comando entram no array commands e classes handler entram no array handlers. Se só comandos forem registrados, nada é executado porque não existe handler para chamar. Se só handlers forem registrados, o registry não terá schema de comando para indexar, então o parser não conseguirá resolver o nome do comando.

Um comando simples

O prefix command mais simples precisa de uma classe de comando com um schema de argumentos e uma classe handler com um método handle. A classe de comando usa decorators de método @PrefixArg sobre um método build para declarar argumentos posicionais. A classe handler usa decorators de parâmetro @PrefixArg no método handle para receber os valores já processados.

import { PrefixArg, PrefixCommand } from '@spraxium/common';

@PrefixCommand({
  name: 'roll',
  aliases: ['r', 'dice'],
  description: 'Roll one or more dice.',
  category: 'Fun',
  usage: '!roll [sides] [count]',
  cooldown: 3,
})
export class RollCommand {
  @PrefixArg.Integer('sides', { required: false, min: 2, max: 1000 })
  @PrefixArg.Integer('count', { required: false, min: 1, max: 25 })
  build() {}
}

Os decorators de método @PrefixArg na classe do comando definem o schema de argumentos. Eles se acumulam de cima para baixo para determinar a ordem posicional: o primeiro decorator vira a posição 0, o segundo vira a posição 1 e assim por diante. No handler, decorators de parâmetro @PrefixArg('name') injetam o valor processado pelo nome. O decorator @Ctx injeta o objeto Message bruto.

Roteamento de subcomandos

Para famílias de comandos como !mod warn, !mod kick e !mod ban, use @PrefixSubcommand para definir cada rota na classe de comando e conecte cada rota a um handler dedicado via @PrefixCommandHandler com a opção subcommand. O primeiro token de argv após o nome do comando é comparado com os subcomandos registrados, e o framework faz o roteamento automaticamente.

import { PrefixArg, PrefixCommand, PrefixSubcommand } from '@spraxium/common';

@PrefixCommand({
  name: 'mod',
  aliases: ['m'],
  description: 'Moderation commands.',
  category: 'Moderation',
  usage: '!mod <warn|kick|ban> <user> [reason]',
})
export class ModCommand {
  @PrefixSubcommand({ name: 'warn', description: 'Warn a user' })
  @PrefixArg.User('target')
  @PrefixArg.Rest('reason', { required: false })
  warn() {}

  @PrefixSubcommand({ name: 'kick', description: 'Kick a user' })
  @PrefixArg.User('target')
  @PrefixArg.Rest('reason', { required: false })
  kick() {}

  @PrefixSubcommand({ name: 'ban', description: 'Ban a user' })
  @PrefixArg.User('target')
  @PrefixArg.Rest('reason', { required: false })
  ban() {}
}

Cada método de subcomando na classe do comando define seu próprio schema de argumentos. O subcomando warn espera uma menção de usuário seguida de um texto restante opcional, enquanto kick e ban seguem o mesmo formato. Cada subcomando recebe sua própria classe handler, o que significa que você pode aplicar guards diferentes, injetar serviços diferentes e tratar erros de forma isolada por subcomando.

Configuração de @PrefixCommand

O decorator @PrefixCommand aceita um objeto de configuração que controla como o comando aparece na saída de ajuda, como é disparado e quais limites operacionais possui. Todos os campos, com exceção de name, são opcionais, mas preencher description, usage e category melhora bastante a saída de qualquer implementação de help.

Registro em módulo

Tanto a classe de comando quanto a classe handler precisam ser registradas em um módulo para que o framework as descubra durante o bootstrap. A classe de comando entra no array commands e a classe handler entra no array handlers. Durante o carregamento do módulo, o framework processa os dois arrays, registra os metadados do comando no PrefixRegistry e conecta as instâncias de handler pelo container de DI.

src/modules/moderation/moderation.module.ts
import { Module } from '@spraxium/common';
import { ModCommand } from './commands/mod.command';
import { RollCommand } from '../fun/commands/roll.command';
import { ModWarnHandler } from './handlers/mod-warn.handler';
import { ModKickHandler } from './handlers/mod-kick.handler';
import { ModBanHandler } from './handlers/mod-ban.handler';
import { RollHandler } from '../fun/handlers/roll.handler';

@Module({
  commands: [ModCommand, RollCommand],
  handlers: [ModWarnHandler, ModKickHandler, ModBanHandler, RollHandler],
})
export class ModerationModule {}