Spraxium logoSpraxium

Comandos y Handlers

El decorador @PrefixCommand define la estructura mientras @PrefixCommandHandler contiene la lógica de ejecución. Aprende comandos simples, árboles de subcomandos y registro en módulos para comandos con prefijo.

Separar estructura de ejecución

En Spraxium, los comandos con prefijo usan un patrón de dos clases. El decorador @PrefixCommand marca una clase como definición de comando que describe el nombre, los alias, el esquema de argumentos y los metadatos. El decorador @PrefixCommandHandler marca una clase separada que contiene el método handle con la lógica real de ejecución. Esta separación mantiene estable el contrato del comando mientras te permite evolucionar el comportamiento del handler de forma independiente.

La clase de comando actúa como contenedor de metadatos. Declara cómo se llama el comando, qué argumentos espera y cómo aparece en la salida de ayuda. La clase handler es donde inyectas servicios, interactúas con Discord y generas respuestas. Esta división también habilita múltiples handlers para el mismo comando mediante el mecanismo de enrutamiento de subcomandos, donde cada subcomando obtiene su propio handler enfocado.

Ambas clases deben registrarse en el mismo módulo. Las clases de comando van en el arreglo commands y las clases handler van en el arreglo handlers. Si solo se registran comandos, nada se ejecuta porque no hay handler que llamar. Si solo se registran handlers, no hay esquema de comando para que el registro lo indexe, así que el parser no puede resolver el nombre del comando.

Un comando simple

El comando con prefijo más simple necesita una clase de comando con esquema de argumentos y una clase handler con un método handle. La clase de comando usa decoradores de método @PrefixArg sobre un método build para declarar argumentos posicionales. La clase handler usa decoradores de parámetro @PrefixArg sobre el método handle para recibir los valores parseados.

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() {}
}

Los decoradores de método @PrefixArg en la clase de comando definen el esquema de argumentos. Se apilan de arriba hacia abajo para determinar el orden posicional: el primer decorador se convierte en la posición 0, el segundo en la posición 1, y así sucesivamente. En el handler, los decoradores de parámetro @PrefixArg('name') inyectan el valor parseado por nombre. El decorador @Ctx inyecta el objeto Message sin procesar.

Enrutamiento de subcomandos

Para familias de comandos como !mod warn, !mod kick y !mod ban, usa @PrefixSubcommand para definir cada ruta en la clase de comando y conecta cada ruta con un handler dedicado mediante @PrefixCommandHandler con una opción subcommand. El primer token de argv después del nombre del comando se compara con los nombres de subcomandos registrados, y el framework enruta automáticamente.

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 en la clase de comando define su propio esquema de argumentos. El subcomando warn espera una mención de usuario seguida de texto libre opcional, mientras que kick y ban siguen la misma forma. Cada subcomando obtiene su propia clase handler, lo que significa que puedes aplicar guards distintos, inyectar servicios distintos y manejar errores de forma independiente por subcomando.

Configuración de @PrefixCommand

El decorador @PrefixCommand acepta un objeto de configuración que controla cómo aparece el comando en la salida de ayuda, cómo se activa y qué límites operativos tiene. Todos los campos excepto name son opcionales, pero proporcionar description, usage y category mejora notablemente la salida de cualquier implementación de comando de ayuda.

Registro en módulo

Tanto las clases de comando como las clases handler deben registrarse en un módulo para que el framework las descubra durante el bootstrap. La clase de comando va en el arreglo commands y la clase handler va en el arreglo handlers. El framework procesa ambos arreglos durante la carga del módulo, registra los metadatos de comando en el PrefixRegistry y conecta las instancias handler mediante el contenedor 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 {}