BuilderBot Framework

website·Indexed 30 days ago

https://builderbot.vercel.app

A framework for creating chatbots focused on low-frequency communication channels such as WhatsApp and Telegram. It features a modular system with support for multiple providers including Meta, Twilio, and Baileys, as well as database integrations like Supabase and Postgres. The framework includes plugins for OpenAI, LangChain, and Shopify, and provides tools for managing conversation flows, context, and deployment to platforms like Railway.

Tokens
26.8K
Snippets
134
Records
155
Agent score
72%

What's inside BuilderBot

  1. Overview of Baileys Provider

    Baileys is a free WhatsApp provider that operates via WhatsApp Web using WebSockets. It does not require Selenium or Chromium, making it more RAM-efficient. Note: Buttons and lists are not fully compatible and may not function as intended on all devices.
  2. Overview of the BuilderBot Framework

    BuilderBot is a framework designed for creating chatbots for low-frequency communication channels such as WhatsApp and Telegram. Its architecture is built around three key components to ensure developer experience and logic reuse: Flows for conversation context, Providers for channel connectivity, and Databases for state persistence.
  3. Overview of BuilderBot Providers

    Providers act as the communication bridge between the bot logic and the messaging platform (WhatsApp, Telegram, or custom providers). BuilderBot allows you to switch providers by implementing the corresponding connector without affecting the core bot logic. Each provider is initialized using the createProvider function, which accepts the provider class and an optional configuration object for access keys and settings.
  4. Overview of BuilderBot Database Adapters

    BuilderBot uses database adapters to maintain a record of events that occur between different conversations. While often used for chat history, the database stores all bot events, not just messages. Database providers can be easily exchanged by swapping the implemented class and updating the corresponding configuration keys.
  5. Deployment requirements for BuilderBot

    BuilderBot requires a persistent execution environment to function correctly. Because the application code must be kept alive continuously, the use of serverless services is not recommended.
  6. Manage message templates with Twilio Content Template Builder

    The Content Template Builder is a Twilio tool with a graphical interface used to create, approve, and manage content-rich message templates. This allows both developers and non-technical users to submit templates for approval via the Twilio Console without writing code, which is a requirement for using advanced features like buttons and lists in WhatsApp integrations.
  7. Configure Bot Providers and Databases

    BuilderBot uses a provider-based architecture to allow switching between communication channels (e.g., Meta, Twilio, Baileys for WhatsApp, or Telegram) and database adapters (e.g., Mongo, MySQL, Postgres, or MemoryDB) without rewriting the core workflow logic. The createBot function initializes the bot with these configurations.
    import { createBot, MemoryDB, createProvider, createFlow } from '@builderbot/bot'
    import { BaileysProvider } from '@builderbot/provider-baileys'
    
    const main = async () => {
        await createBot({
            database: new MemoryDB(),
            provider: createProvider(BaileysProvider),
            flow: createFlow([flowDemo])
        })
    }
    
    main()
  8. Use the context object to access user interaction data

    The context (ctx) object is a core component of the framework that provides real-time information about the interaction between a user and the bot. It is passed as the first argument to callback functions when using methods like addAnswer and addAction.

    Standard properties available across all providers include:

    • from: The unique identifier of the sender.
    • body: The actual message or input text sent by the user.
    • name: The name of the sender.

    Note: Additional properties may be available depending on the specific provider being used.

    import { addKeyword } from '@builderbot/bot';
    
    const flowA = addKeyword('register')
      .addAnswer('What is your name?', { capture: true }, async (ctx, { state }) => {
        // Accessing standard context properties
        const responseName = ctx.body
        const nameFrom = ctx.name
        const numberFrom = ctx.from
    
        console.log(`Other properties:`, ctx)
      })
    
    export default flowA
  9. Implement scheduled reminders using node-cron in BuilderBot

    To send individual reminders to users at specific intervals (e.g., every 12 hours), use the node-cron library. This allows you to fetch a list of users from an API or database and send them messages via the bot's provider. To avoid being flagged for robotic behavior or banned, it is recommended to use a varied list of messages and implement a delay between messages using utils.delay().
    pnpm i node-cron
    pnpm i @types/node-cron -D
  10. Implement a debounced message queue for fast user entries

    To prevent a chatbot from responding to every single short message sent by an impatient user, you can implement a message queue with a debounce timer. This system accumulates all messages sent by a specific user within a defined time window (e.g., 3000ms) and processes them as a single combined conversation string once the timer expires.

    Key features of this implementation include:

    • User-specific Queues: Separate conversation handling for each user based on their identifier (from).
    • Configurable Gap: The time window for accumulating messages is adjustable via QueueConfig.
    • Callback Processing: Once the gap expires, the accumulated text is passed to a callback function for bot processing.
    /**
     * @file multiUserMessageQueue.ts
     * @description An improved functional implementation of a multi-user message queueing system with debounce functionality,
     * ensuring separate conversation handling for each user.
     */
    
    import { BotContext } from "@builderbot/bot/dist/types";
    
    interface Message {
        text: string;
        timestamp: number;
    }
    
    interface QueueConfig {
        gapMilliseconds: number;
    }
    
    interface UserQueue {
        messages: Message[];
        timer: NodeJS.Timeout | null;
        callback: ((body: string, from: string) => void) | null;
    }
    
    interface QueueState {
        queues: Map<string, UserQueue>;
    }
    
    function createInitialState(): QueueState {
        return {
            queues: new Map()
        };
    }
    
    function resetTimer(userQueue: UserQueue): UserQueue {
        if (userQueue.timer) {
            clearTimeout(userQueue.timer);
        }
        return { ...userQueue, timer: null };
    }
    
    function processQueue(messages: Message[]): string {
        const result = messages.map(message => message.text).join(" ");
        console.log('Accumulated messages:', result);
        return result;
    }
    
    function createMessageQueue(config: QueueConfig) {
        let state: QueueState = createInitialState();
    
        return function enqueueMessage(ctx: BotContext, callback: (body: string, from: string) => void): void {
            const from = ctx.from;
            const messageBody = ctx.body;
    
            if (!from || !messageBody) {
                console.error('Invalid message context:', ctx);
                return;
            }
    
            console.log('Enqueueing:', messageBody, 'from:', from);
    
            let userQueue = state.queues.get(from);
            if (!userQueue) {
                userQueue = { messages: [], timer: null, callback: null };
                state.queues.set(from, userQueue);
            }
    
            userQueue = resetTimer(userQueue);
            userQueue.messages.push({ text: messageBody, timestamp: Date.now() });
            userQueue.callback = callback;
    
            console.log('Messages for', from, ':', userQueue.messages);
    
            if (!userQueue.timer) {
                userQueue.timer = setTimeout(() => {
                    const currentQueue = state.queues.get(from);
                    if (currentQueue) {
                        const result = processQueue(currentQueue.messages);
                        if (currentQueue.callback) {
                            currentQueue.callback(result, from);
                        }
                        state.queues.set(from, { ...currentQueue, messages: [], timer: null });
                    }
                }, config.gapMilliseconds);
            }
    
            state.queues.set(from, userQueue);
        };
    }
    
    export { createMessageQueue, QueueConfig };
  11. Send dynamic messages with flowDynamic

    Use flowDynamic to send messages based on API calls, database results, or other dynamic processes. It can accept a simple string or an array of objects for advanced control over delays and media.
    import { addKeyword } from '@builderbot/bot';
    
    const flow = addKeyword('register')
      .addAction(async (_, { flowDynamic }) => {
        // Simple string message
        await flowDynamic(`Thanks for registering`);
    
        // Advanced options using an array of objects
        await flowDynamic([
          { 
            body: `Message with 2 seconds delay`,
            delay: 2000 
          },
          { 
            body: `Look at this image`,
            media: `https://i.imgur.com/0HpzsEm.png` 
          },
          { 
            body: `Look at this file`,
            media: join('assets', 'file.pdf') 
          }
        ]);
      });
  12. Disable the bot for a specific user

    You can turn off the bot for a specific user by storing a flag in the user's state and calling endFlow() when that flag is active.
    const flow = addKeyword<BaileysProvider>('magic keyword')
      .addAction(async (_, { state, endFlow }) => {
          const botOffForThisUser = state.get<boolean>('botOffForThisUser')
          await state.update({botOffForThisUser:!botOffForThisUser})
          if(botOffForThisUser) return endFlow()
      })
      .addAnswer('Hello!')