botmesh viene con @botmesh/core (con pnpm, ejecútalo como
pnpm exec botmesh dev src/bot.js). Los proyectos generados con
create-botmesh ya lo tienen como npm run dev.
1. El patrón factory (defineBot)
Tu módulo exporta por defecto una factory que recibe el provider y
devuelve el Bot sin arrancarlo. Quien ejecuta el archivo decide qué
provider inyectar:
- La factory puede ser async (
async (provider) => { ... }). isMainModule(import.meta)usaimport.meta.main(Node ≥ 22.18 / 24.2) o comparaprocess.argv[1](sin distinguir mayúsculas en Windows). Dafalsesi un gestor de procesos importa tu archivo desde su propio wrapper (pm2 en modo ESM); en ese caso usa un archivo de arranque aparte que importe la factory.botmesh devdefine la variable de entornoBOTMESH_DEV=1por si tu código necesita detectar el simulador.
2. Uso
Códigos de salida:
0 ok, 1 fallo en ejecución, 2 error de uso.
3. Comandos dentro del simulador
- Cualquier línea es un mensaje para el bot. Los comandos con barra
(
/reset) se envían como mensajes. ::textoenvía un texto que empieza por:. Un emoticono como:)es un mensaje normal.:as <user> [nombre]cambia de remitente (se queda en el mismo chat).:group <id>entra a un chat de grupo; menciona al bot como@bot.:dmvuelve al chat privado.:media <image|video|audio|voice|document|sticker> <ruta> [caption]envía un archivo del disco (alias:photo,doc,file,ptt; rutas con espacios entre comillas). Los bytes llegan víaprovider.downloadMedia(ref).:click <n|id|label>pulsa un botón, una fila de lista o un botón de card, o vota en el último poll (llega abot.on('poll')).:statemuestra quién eres, el estado del bot y la conversación activa (si espera respuesta y suc.state).:resetreinicia el bot desde cero (borra conversaciones y estado en memoria).:reloadvuelve a importar el código.:help,:quit(también Ctrl+C o Ctrl+D).
[1] Info); la media
sale como [image: url] con el caption en la línea siguiente. Los mensajes
del bot a otros chats salen como bot → <chatId> › ....
Con la entrada por pipe (printf 'hola\n' | npx botmesh dev src/bot.js --no-watch),
cada línea espera a que el bot termine de reaccionar (hasta 30 s, para
cubrir llamadas a IA), así que el transcript sale en orden.
4. Recarga en caliente
- Al guardar un
.js/.mjs/.cjs/.ts/.mts/.cts/.jsx/.tsxen la carpeta vigilada se para el bot actual (bot.stop()), se reimporta la entry y se crea un bot nuevo. Los imports relativos (./flows/menu.js) también se recargan; los paquetes (@botmesh/core,node_modules) no. - Se ignoran
node_modules, las carpetas que empiezan por punto,sessions/,*_sessions,.json,.envy los logs, para que escribir un store o credenciales nunca provoque un bucle. - Si la recarga falla (p. ej. un error de sintaxis), sigue corriendo la versión anterior. Al recargar se pierden las conversaciones en memoria.
- Node no puede descargar módulos viejos, así que en sesiones muy largas la
memoria crece: reinicia de vez en cuando. Cierra los recursos que crees
fuera del bot (servidores, intervalos) en
bot.on('stopped', ...).
5. TypeScript
- Necesita el type stripping nativo de Node: viene activado en Node ≥ 22.18
/ 23.6. En Node 22.6–22.17 usa
NODE_OPTIONS=--experimental-strip-types npx botmesh dev src/bot.ts. - Los imports relativos deben llevar la extensión
.tsreal y los imports de solo tipos deben usarimport type. Sin TSX, enums ni namespaces (salvo con transform-types). - Si falta soporte, el CLI muestra estas opciones en lugar de un error críptico.
6. Seguridad
- Si la factory construye el
Botcon otro provider en vez del que recibe, el simulador se niega a arrancarlo (podría conectar de verdad), para ese bot y se detiene: nunca vuelve a llamar a la factory y, en modo watch, el CLI sale con código 1. - Un export por defecto que no se creó con
defineBot()recibe un aviso (una sola vez). - Si la entry se ejecuta pero no exporta una factory (un
bot.jsantiguo que arranca solo al importarse),botmesh devtermina el proceso, aunque esté en modo watch, para cerrar cualquier conexión real que haya abierto.
7. API programática (@botmesh/core/dev)
DevSessionno hace I/O por sí misma; inyectaloadywrite. Entre sus opciones estánuser,name,group,echo,settleMs/maxSettleMsyonHalt.DevProviderextiendeMockProvidercononOutbound,injectMediaeinjectPollVote.- También se exportan funciones puras:
renderOutbound,parseDevInput,watchForChangesyrunCli(argv, io)con streams inyectables.
bot.inspectConversation({ from, sender, isGroup })
devuelve { key, state, waiting, queued, accepts? } o undefined (state es una
copia superficial; accepts dice qué acepta la espera pendiente: 'text', la lista
de tipos de waitMedia o 'any' en choose/waitAny), y bot.isBusy() dice si el bot sigue atendiendo un
evento o ejecutando un paso de conversación.