Multiple messages

Issue

Sometimes, you need the bot to generate responses based on an event such as receiving images from users, but you don't want the bot to throw multiple and redundant responses if the user quickly sends several images.


Possible Solution

To ensure that the bot responds only once despite receiving too many calls in a short period of time, a debounce decorator could be implemented around the gotoFlow and endFlow methods.

In the example below I will show you how I implemented this solution in my bot to analyze several images and depending on whether they are images of furniture or not, terminate the flow with an error message or send it to another registration flow

import path from "node:path";
import fs from "node:fs/promises";
import process from "node:process";
import { addKeyword, EVENTS } from "@builderbot/bot";
import { BaileysProvider } from "@builderbot/provider-baileys";
import { UrlToBase64 } from "@builderbot-plugins/url-to-base64";

import { PROMPT_IMAGE } from "./prompt";
import { debounce } from "~/utils/debounce";
import AIClass from "~/services/OpenAIService";
import { registerFlow } from "../order/register.flow";


const localPaths = [];
let debouncedEndFlow:(...args: any[]) => void;
let debouncedGoToFlow:(...args: any[]) => void;
const filePath = path.join(process.cwd(), 'src', 'database', 'images');

export const mediaFlow = addKeyword<BaileysProvider>(EVENTS.MEDIA)
.addAction(async(ctx, { provider, queue }) => { 
  await queue.enqueue('processImage', async () => {
    const localPath = await provider.saveFile(ctx, { path: filePath });
    localPaths.push(localPath);
  }, 'imageProcessingTask');

  await queue.processQueue('processImage');
  await queue.clearQueue('processImage');
  queue.clearAndDone('processImage', {fingerIdRef: 'imageProcessingTask'});
})
.addAction(async (_, { extensions, gotoFlow, endFlow }) => {

  const ai = extensions.ai as AIClass;  
  if(!debouncedEndFlow && !debouncedGoToFlow){
    debouncedEndFlow = debounce(endFlow, 1500);
    debouncedGoToFlow = debounce(gotoFlow, 1500);
  }
    
  for(const path of localPaths) {
    const { data, mimetype } = UrlToBase64.fromFilePath(path);    
    const aiResponse = await ai.readImage(data, PROMPT_IMAGE, mimetype);

    if(aiResponse.includes('NOT_FURNITURE')) {
      for(const filePath of localPaths) {
        await fs.unlink(filePath);
      }
      localPaths.length = 0;
      return debouncedEndFlow('Ups! Asegurate de enviar una foto correcta de un mueble');
    }   
  }

  localPaths.length = 0;
  return debouncedGoToFlow(registerFlow)
})

Remember that this is an alternative solution, and it is possible that its implementation could be improved.


Guides

My first chatbot

Learn how build your first chatbot in few minutes

Read more

Concepts

Understand the essential concepts for building bots

Read more

Add Functions

The key to learning how to write flows is add-functions.

Read more

Plugins

Unlimitate and start implementing the community plugins.

Read more

Resources

Modularize

Learn how to modularise flows so that you can have a more maintainable bot.

Send Message

How to send a message via HTTP to start conversations, you can send multimedia as well.

Dockerizer

A good practice is to dockerise your bots to make them more maintainable and effective.

Events

Learning about events will make us more fluent when creating chatbots.