Loading...
Loading...
Expert assistant for BuilderBot (v1.4.0) — a TypeScript/JavaScript framework for building multi-platform chatbots (WhatsApp, Telegram, Instagram, Email, etc.). Use when creating or editing flows (addKeyword, addAnswer, addAction), wiring EVENTS, managing per-user state or globalState, configuring providers (Baileys, Meta, Telegram, Evolution, etc.) or databases (Mongo, Postgres, MySQL, JSON), implementing REST API endpoints (handleCtx, httpServer), debugging flow control (gotoFlow, endFlow, fallBack, idle, capture, flowDynamic), or handling blacklist logic. Architecture: Provider + Database + Flow.
npx skill4agent add leifermendez/skill-builderbot-code builderbot-code-skillctx: BotContext{ body, from, name, host, ...platform fields }methods: BotMethods{ flowDynamic, gotoFlow, endFlow, fallBack, state, globalState, blacklist, provider, database, extensions }returnreturn gotoFlow(flow)return endFlow('msg')return fallBack('msg')awaitawait flowDynamic(...)await state.update(...)await globalState.update(...)eslint . --no-ignore| Bug | Wrong | Right |
|---|---|---|
Missing | | |
Missing | | |
| | |
| | Split into two chained |
| Unnecessary dynamic import | | Top-level |
| Circular ESM import | Top-level | Dynamic |
| | |
Using | | |
| Using buttons | | Text-based numbered menu + |
Checking | | Never use |
addKeyword(keywords, options?) // ActionPropertiesKeyword: capture, idle, media, delay, regex, sensitive ← NEVER use `buttons`
.addAnswer(message, options?, cb?, childFlows?)
.addAction(cb)
// cb: async (ctx: BotContext, methods: BotMethods) => void// Per-user
await state.update({ key: value })
state.get('key') // dot notation supported: 'user.profile.name'
state.getMyState()
state.clear()
// Global (shared across all users)
await globalState.update({ key: value })
globalState.get('key')
globalState.getAllState()| # | Question | What to check / fix |
|---|---|---|
| 1 | Does every prompt tell the user exactly what to type? | Each |
| 2 | Are all invalid inputs handled? | Every captured step must have a |
| 3 | Is there an idle timeout on long captures? | Add |
| 4 | Can the user always exit? | Provide a cancel keyword (e.g. "cancel", "salir") or honour it inside captures and call |
| 5 | Are messages short, mobile-friendly, and max 3-4 bubbles? | No wall-of-text AND no bubble spam. Group lines with |
| 6 | Is the user's name used where natural? | Greetings and confirmations should reference |
| 7 | Are menus numbered text lists (never | Use |
| 8 | Does each multi-step flow confirm before committing? | Before irreversible actions (order, payment, delete) show a summary and ask "confirm? yes / no". |
| 9 | Does the flow end with a clear closing message? | The final step must tell the user what happened and what to do next (or say goodbye). |
| 10 | Are error messages actionable? | Never say just "error". Say what went wrong and how to fix it: |
| Style | Syntax | Example |
|---|---|---|
| Bold | | |
| Italic | | |
| Strikethrough | | |
| Monospace | | |
| Bullet list | | |
*bold*_italic_•-━━━━━━━---<b><br>**bold**## headingconst body = [
'*🍕 Tu pedido*',
'━━━━━━━━━━━━━━',
`• Pizza: *Pepperoni*`,
`• Tamaño: *Mediana (30 cm)*`,
`• Cantidad: *2*`,
'',
`💰 Total: *$26 USD*`,
'',
'_Responde *sí* para confirmar o *no* para cancelar._',
].join('\n')These rules apply to every flow. Violating them makes the bot feel like spam.
flowDynamicaddAnswer| Call | Array behavior |
|---|---|
| ⚠️ Each string = separate WhatsApp message |
| ✅ Joined into one message with line breaks |
Always usewithFlowDynamicMessage[]in.join('\n')when callingbody. Never pass raw string arrays.flowDynamic
| Rule | Wrong | Right |
|---|---|---|
| Max 3-4 bubbles per turn | | |
| Always use random delay | No delay between bubbles | |
| Never spread item lists | | |
const rnd = () => Math.floor(Math.random() * 800) + 500await flowDynamic([
{
body: [
'*Título*',
'',
items.map(i => `• ${i.name} — $${i.price}`).join('\n'),
].join('\n'),
delay: rnd(),
},
{
body: '*Sección 2*\nLínea A\nLínea B',
delay: rnd(),
},
])
// addAnswer o addAction siguiente = bubble 3Simulates "typing..." or "recording..." before sending a message. Makes the bot feel human. Only available withandBaileysProvider.SherpaProvider
type WAPresence = 'unavailable' | 'available' | 'composing' | 'recording' | 'paused'
// composing = typing bubble recording = audio bubbleconst waitT = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
.addAction(async (ctx, { provider, flowDynamic }) => {
await provider.vendor.sendPresenceUpdate('composing', ctx.key.remoteJid)
await waitT(1500)
await flowDynamic([{ body: 'Mensaje que parece escrito por humano', delay: rnd() }])
await provider.vendor.sendPresenceUpdate('paused', ctx.key.remoteJid)
})sendPresenceUpdate('paused', ...)rnd()flowDynamiccomposingrecordingreturn gotoFlow(...)return endFlow(...)return fallBack(...)await state.update(...)capture: trueidleidleFallBackEVENTS.*await import()require()importeslint . --no-ignorebuilderbot/func-prefix-endflow-flowdynamicflowDynamicendFlowaddAction.addAction(async (_, { flowDynamic }) => { await flowDynamic(lines) })
.addAction(async (_, { endFlow }) => { return endFlow('bye') })"type": "module""module": "ES2022"require()await import()✅ Static (no cycle): welcome → menu → order → payment
import { orderFlow } from './order.flow' ← top of file
✅ Dynamic (real cycle): welcome ↔ order
const { orderFlow } = await import('./order.flow') ← inside callbackimport().addAction(async (ctx, { gotoFlow }) => {
const { targetFlow } = await import('./target.flow')
return gotoFlow(targetFlow)
})src/flows/index.tssrc/
├── app.ts
├── flows/
│ ├── index.ts # createFlow([...all flows])
│ ├── welcome.flow.ts
│ └── order.flow.ts
└── services/// src/flows/index.ts
import { createFlow } from '@builderbot/bot'
import { welcomeFlow } from './welcome.flow'
import { orderFlow } from './order.flow'
export const flow = createFlow([welcomeFlow, orderFlow])// src/app.ts
import { createBot, createProvider } from '@builderbot/bot'
import { MemoryDB as Database } from '@builderbot/bot'
import { BaileysProvider as Provider } from '@builderbot/provider-baileys'
import { flow } from './flows'
const main = async () => {
const provider = createProvider(Provider)
const database = new Database()
await createBot({ flow, provider, database })
provider.initHttpServer(+(process.env.PORT ?? 3008))
}
main()