Skip to main content
Esta página compara todos los providers del repositorio: cómo recibe eventos cada uno y qué puede y qué no puede hacer. Cada canal tiene su propia página con la configuración, las opciones y los límites (ver Providers). Instala el core y el canal que necesites:
Prueba un canal sin cuentaEl sandbox de plataformas ejecuta tu bot sobre los providers reales de la Cloud API, Telegram, Twilio, Discord y otros contra simuladores locales, y el simulador en la terminal te deja chatear con él en la terminal. Ninguno necesita una app, un número de teléfono ni una URL pública.

Paquetes publicados y sin publicar

@botmesh/tiktok está implementado y con tests, pero es privado: no está en npm y su API puede cambiar. El estado de publicación, los dist-tags y cómo instalar cada paquete están en Paquetes y versiones.

Elegir un canal

WhatsApp tiene varias opciones:
  • @botmesh/baileys: vincula el bot como un dispositivo de WhatsApp Web (QR o código de emparejamiento) y abre su propio socket. Sin servidor público ni webhook, con grupos y encuestas, sin coste por mensaje. Es un cliente no oficial: automatizar una cuenta puede infringir los términos de WhatsApp y acabar en el bloqueo del número. Deja activado el rate limiter.
  • @botmesh/meta: la WhatsApp Cloud API oficial. Botones, listas, plantillas y formularios con WhatsApp Flows nativos, sin riesgo de baneo. Necesita un webhook HTTPS público, no tiene grupos y los mensajes libres solo funcionan dentro de la ventana de atención de 24 horas.
  • @botmesh/evolution / @botmesh/wppconnect: ya tienes desplegado un Evolution API o un WPPConnect Server. El bot habla con ese gateway por REST y recibe sus webhooks. Ambos gateways manejan WhatsApp Web, así que valen las mismas advertencias que con Baileys (no oficial, sin botones nativos fiables). Lo común (cliente REST, media, reconciliación LID/PN) vive en @botmesh/wa-gateway-kit, una dependencia pública de ambos; se instala el canal, no el kit (salvo que escribas un transporte para otro gateway).
  • @botmesh/twilio / @botmesh/gupshup: envías WhatsApp a través de la cuenta de ese proveedor. Admiten plantillas; Twilio cubre además SMS/MMS con la misma clase.
  • @botmesh/gohighlevel: el bot responde a los contactos del CRM HighLevel por el canal que usaron (SMS, WhatsApp, Email, Facebook, Instagram, Live chat…).
Otros canales: @botmesh/telegram (long polling por defecto, webhook opcional), @botmesh/discord (socket del gateway), @botmesh/messenger e @botmesh/instagram (webhooks de Meta), @botmesh/email (cada hilo de correo es un chat) y @botmesh/tiktok (comentarios en tus propios videos y DMs).

Socket, polling o webhook

  • Socket / polling (el provider abre la conexión): Baileys, Discord, Telegram en modo polling, Email (IMAP IDLE). No hay nada que exponer a internet.
  • Webhook (capabilities.pushIngress: true): Cloud API, Messenger, Instagram, Telegram en modo webhook, Evolution, WPPConnect, Twilio, HighLevel, Gupshup, TikTok. Tu servidor HTTP recibe el POST y le pasa el body crudo y las cabeceras a provider.ingest(), que verifica la firma o el token y devuelve el status HTTP con el que responder. @botmesh/server resuelve ese cableado (body crudo, límites de tamaño, timeouts, límite de concurrencia, endpoints de salud) para node:http, Express, Fastify y Hono.
La forma recomendada es @botmesh/server (pnpm add @botmesh/server): una ruta por provider, y el handshake GET y los POST firmados quedan resueltos:
Sin él basta una ruta en cualquier framework. Lee el body como bytes (la firma cubre los bytes exactos, así que nunca pases un objeto JSON re-serializado) y responde con el status que devuelve ingest():
Los demás providers por webhook siguen el mismo patrón: Messenger e Instagram tienen el mismo handshake GET, Telegram no tiene ninguno, y los providers cuya autenticación cubre la URL también la reciben (el ingest(body, headers, url) asíncrono de WPPConnect, ingestRequest({ url, body, headers }) en Twilio y Gupshup). ingest() no espera a tus handlers, así que la plataforma recibe su respuesta enseguida. El constructor de todo provider por webhook (Telegram, solo en modo webhook) lanza un error si falta su credencial de webhook (app secret, auth token o token compartido), salvo que pases allowUnsignedWebhooks: true, pensado solo para desarrollo local aislado. HighLevel es la excepción: su clave pública de firma viene incluida.

