Saltar al contenido principal

Los 3 tipos

before — antes de procesar

Olvidar next() bloquea el eventoUn hook before que vuelve sin llamar a next() detiene la cadena y el evento, igual que ctx.abort(): el mensaje no se enruta (un 'send' se omite, un evento del provider no se emite). Un hook que solo observa o reescribe debe terminar siempre con await next(). Para bloquear a propósito (rate limiting, filtros), vuelve sin next() o llama a ctx.abort() (ver abort() más abajo); abort() se lee más explícito.En 'message', next() ejecuta los hooks before restantes; el enrutado (el comando, la conversación o el handler) empieza cuando todos los hooks before han vuelto. Para actuar después del handler, usa un hook after.Llama a next() una sola vez. Un hook que vuelve a llamarlo conserva el primer despacho (el resto de la cadena corre una vez y cada next() posterior devuelve la misma promesa) y el mal uso se informa en el evento 'error'.

after — después de procesar

onError — cuando algo falla

También existe un bot.hook(event, phase, handler, priority?) genérico si necesitas registrar una fase dinámicamente en lugar de llamar directamente a before/after/onError. El error de un handler se emite siempre en bot.on('error', ({ error, ctx }) => ...), haya o no un hook onError('message') registrado.

Casos de uso

Logging

Los ids de chat son teléfonos o emails y los cuerpos son mensajes privados: mantenlos fuera de tus logs, o hashéalos como hace el bot con logIds.

Rate limiting

Filtrar grupos

Para mensajes de grupo suele ser más simple la opción integrada groupMode, que filtra antes incluso de que corran los hooks.

Bloquear usuarios

Para una lista de bloqueo de verdad usa bot.blacklist / bot.whitelist: descartan el mensaje antes incluso de persistirlo y también cubren botones y llamadas. Ver Handoff y listas de acceso.

Transformar o bloquear mensajes salientes

'send' se dispara en cada envío saliente, ya sea que venga de ctx.reply/ctx.photo/… o de bot.send. El payload es { chatId, content, options } — content es el OutboundContent neutral de transporte ({ kind: 'text', text }, { kind: 'image', media, caption }, …); mútalo para transformar un mensaje, o aborta para saltarte el envío por completo:
Un envío abortado resuelve { skipped: true } en lugar de rechazar (una difusión lo cuenta como skipped).

Ediciones y salida en streaming

Algunos payloads llevan una marca para que un hook los distinga de un envío normal: Un hook puede reescribir payload.content o hacer abort() en cualquiera de ellos. Una edición abortada resuelve skipped y no envía un mensaje nuevo en su lugar. Una edición degradada (el provider no puede editar) sale como un envío normal, sin edit.

abort() — detener el pipeline

abort() detiene los hooks before restantes y el propio evento. Volver sin next() tiene el mismo efecto; abort() sirve además desde código que no tiene next a mano (un helper que solo recibe ctx).

Prioridad

Contextos tipados

El contexto que recibe un hook se tipa por el nombre del evento (HookContextMap): 'message' da un MessageContext, 'send' un SendHookContext ({ chatId, content, options?, stream?, edit? }), 'call' un CallEvent, 'reaction' un InboundReaction, etc.; los hooks before reciben además abort(). No hace falta anotar nada:
Un evento propio (uno para el que un plugin corre sus propios hooks) es unknown salvo que anotes el handler, pases el argumento de tipo o amplíes HookContextMap (declaration merging):
HookHandler<TCtx = unknown> y HookErrorHandler<TCtx = unknown> nombran el tipo de un handler; un handler tipado para un evento (HookHandler<MessageContext>) no se puede registrar en otro ('send').

Eventos disponibles

Los eventos de conexión a nivel de provider (qr, ready, connection, …) son listeners bot.on(...) normales, no fases de hooks — ver Conceptos básicos. Lo mismo vale para 'conversation:started' / 'conversation:ended' (ver Operación).

Siguientes pasos