Qué es un comando con prefijo
Un comando con prefijo es un par de clases que reacciona a un mensaje de texto que comienza con un carácter configurable como ! o ?. Cuando un usuario envía !roll 20, el framework detecta el prefijo, extrae el nombre del comando, analiza los argumentos, ejecuta guards y despacha el mensaje al handler correcto. Este es el modelo tradicional de comandos para bots de Discord anterior a los slash commands, y sigue siendo útil para flujos de trabajo rápidos de operadores, herramientas internas de moderación y bots donde los usuarios ya conocen atajos de comandos.
La diferencia clave entre los comandos con prefijo y los slash commands es qué los activa. Los slash commands se activan mediante el sistema de interacciones integrado de Discord, con autocompletado y validación. Los comandos con prefijo se activan por mensajes de texto sin procesar, lo que significa que el framework debe encargarse por sí mismo del parsing, la coerción de argumentos y el reporte de errores. Algunos bots necesitan ambos, y Spraxium permite registrar comandos con prefijo y slash commands en el mismo módulo sin conflictos.
Los comandos con prefijo en Spraxium no son una característica heredada añadida al final. Son clases completas gestionadas por el contenedor de inyección de dependencias, registradas en módulos y conectadas automáticamente al cliente de Discord durante el bootstrap. Esto significa que tus handlers de comandos pueden recibir servicios inyectados, participar en el pipeline de guards, beneficiarse del seguimiento de cooldown y usar el mismo manejo de excepciones que usan los slash commands.
Cuándo siguen teniendo sentido los comandos con prefijo
Los comandos con prefijo siguen encajando muy bien en bots usados por equipos de staff, herramientas internas, comunidades heredadas con mucha migración, o flujos donde los atajos escritos importan más que la interfaz de selector de comandos de Discord. También son útiles cuando ya existen hábitos basados en mensajes y mover todo a slash commands añadiría fricción sin mucho beneficio.
Son menos adecuados cuando necesitas alta descubribilidad para usuarios casuales, validación rica de opciones en el cliente de Discord o entradas con mucho uso de autocompletado. En esos casos, los slash commands suelen ser la mejor opción por defecto.
Un ejemplo básico de comando
Aquí tienes dos clases que muestran el patrón de dos clases. La primera es una definición de comando que declara el nombre, los alias y el esquema de argumentos mediante decoradores @PrefixArg. La segunda es un handler que recibe los argumentos parseados y el Message sin procesar mediante @Ctx. Juntas, cubren la estructura mínima que necesitas para crear un comando con prefijo funcional.
import { PrefixCommand } from '@spraxium/common';
@PrefixCommand({
name: 'ping',
aliases: ['p'],
description: 'Check bot latency.',
category: 'Fun',
usage: '!ping',
cooldown: 3,
})
export class PingCommand {
build() {}
}Observa que la clase de comando es puramente declarativa: describe cómo se llama el comando, qué alias acepta y qué cooldown aplica. La clase handler contiene la lógica de ejecución y recibe dependencias mediante decoradores. Para comandos con argumentos, agregas decoradores @PrefixArg en el método build del comando y decoradores de parámetro @PrefixArg equivalentes en el método handle del handler. La siguiente página cubre en detalle los esquemas de argumentos.
Flujo en tiempo de ejecución
En runtime, el flujo es directo:
- El framework detecta el prefijo configurado.
- Extrae el nombre del comando y tokeniza el texto restante.
- Resuelve la definición del comando y parsea los argumentos.
- Ejecuta guards y comprobaciones de cooldown.
- Invoca el handler registrado.
Ese pipeline es la razón por la que la definición del comando y la ejecución del comando se separan en dos clases. Una clase describe el esquema; la otra ejecuta la lógica sobre los valores parseados.
Registrar comandos en un módulo
Los comandos con prefijo no se activan solo por existir en tu código fuente. Debes registrar la clase de comando en el arreglo commands del módulo y la clase handler en el arreglo handlers para que el framework las conozca y las conecte durante el bootstrap. Sin ambos registros, tu comando será invisible para el parser o no tendrá un handler al que despachar.
import { Module } from '@spraxium/common';
import { PingCommand } from './commands/ping.command';
import { PingHandler } from './handlers/ping-command.handler';
@Module({
commands: [PingCommand],
handlers: [PingHandler],
})
export class FunModule {}El módulo debe importarse desde tu módulo raíz o desde otro módulo en la cadena de imports. Si un handler depende de un servicio, ese servicio debe estar disponible en el scope de providers del módulo o exportarse desde un módulo importado, como cualquier otra clase inyectable. Un módulo típico de prefijos organiza comandos, handlers y guards en carpetas propias dentro del directorio del dominio.
Esta estructura mantiene las definiciones de comandos y los handlers en carpetas dedicadas, y el archivo del módulo en la raíz del dominio. A medida que tu bot crece, puedes añadir más archivos de comandos y handlers sin cambiar la estructura de carpetas. El archivo del módulo es el único lugar que declara qué comandos y handlers están activos.
Los comandos con prefijo también se benefician de mantenerse orientados por dominio. Un módulo de moderación debería ser dueño de sus comandos con prefijo de moderación, y un módulo de economía debería ser dueño de sus comandos de economía. Eso mantiene alias, guards, handlers y servicios de soporte cerca del mismo contexto de negocio.
Dos clases por comando
Cada comando con prefijo requiere tanto una clase de comando como una clase handler registradas en el mismo módulo. La clase de comando declara la estructura y los metadatos. La clase handler contiene la lógica de ejecución y las dependencias inyectables. Si olvidas registrar cualquiera de las dos, el comando no funcionará en runtime.
Si planeas soportar comandos con prefijo y slash commands, mantén la lógica de negocio en servicios compartidos y deja que cada superficie de comando actúe como un punto de entrada delgado. Eso evita duplicar el comportamiento real solo porque existan dos estilos de comando.