Matriz de capacidades

Los valores salen de la declaración capabilities de cada provider. Una función marcada “no” se degrada, no se rompe: los botones y las listas pasan a un menú de texto numerado, ctx.edit envía un mensaje nuevo, ctx.react resuelve { skipped: true } y ctx.typing no hace nada (ver Mensajes y media). Contenido. Lo que cada canal envía de forma nativa. Entrada y presencia. Cómo llegan los eventos y qué indicadores de presencia funcionan.
  • Enviar reacciones solo describe el envío. Varios providers reciben igualmente reacciones como eventos reaction (Messenger, Instagram, Gupshup, TikTok…).
  • ctx.stream: borrador = vistas previas nativas en borrador y un mensaje final (chats privados de Telegram; ediciones en el resto), edición = un mensaje que se edita a medida que crece el texto, fragmentos = sin vista previa en vivo, mensajes del tamaño de una frase con indicador de escritura (streaming: 'none'). Ver Respuestas en streaming.
  • Formularios: nativo envía flow().form() como un WhatsApp Flow; campo a campo pregunta cada campo con mensajes de texto.
  • textFormat (no es columna): el formato de texto que muestra el canal, para que quien genere Markdown, como @botmesh/ai, lo convierta. 'whatsapp' (*negrita*, _cursiva_, ~tachado~): Baileys, Cloud API, Evolution, WPPConnect, Gupshup. 'plain' (sin formato): Telegram (se envía sin parse_mode), Messenger, Instagram, TikTok. 'markdown': Discord. Sin declarar: Email. Los providers que sirven varios canales deciden por chat con el método opcional textFormatFor(chatId), que gana a capabilities.textFormat y llega a los handlers como ctx.textFormat: Twilio devuelve 'whatsapp' en los chats whatsapp: y 'plain' en SMS; HighLevel sigue el canal de respuesta del contacto ('whatsapp' en WhatsApp, 'plain' en SMS, Email, FB, IG, Live chat, GMB y custom).
  • Plantillas es el tipo de contenido template: plantillas de la Cloud API, Content Templates de Twilio (ContentSid), templateId de HighLevel, plantillas de Gupshup. El resto de providers lo rechaza con UnsupportedContentError.
  • Todos los providers salvo Baileys aceptan el tipo de contenido native (ctx.sendNative('<id del provider>', payload)) como vía de escape a la API propia del canal, con un payload específico de cada provider. Baileys lo rechaza con UnsupportedContentError. Discord, Messenger, Instagram, Evolution, WPPConnect, Twilio, Gupshup, HighLevel, Email y TikTok también rechazan un payload dirigido a otro id de provider en lugar de enviarlo.

Contrato común de los canales

