Spraxium logoSpraxium

Visión general de listeners

Los listeners reaccionan a eventos de Discord como mensajes, entradas de miembros y preparación del cliente. Aprende el modelo de listeners, cómo se conectan a los eventos y cómo se registran en módulos.

Qué es un listener

Un listener es una clase que reacciona a eventos de Discord. Cuando algo ocurre en Discord, como cuando un usuario envía un mensaje, un miembro entra a un servidor o el bot termina su inicio de sesión, Discord.js dispara un evento. Una clase listener en Spraxium captura ese evento y ejecuta tu código como respuesta. Sin listeners, tu bot no podría reaccionar a nada de lo que ocurre fuera de los comandos explícitos.

La diferencia clave entre listeners y comandos es qué los activa. Los comandos se activan cuando un usuario escribe un comando slash o un comando prefix. Los listeners se activan por eventos de Discord que ocurren en segundo plano, como envío de mensajes, entrada o salida de miembros, adición de reacciones o cuando el cliente queda listo. Algunos de estos eventos implican interacciones de usuario que pueden recibir respuestas, y otros son eventos en segundo plano que ocurren de forma silenciosa.

Los listeners en Spraxium no son callbacks en bruto. Son clases completas administradas 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 clases listener pueden recibir servicios inyectados, participar en el pipeline de guards para eventos compatibles y beneficiarse del mismo manejo de excepciones que usan los comandos.

Cuándo un listener es la herramienta correcta

Usa un listener cuando la acción comienza por un evento de Discord y no por un comando explícito. Buenos ejemplos incluyen flujos de bienvenida al entrar un miembro, analítica en creación de mensajes, actualizaciones de caché en cambios de roles o tareas de arranque ligadas al evento ready del cliente.

Si la acción debe ocurrir porque un usuario la solicitó directamente, prefiere un manejador de slash command, prefix command, botón, select o modal. Así mantienes claramente separado el comportamiento impulsado por eventos del comportamiento invocado por usuarios.

Un ejemplo básico de listener

Aquí tienes dos listeners que muestran los dos modos de enlace. El primero usa @Once para reaccionar a ClientReady una sola vez cuando el bot se conecta. El segundo usa @On para reaccionar a cada evento MessageCreate durante toda la vida del bot. Juntos cubren los dos patrones más comunes que vas a usar.

import { Events, Listener, Once } from '@spraxium/common';
import { Logger } from '@spraxium/core';
import type { Client } from 'discord.js';

@Listener(Events.ClientReady)
export class ReadyListener {
  private logger: Logger = new Logger(ReadyListener.name);

  @Once()
  onReady(client: Client<true>): void {
    this.logger.info(`Client is ready as ${client.user.tag}`);
  }
}

Observa que el parámetro del método recibe directamente el payload del evento de Discord.js. Para ClientReady, el payload es el objeto Client. Para MessageCreate, el payload es el objeto Message. Cada evento de Discord tiene su propio tipo de payload, y tu método recibe exactamente lo que Discord.js proporciona para ese evento específico.

Registrar listeners en un módulo

Los listeners no se activan solo por existir en tu código fuente. Debes registrarlos en el arreglo listeners de un módulo para que el framework los conozca y pueda conectarlos al cliente de Discord durante el bootstrap. Sin este registro, tu clase listener se ignorará por completo.

src/events/events.module.ts
import { Module } from '@spraxium/common';
import { ReadyListener } from './listeners/ready.listener';
import { MessageCreateListener } from './listeners/message-create.listener';

@Module({
  listeners: [ReadyListener, MessageCreateListener],
})
export class EventsModule {}

El módulo en sí debe importarse en tu módulo raíz o en otro módulo dentro de la cadena de imports. El framework procesa los módulos durante el bootstrap, resuelve sus arreglos de listeners, crea cada clase listener mediante el contenedor de DI y luego las enlaza al cliente de Discord. Si un listener depende de un servicio, ese servicio debe estar disponible en el alcance de providers del módulo o exportado desde un módulo importado, igual que cualquier otra dependencia inyectable.

Cómo el bootstrap conecta listeners

El proceso de bootstrap maneja listeners en una secuencia específica que importa para entender cuándo tu listener queda activo. Primero, el framework lee el arreglo listeners de cada módulo. Segundo, crea cada clase listener mediante el contenedor de DI, resolviendo dependencias del constructor. Tercero, el ListenerRegistry interno escanea cada instancia en busca de métodos decorados con @On o @Once y los agrupa por nombre de evento. Cuarto, el ListenerBinder toma esos grupos y llama a los métodos on u once del cliente de Discord.js para crear los enlaces reales. Finalmente, cuando se dispara un evento de Discord, el ListenerInvoker llama a cada handler registrado para ese evento.

Estructura típica de un módulo de listeners
ready.listener.ts
message-create.listener.ts
member.listener.ts
events.module.ts

Esta estructura mantiene todas las clases listener en una carpeta dedicada y el archivo del módulo en la raíz de events. A medida que tu bot crece, puedes agregar más archivos listener sin cambiar la estructura de carpetas. Cada listener maneja un evento, y el archivo del módulo es el lugar único que declara qué listeners están activos.

Errores comunes que debes evitar

  1. Poner lógica de negocio pesada directamente en el listener en lugar de delegar a un servicio.
  2. Olvidar los gateway intents requeridos y asumir que el enlace del listener está roto.
  3. Registrar la clase listener, pero no importar el módulo dueño dentro del grafo de módulos activos.
  4. Pasar objetos completos de Discord.js a servicios profundos cuando solo se necesitan algunos IDs.

Una clase por evento

Cada clase listener se enlaza exactamente a un evento de Discord mediante el decorador Listener. Si necesitas escuchar múltiples eventos, crea clases listener separadas para cada uno. Esto mantiene cada clase enfocada y fácil de encontrar al depurar.

Esa separación se vuelve muy valiosa rápidamente en bots grandes. El cableado de eventos se mantiene fácil de inspeccionar, y la lógica de dominio sigue siendo reutilizable desde otros puntos de entrada como comandos, rutas HTTP o tareas programadas.

Página siguiente

Continúa con Decoradores y ciclo de vida.