🧱 Módulo 3: Arquitectura del bot y flujos con addKeyword + addAnswer
En este módulo profundizaremos en la arquitectura de bot-whatsapp y aprenderás a construir flujos conversacionales básicos y sólidos con addKeyword y addAnswer, incluyendo captura de datos del usuario.
3.1 Componentes clave de la arquitectura
La arquitectura de bot-whatsapp se basa en 3 componentes principales:
1) Provider (Proveedor de mensajería)
Conecta el bot con WhatsApp. Ejemplos: BaileysProvider, MetaProvider, VenomProvider.
2) Database Adapter (Adaptador de almacenamiento)
Gestiona sesiones/estados. Ejemplos: JsonFileAdapter, MongoAdapter, MySQLAdapter.
3) Flow (Flujo conversacional)
Define respuestas y lógica del bot. Se crea con addKeyword(...) y se extiende con addAnswer, addAction, flowDynamic, gotoFlow, etc.
3.2 Arquitectura mínima lista para correr
const {
createBot,
createProvider,
createFlow,
addKeyword,
} = require("@bot-whatsapp/bot");
const { BaileysProvider } = require("@bot-whatsapp/provider-baileys");
const { JsonFileAdapter } = require("@bot-whatsapp/database-json");
// Flujo mínimo de bienvenida
const flowBienvenida = addKeyword(["hola", "inicio"])
.addAnswer("Hola 👋, soy tu asistente virtual.")
.addAnswer("Escribe *menu* para ver opciones.");
const main = async () => {
const adapterDB = new JsonFileAdapter();
const adapterFlow = createFlow([flowBienvenida]);
const adapterProvider = createProvider(BaileysProvider);
await createBot({
flow: adapterFlow,
provider: adapterProvider,
database: adapterDB,
});
};
main();
Explicación rápida:
createFlow([...])registra los flujos.createProvider(BaileysProvider)define el conector WhatsApp.JsonFileAdapter()guarda el estado en archivos (ideal para desarrollo).createBot({...})levanta el bot con todo lo necesario.
3.3 addKeyword: activar un flujo por palabra clave
Qué hace: dispara el flujo cuando el usuario escribe la palabra/frase clave.
API básica: addKeyword('texto' | ['t1','t2',...])
const flowSaludo = addKeyword(["hola", "alo"])
.addAnswer("👋 ¡Hola! Bienvenido/a.")
.addAnswer("¿En qué puedo ayudarte hoy?");
Explicación:
- Si el usuario escribe hola o alo, el flujo se activa.
- Cada
.addAnswer(...)envía un mensaje separado en orden.
3.4 addAnswer: responde con texto, medios y más
Qué hace: envía mensajes (texto, imágenes, PDF, botones, etc.) y permite opciones como delay o capture.
Ejemplo: envío de imagen desde URL pública
const flowImagen = addKeyword("imagen")
.addAnswer("📷 Te envío una imagen de ejemplo…", {
media: "https://i.imgur.com/0HpzsEm.png",
})
.addAnswer("¿Necesitas otro recurso (PDF/Video)?");
Explicación:
media: 'URL'adjunta un archivo remoto (imagen, PDF, audio, etc.).- Asegúrate de que la URL sea pública y accesible.
3.5 Ejemplo con retardo y botones (menú rápido)
🔔 Nota: Los botones funcionan mejor en proveedores oficiales (Meta/Twilio). En proveedores web gratuitos pueden variar.
const flowMenu = addKeyword(["menu", "opciones"])
.addAnswer("Un momento, cargando menú…", { delay: 1000 }) // 1s de espera
.addAnswer("Selecciona una opción:", {
buttons: [
{ body: "🛍️ Ver productos" },
{ body: "📦 Mis pedidos" },
{ body: "👤 Soporte" },
],
});
Explicación:
{ delay: 1000 }envía el mensaje con 1 segundo de espera (mejor UX).buttons: [...]muestra 3 opciones táctiles al usuario.
3.6 Captura de datos del usuario con capture: true
Qué hace: espera la siguiente respuesta del usuario y la entrega al handler para procesarla (validar, guardar en DB, etc.).
Ejemplo A — Capturar nombre del usuario
const flowNombre = addKeyword(["mi nombre", "nombre"]).addAnswer(
"¿Cómo te llamas?",
{ capture: true },
async (ctx, { flowDynamic }) => {
const nombre = (ctx.body || "").trim();
if (!nombre || nombre.length < 2) {
return flowDynamic(
"Nombre no válido. Intenta de nuevo con al menos 2 caracteres."
);
}
await flowDynamic(`¡Mucho gusto, *${nombre}*! 🙂`);
}
);
Explicación clave:
{ capture: true }hace que este.addAnswer(...)espere la próxima entrada del usuario.ctx.bodyes el texto que el usuario escribió.- Validamos longitud básica y respondemos dinámicamente.
Ejemplo B — Capturar y validar correo electrónico
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const flowCorreo = addKeyword(["correo", "email"]).addAnswer(
"📧 Escribe tu correo electrónico:",
{ capture: true },
async (ctx, { flowDynamic }) => {
const email = (ctx.body || "").trim();
if (!EMAIL_REGEX.test(email)) {
return flowDynamic(
"Formato inválido. Ejemplo válido: usuario@dominio.com"
);
}
await flowDynamic(`Gracias. Guardaremos: *${email}*`);
}
);
Explicación clave:
- Se usa una expresión regular simple para validar el correo.
- Si es inválido, se corta con un mensaje de ayuda.
- Si es válido, confirmamos y podrías guardar en DB aquí.
Ejemplo C — Capturar opción de un menú simple
const flowSeleccion = addKeyword(["selección", "elige"]).addAnswer(
"Elige una opción (1-3):\n1) Productos\n2) Pedidos\n3) Soporte",
{ capture: true },
async (ctx, { flowDynamic }) => {
const opcion = (ctx.body || "").trim();
switch (opcion) {
case "1":
return flowDynamic("Has elegido *Productos* 🛍️");
case "2":
return flowDynamic("Has elegido *Pedidos* 📦");
case "3":
return flowDynamic("Has elegido *Soporte* 👤");
default:
return flowDynamic("Opción inválida. Responde con 1, 2 o 3.");
}
}
);
Explicación clave:
- Se guía al usuario con una instrucción clara y se evalúa su respuesta.
- Buena base para integrar
gotoFlowa subflujos específicos.
3.7 Glue Code: integrar todos los flujos y correr
const {
createBot,
createProvider,
createFlow,
addKeyword,
} = require("@bot-whatsapp/bot");
const { BaileysProvider } = require("@bot-whatsapp/provider-baileys");
const { JsonFileAdapter } = require("@bot-whatsapp/database-json");
// --- Flujos básicos ---
const flowSaludo = addKeyword(["hola", "alo"])
.addAnswer("👋 ¡Hola! Bienvenido/a.")
.addAnswer("Escribe *menu* para opciones o *imagen* para un ejemplo.");
const flowImagen = addKeyword("imagen").addAnswer(
"📷 Te envío una imagen de ejemplo…",
{
media: "https://i.imgur.com/0HpzsEm.png",
}
);
const flowMenu = addKeyword(["menu", "opciones"])
.addAnswer("Un momento, cargando menú…", { delay: 1000 })
.addAnswer("Selecciona una opción:", {
buttons: [
{ body: "🛍️ Ver productos" },
{ body: "📦 Mis pedidos" },
{ body: "👤 Soporte" },
],
});
// --- Flujos con captura ---
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const flowNombre = addKeyword(["mi nombre", "nombre"]).addAnswer(
"¿Cómo te llamas?",
{ capture: true },
async (ctx, { flowDynamic }) => {
const nombre = (ctx.body || "").trim();
if (!nombre || nombre.length < 2) {
return flowDynamic(
"Nombre no válido. Intenta de nuevo con al menos 2 caracteres."
);
}
await flowDynamic(`¡Mucho gusto, *${nombre}*! 🙂`);
}
);
const flowCorreo = addKeyword(["correo", "email"]).addAnswer(
"📧 Escribe tu correo electrónico:",
{ capture: true },
async (ctx, { flowDynamic }) => {
const email = (ctx.body || "").trim();
if (!EMAIL_REGEX.test(email)) {
return flowDynamic(
"Formato inválido. Ejemplo válido: usuario@dominio.com"
);
}
await flowDynamic(`Gracias. Guardaremos: *${email}*`);
}
);
const flowSeleccion = addKeyword(["selección", "elige"]).addAnswer(
"Elige una opción (1-3):\n1) Productos\n2) Pedidos\n3) Soporte",
{ capture: true },
async (ctx, { flowDynamic }) => {
const opcion = (ctx.body || "").trim();
switch (opcion) {
case "1":
return flowDynamic("Has elegido *Productos* 🛍️");
case "2":
return flowDynamic("Has elegido *Pedidos* 📦");
case "3":
return flowDynamic("Has elegido *Soporte* 👤");
default:
return flowDynamic("Opción inválida. Responde con 1, 2 o 3.");
}
}
);
// --- Bootstrap del bot ---
const main = async () => {
const adapterDB = new JsonFileAdapter();
const adapterFlow = createFlow([
flowSaludo,
flowImagen,
flowMenu,
flowNombre,
flowCorreo,
flowSeleccion,
]);
const adapterProvider = createProvider(BaileysProvider);
await createBot({
flow: adapterFlow,
provider: adapterProvider,
database: adapterDB,
});
};
main();
Explicación:
- Registramos todos los flujos en
createFlow([...]). - Puedes separar cada flujo en su propio archivo si el proyecto crece.
- A partir de aquí, es sencillo añadir
gotoFlow,state,flowDynamiccon API/DB, etc.
3.8 Checklist para evitar errores comunes
- ✅ Importa desde paquetes correctos (
@bot-whatsapp/bot,@bot-whatsapp/provider-*,@bot-whatsapp/database-*). - ✅ Declara cada flujo antes de pasarlo a
createFlow([...]). - ✅ Usa arrays si necesitas varias palabras clave
['hola', 'alo']. - ✅ En
media, usa URLs públicas o rutas absolutas válidas. - ✅ Si usas botones, prueba en un proveedor compatible (Meta/Twilio).
- ✅ En capturas, valida
ctx.bodyy guía al usuario ante formatos inválidos.
✅ Resumen rápido
| Concepto | Para qué sirve |
|---|---|
addKeyword | Dispara el flujo con palabras/expresiones clave. |
addAnswer | Envía mensajes (texto/medios), soporta delay/capture. |
capture: true | Espera la siguiente respuesta del usuario. |
flowDynamic | Respuestas dinámicas (basadas en lo que envía el user). |
createFlow | Registra y une todos los flujos en el bot. |
¿Listo para el siguiente paso? En el Módulo 4 añadiremos addAction, ctx, state y gotoFlow para construir menús encadenados y flujos con validaciones.