Las mismas reglas valen en todos los canales, así un bot se comporta igual donde corra. Quien escribe un provider las comprueba con los helpers de @botmesh/core/testing (ver Testing).
  • Los timestamps son milisegundos epoch. ctx.timestamp, InboundMessage.timestamp, InboundInteraction.timestamp y DeliveryStatus.timestamp son ms epoch en todos los canales. Los transportes que informan segundos (WhatsApp, Telegram, la Cloud API) se multiplican por 1000; uno que no informa hora (Twilio) usa la hora de recepción, nunca 0. Telegram no envía la hora de pulsación de un botón, así que un callback query lleva la hora de recepción.
  • El texto largo se divide. Cada canal declara su límite de texto y el core divide un envío text más largo en varios mensajes (ver Mensajes; splitLongText: false lo desactiva).
  • Las operaciones no soportadas no existen. Una operación que el transporte no puede hacer no se implementa: falta el método opcional (typing, markRead, editMessage, deleteMessage) y su flag de capacidad es false, así que ctx.typing(), ctx.markRead() y ctx.delete() no hacen nada. Un método que existe hace la operación o rechaza; nunca es un sustituto silencioso.
  • La presencia es de mejor esfuerzo sin conexión. typing() y markRead() resuelven sin tocar el transporte mientras el último estado de conexión del provider no es 'ready' (antes de que start() conecte, durante una reconexión, después de stop()): sin error, sin cola y sin reintento posterior. El bot aplica la misma regla antes de llamarlos, registra la señal omitida en debug y la cuenta en metrics().presenceSkipped (botmesh_presence_skipped_total en toPrometheus()). Ya conectado, un fallo del transporte sigue rechazando. assertPresenceWhileDisconnected() lo comprueba en un provider.
  • Demasiadas opciones rechazan. Un transporte que muestra menos botones, filas o respuestas de encuesta de las que enviaste rechaza con UnsupportedContentError (Cloud API, Messenger, Instagram, Discord), salvo que tenga una degradación documentada (texto numerado en Baileys, Evolution, WPPConnect, Gupshup, TikTok).
  • Una ruta local de media que no se puede leer rechaza con UnsupportedContentError (el error de fs como cause), nunca con un ENOENT crudo.
  • Las esperas tienen tope. Cada llamada REST tiene timeout (requestTimeoutMs en la Cloud API, Telegram, Discord, Messenger e Instagram) y un RateLimitedError nunca pide más de 24 h.
  • Una edición conserva el id original. Donde el transporte informa de ediciones de texto (Baileys, edited_message de Telegram, MESSAGE_UPDATE de Discord), la edición llega como mensaje con el texto nuevo y el ref.id original; cada uno de esos paquetes exporta isEditedMessage(ctx) y una opción editedMessages: 'ignore'. El dedupe del core deja pasar la edición porque su contenido cambió y descarta una re-entrega de la misma edición.
  • Los votos de encuesta tienen una sola forma. Baileys y Telegram emiten cada voto como evento 'poll' (bot.on('poll', (vote) => …)): vote.ref es el mensaje de la encuesta, vote.senderId quien votó y vote.options el recuento actual de cada opción con sus voters.
  • Un solo esquema de nombres para los helpers de bajo nivel. El mapeador de errores de cada provider es to<Canal>Error (toTelegramError, toDiscordError, toTwilioError…; los canales de Meta comparten toGraphError de @botmesh/graph-core). Telegram, TikTok y la Cloud API conservan su antiguo toProviderError (y Email su toEmailProviderError) como alias deprecados; los canales que se publican por primera vez con la beta solo tienen los nombres nuevos. ingest() devuelve el estado HTTP con el que responder (un number); WPPConnect lo devuelve como Promise<number> (resuelve los LID desconocidos contra el gateway antes de emitir, sin esperar a tus handlers) y el ingest() de email recibe un mensaje crudo, no un webhook, y devuelve un EmailIngestResult.

Providers

Cada canal tiene su propia página con la configuración, las opciones y los límites.

@botmesh/baileys

WhatsApp Web a través de Baileys: vinculación por QR o código, socket propio, grupos y encuestas. Ver Baileys (WhatsApp Web).

@botmesh/meta (WhatsApp Cloud API)

La WhatsApp Cloud API oficial: botones, listas, plantillas y formularios de WhatsApp Flows nativos sobre webhooks. Ver WhatsApp Cloud API.

@botmesh/telegram

Telegram Bot API, por long polling o webhook. Ver Telegram.

@botmesh/messenger

Facebook Messenger a través de la Send API de Graph y los webhooks de Meta. Ver Messenger.

@botmesh/instagram

DMs de Instagram de una cuenta profesional (Messenger API for Instagram). Ver Instagram.

@botmesh/discord

Gateway de Discord (WebSocket) para los eventos y REST para enviar. Ver Discord.

@botmesh/evolution

Evolution API v2, un gateway de WhatsApp self-hosted: REST de salida, webhook de la instancia de entrada. Ver Evolution API.

@botmesh/wppconnect

