Flujo de compra
Un flujo con un menú numerado y branching usando .choose().
El flujo
import { Bot, flow } from '@botmesh/core'
import { BaileysProvider } from '@botmesh/baileys'
const bot = new Bot({
provider: new BaileysProvider({ session: 'demo-bot', qr: { terminal: true, small: true } }),
})
const catalog: Record<string, string> = {
electronics: '📱 *Electronics* — iPhone 15 ($999), Samsung S24 ($899), Xiaomi 14 ($599)',
clothing: '👕 *Clothing* — Shirt ($25), Jeans ($45), Jacket ($80)',
home: '🏠 *Home* — Lamp ($35), Rug ($60), Glass ($12)',
}
const buy = flow('buy')
.say('🛒 *What do you want to buy?*')
.choose('Write the option number', {
electronics: 'Electronics 📱',
clothing: 'Clothing 👕',
home: 'Home 🏠',
}, 'category')
.say((_ctx, c) => catalog[String(c.state.category)])
.say('✅ Thanks for your interest! Anything else?')
bot.use(buy)
await bot.start()Cómo funciona
- El usuario envía "buy"
.say(...)envía la introducción, y luego.choose()construye un menú numerado a partir de las claves deoptions(1) Electronics 📱,2) Clothing 👕,3) Home 🏠) y espera un número válido- La CLAVE elegida (
electronics,clothingohome— no el número que escribió el usuario) se guarda enc.state.category, ya que'category'es el tercer argumento de.choose() - El siguiente
.say()leec.state.categorypara mostrar el texto del catálogo correspondiente - El flujo cierra con un mensaje de agradecimiento
Ramificar con sub-pasos then
En lugar de leer c.state después, cada opción puede ejecutar su propio paso then justo en el momento en que se elige. El control vuelve al flujo padre en cuanto termina el sub-paso — no hay que construir un flujo anidado aparte:
const buy = flow('buy')
.say('🛒 *What do you want to buy?*')
.choose('Write the option number', {
electronics: {
label: 'Electronics 📱',
then: (_ctx, c) => { c.state.summary = '📱 iPhone 15 — $999' },
},
clothing: {
label: 'Clothing 👕',
then: (_ctx, c) => { c.state.summary = '👕 Shirt — $25' },
},
home: {
label: 'Home 🏠',
then: (_ctx, c) => { c.state.summary = '🏠 Lamp — $35' },
},
})
.say((_ctx, c) => `${c.state.summary}\n\n✅ Thanks for your interest!`)
bot.use(buy)La clave elegida sigue estando disponible después como c.state.choice (el stateKey por defecto cuando no pasas uno) si la necesitas más adelante en la cadena.
Ponerle un timeout a todo el flujo
.timeout() define cuánto tiempo espera cada wait del flujo (incluido el menú de .choose()) por una respuesta. El mensaje que se envía al expirar NO es un argumento de .timeout() — se configura una sola vez en el bot:
const bot = new Bot({
provider: new BaileysProvider({ session: 'demo-bot' }),
conversation: { timeoutMessage: '⏰ Session expired due to inactivity.' },
})
const buy = flow('buy')
.timeout('2m')
.say('🛒 *What do you want to buy?*')
.choose('Write the option number', {
electronics: 'Electronics 📱',
clothing: 'Clothing 👕',
home: 'Home 🏠',
}, 'category')
.say((_ctx, c) => catalog[String(c.state.category)])
bot.use(buy)Volver al menú principal
Una opción con goto sale del flujo actual hacia otro; una opción cuyo then es un flujo completo lo ejecuta en línea y vuelve (como .use()):
const menu = flow('menu').choose('Menú principal', { comprar: 'Comprar 🛒', ayuda: 'Ayuda' }, 'seccion')
const direccion = flow('direccion').ask('¿Calle?', 'calle').ask('¿Ciudad?', 'ciudad')
const checkout = flow('checkout')
.choose('¿Confirmas el pedido?', {
si: { label: 'Sí ✅', then: direccion }, // ejecuta el sub-flujo y sigue abajo
volver: { label: '⬅️ Menú principal', goto: menu }, // sale de este flujo
})
.say((_ctx, c) => `📦 Envío a ${c.state.calle}, ${c.state.ciudad}`)
bot.use([menu, checkout]) // `direccion` corre en línea, no hace falta registrarlobot.lint() avisa de un goto a un flujo no registrado (ver Inspeccionar y validar flujos).
Siguientes pasos
- Soporte con timeout — timeout y fallback
- Flujos — guía completa del DSL