Spraxium logoSpraxium

Argumentos y Parsing

El parser de prefijos tokeniza mensajes y coerciona argumentos a 14 salidas tipadas. Aprende cada tipo de @PrefixArg, sus opciones de validación y cómo funciona el pipeline de parsing.

Cómo la entrada del mensaje se convierte en argv

Después de detectar el prefijo, el parser elimina el prefijo coincidente y tokeniza el texto restante. El primer token se convierte en el nombre del comando y todo lo que sigue se convierte en el arreglo argv. La tokenización soporta strings entre comillas dobles, por lo que "hello world" se trata como un único token en lugar de dos. Esto permite que los usuarios pasen argumentos de varias palabras sin usar escapes.

El tokenizador divide por espacios en blanco por defecto. Cuando encuentra una comilla doble de apertura, recopila todos los caracteres hasta la comilla de cierre y los emite como un solo token. Si falta la comilla de cierre, el resto de la entrada se convierte en el token actual. Este comportamiento es intencional y evita desalineaciones silenciosas de argumentos.

Para comandos con subcomandos, el primer token de argv se compara con los nombres de subcomandos registrados antes de que empiece el parsing de argumentos. Si coincide con un subcomando, el framework desplaza el arreglo argv y usa el esquema de argumentos específico de ese subcomando en lugar del esquema raíz. Esto significa que el parser resuelve el enrutamiento antes de resolver argumentos.

Decoradores de método @PrefixArg

El namespace @PrefixArg proporciona 14 decoradores de método tipados que se apilan sobre un método build (para comandos simples) o sobre métodos @PrefixSubcommand (para comandos con subcomandos). Cada decorador declara un argumento posicional con su nombre, tipo y reglas de validación opcionales. Los decoradores se apilan de arriba hacia abajo para determinar el orden posicional.

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

@PrefixCommand({ name: 'roll', cooldown: 3 })
export class RollCommand {
  @PrefixArg.Integer('sides', { required: false, min: 2, max: 1000 })
  @PrefixArg.Integer('count', { required: false, min: 1, max: 25 })
  build() {}
}

Tipos de argumentos soportados

El framework ofrece 14 tipos de argumentos que cubren los patrones de entrada más comunes en bots de Discord. Cada tipo tiene su propio parser que convierte el token de texto sin procesar en una salida tipada. Si la conversión falla, el parser lanza una ArgumentException con un mensaje de error descriptivo.

Opciones de validación

Cada decorador @PrefixArg acepta un objeto opcional de configuración con reglas de validación. Estas reglas se aplican durante la fase de parsing antes de que el handler reciba el valor. Si falla cualquier validación, el parser lanza una ArgumentException que incluye el nombre del argumento, la restricción esperada y la entrada recibida.

Orden del pipeline de validación

El parser de argumentos aplica validaciones en un orden específico para cada argumento. Entender este orden te ayuda a diseñar esquemas que produzcan mensajes de error claros, porque la primera comprobación que falla es la que verá el usuario. El pipeline hace short-circuit en el primer fallo, así que las comprobaciones posteriores solo se ejecutan si todas las anteriores pasan.

Comprobación de presencia

Si el argumento es requerido y el token sin procesar falta o está vacío, el parser lanza una ArgumentException inmediatamente. Los argumentos opcionales con tokens ausentes reciben undefined y omiten todas las comprobaciones restantes. Esta es la primera puerta que evita que valores vacíos lleguen a los parsers específicos de tipo.

Validación personalizada

Si se define una regex validation.pattern, la entrada sin procesar debe coincidir con ella. Si se define una función validation.validate, recibe la entrada sin procesar y debe devolver true o un string de error. Ambas comprobaciones se ejecutan sobre el texto en bruto antes de cualquier coerción de tipo, por lo que puedes validar restricciones de formato de forma independiente del parsing de tipo.

Comprobación de choices

Si se define un arreglo choices, la entrada sin procesar debe coincidir exactamente con uno de los valores permitidos. Esto actúa como una restricción tipo enum y es útil para valores de argumentos que deben provenir de un conjunto fijo como nombres de estado, tipos de acción o selecciones de modo.

Coerción de tipo y límites

El parser convierte el token sin procesar al tipo objetivo usando el parser específico de ese tipo. Para tipos numéricos, las comprobaciones de límites (min, max) se ejecutan después de una coerción exitosa. Para tipos string, se aplican las comprobaciones de longitud (minLength, maxLength). Si falla la coerción o los límites, el parser lanza una ArgumentException.

Decorador de parámetro @PrefixArg

En la clase handler, la función @PrefixArg (sin el namespace) es un decorador de parámetro que inyecta un valor de argumento parseado por nombre. El nombre debe coincidir con un argumento declarado en el esquema del comando. El decorador @Ctx inyecta el objeto Message sin procesar. Juntos, permiten que el handler reciba parámetros completamente tipados sin parsing manual.

src/modules/fun/handlers/roll-command.handler.ts
import { Ctx, PrefixArg, PrefixCommandHandler } from '@spraxium/common';
import type { Message } from 'discord.js';
import { RollCommand } from '../commands/roll.command';

@PrefixCommandHandler(RollCommand)
export class RollHandler {
  async handle(
    @Ctx() message: Message,
    @PrefixArg('sides') sides: number | undefined,
    @PrefixArg('count') count: number | undefined,
  ): Promise<void> {
    const faces = sides ?? 6;
    const rolls = count ?? 1;
    const results = Array.from(
      { length: rolls },
      () => Math.floor(Math.random() * faces) + 1,
    );
    await message.reply(`Rolled ${rolls}d${faces}: [${results.join(', ')}]`);
  }
}