WPPConnect Server, un gateway de WhatsApp Web self-hosted: REST con token Bearer, webhooks con token en la URL. Ver WPPConnect.

@botmesh/twilio

Twilio Programmable Messaging: WhatsApp y SMS/MMS con una sola clase. Ver Twilio.

@botmesh/gohighlevel

HighLevel (LeadConnector) Conversations API: responde a los contactos del CRM por el canal que usaron. Ver HighLevel (GoHighLevel).

@botmesh/gupshup

Gupshup WhatsApp API, con callbacks autenticados por un token compartido. Ver Gupshup.

@botmesh/email

Email por IMAP IDLE y SMTP: cada hilo de correo es un chat. Ver Email.

@botmesh/tiktok (no está en npm)

TikTok API for Business: comentarios en tus propios vídeos y mensajes directos. Ver TikTok.

Actualizar desde una alpha de 2.0

Los bots nuevos pueden saltarse esta sección. Si tenías un bot sobre una de las primeras alphas de 2.0, el contrato común de los canales cambió algunos comportamientos; el detalle por paquete está en Notas de actualización: canales y plugins y Migrar a la API estable.
  • ctx.timestamp es milisegundos epoch en todas partes. Baileys, la Cloud API y MockProvider informaban segundos epoch. Quita cualquier * 1000 (new Date(ctx.timestamp) ya es correcto). Lo mismo vale para los timestamps de InboundMessage, InboundInteraction y DeliveryStatus en hooks propios. Un provider de terceros que aún emite segundos sigue funcionando: el core convierte un valor menor que 1e12 para ctx.timestamp y el log de mensajes, pero actualízalo para emitir ms (assertInboundContract lo detecta).
  • Las pulsaciones de botones en Telegram llevan la hora de recepción, no la fecha del mensaje que tiene el teclado.
  • Se quitaron métodos que no hacían nada: CloudAPIProvider#typing() y #deleteMessage(), TelegramProvider#markRead() y DiscordProvider#markRead(). Usa ctx.typing()/ctx.delete()/ctx.markRead() (que ahí no hacen nada) o provider.typing?.(…).
  • El texto largo se divide en varios mensajes en vez de rechazar con PayloadTooLargeError. new Bot({ splitLongText: false }) recupera el comportamiento anterior. Los hooks before('send') siguen corriendo una vez por mensaje (sobre el texto completo, antes de dividirlo); los after('send'), una vez por parte.
  • Demasiadas opciones rechazan en Discord (más de 25 botones u opciones de menú, más de 10 respuestas de encuesta), Messenger (más de 3 botones, más de 10 filas de lista) e Instagram (más de 13 quick replies) con UnsupportedContentError; antes descartaban las opciones sobrantes en silencio.
  • Discord difiere las interacciones sin responder tras 2 s. autoAcknowledgeMs: false recupera el comportamiento anterior. Un ctx.acknowledge('texto') tardío sale entonces como followup; sin appId ni id de aplicación recibido de Discord, rechaza.
  • MockProvider informa capabilities.typing y readReceipts como false salvo que exista un método typing()/markRead() (antes tampoco tenían efecto), y emite timestamps en ms epoch.
  • RateLimitedError.retryAfterMs tiene un tope de 24 h.
  • Las encuestas de Telegram se envían no anónimas para que sus votos lleguen al bot como eventos 'poll'. new TelegramProvider({ anonymousPolls: true }) mantiene las encuestas anónimas.
  • Las ediciones de Discord se entregan como mensajes con el ref.id original; un handler que no deba verlas comprueba isEditedMessage(ctx) o usa editedMessages: 'ignore'.
  • Helpers renombrados (los nombres antiguos son alias deprecados hasta v3): pollTimeout de Telegram (segundos) → pollTimeoutMs; toProviderError → toTelegramError, toTikTokError; toEmailProviderError → toEmailError; el toProviderError de @botmesh/meta → toGraphError de @botmesh/graph-core. Discord, Messenger e Instagram quitaron su toProviderError de las alphas antes de su primera publicación en npm: usa toDiscordError, toMessengerError y toInstagramError.

Siguientes pasos