Methods

Inside each addAction or addAnswer we can execute logic and we can make use of some methods that will facilitate the development.


State

In many occasions you will need to share data between flows and no matter if you have modularized your project in different files you can use state in the callback function to be able to access the individual state of each user.

Let's imagine the case where you have two flows. Flow A: In charge of collecting user data. Flow B: Responsible for generating a record in the database. but both flows are independent files

Remember that the state is independent per conversation between user and bot.

  import { createFlow, MemoryDB, createProvider } from '@builderbot/bot';
  // ...
  import flowA from './flows/flow-a'
  import flowB from './flows/flow-b'

  const main = async () => {

      const adapterDB = new MemoryDB()
      const adapterFlow = createFlow([flowA, flowB])
      const adapterProvider = createProvider(BaileysProvider)

      adapterProvider.initHttpServer(3000)

      await createBot({
          flow: adapterFlow,
          provider: adapterProvider,
          database: adapterDB,
      })
  }

Each conversation history with the user is totally independent, in this way we avoid mixing conversations.


GlobalState

Very similar to state there is another method called GlobalState to share a global state of the bot between different flows. The main use for this method is to share data that can change and that every conversation between bot and user can access.

Below you can see a practical example where we use the globalState to use it as a switch to allow or disallow the bot to respond no matter who writes to it.

  import { createFlow, MemoryDB, createProvider } from '@builderbot/bot';
  // ...
  import flowWelcome from './flows/flow-welcome'
  import flowOnOff from './flows/flow-on-off'

  const main = async () => {

      const adapterDB = new MemoryDB()
      const adapterFlow = createFlow([flowWelcome, flowOnOff])
      const adapterProvider = createProvider(BaileysProvider)

      adapterProvider.initHttpServer(3000)

      await createBot({
          flow: adapterFlow,
          provider: adapterProvider,
          database: adapterDB,
      })
  }

State/GlobalState Options

Both methods such as state and global state contain similar options and funcinalides, which depending on the use case can be very useful.

Clear

This method allows the state to be cleaned either globally or independently. It technically clears the Map.

Available in: state, globalState

.addAnswer('..', null, async (_, { state }) => {
  state.clear()
})
.addAction(async (_, { globalState }) => {
  globalState.clear()
})

Update

To add or update a value in the state we have available the update method. This method receives an object which if the value does not exist creates it and returns a new object with all the values. It is important to understand that it is a promise to avoid rare behavior by adding the await

Available in: state, globalState

.addAnswer('..', null, async (_, { state }) => {
  await state.update({name:'Joe', age:'33'})
  await state.update({email:'test@test.com'})
})
.addAction(async (_, { globalState }) => {
  await globalState.update({name:'Joe', age:'33'})
  await globalState.update({email:'test@test.com'})
})

Get

When we need to retrieve the state values we can do it individually by calling by the property name as follows.

Available in: state, globalState

.addAnswer('..', null, async (_, { state }) => {
  state.get('propertyName')
})
.addAction(async (_, { globalState }) => {
  globalState.get('propertyName')
})

GetMyState

Another way to retrieve the entire state object belonging to a user-independent conversation is by using getMyState

Available in: state

.addAnswer('..', null, async (_, { state }) => {
  state.getMyState()
})

GetAllState

When we are working with the globalState and we want to retrieve all the properties object with their respective values you can use getAllState

Available in: globalState

.addAnswer('..', null, async (_, { globalState }) => {
  globalState.getAllState()
})

FlowDynamic

Many times you will need to send messages coming from an API call or dynamic data from data base or from processes. In this case you should use flowDynamic.

  import { addKeyword } from '@builderbot/bot';

  const flowStandAlone = addKeyword('register')
    .addAnswer('What is your name?', { capture: true }, async (ctx, { flowDynamic }) => {
      const responseName = ctx.body
      //.... db.insert({name:responseName})
      await flowDynamic(`Thanks for register ${responseName}`)
    })

  export default flowStandAlone

If you want to send a list of products it is recommended to send a few products between 4 to 8 and you can ask the user what category of products and other details to filter and be able to respond with the ideal product list.


FlowDynamic Options

  • Name
    body
    Type
    string
    Description

    You can send a message inside an object using the body property. It is ideal when you need to send a message apart from the message to send a media or to place a delay.

  • Name
    delay
    Type
    number
    Description

    This is the number of milliseconds that will elapse before the message is sent.

  • Name
    media
    Type
    string
    Description

    The url or local path of the file to send, must be a text string and if it is a URL it must be public.

const flow = addKeyword('register')
.addAction(async (_, { flowDynamic }) => {

  await flowDynamic([{ body: `Thanks  ${responseName}` }])

  await flowDynamic([{ 
    body: `message with 2 seconds delay`,
    delay: 2000 
   }])

  await flowDynamic([{ 
    body: `Look at this`,
    media: `https://i.imgur.com/0HpzsEm.png` 
  }])

  await flowDynamic([{ 
    body: `Look at this`,
    media: join('assets','file.pdf') 
  }])

})

FallBack

The fallBack() function is a fundamental resource within a bot's interaction flow, used to handle invalid or unexpected responses from the user. When a user provides a message that does not match any keyword or expected response, the bot can invoke the fallBack() function to repeat the last message and wait for a valid response.

To integrate the fallBack() function into the bot interaction flow, it is used within the addAnswer() or addAction() method. Within this method, a condition is set that verifies whether the user's response is valid or not. In case the response does not meet the expected criteria, fallBack() is called to repeat the last message and request a valid response. For example:

import { addKeyword } from '@builderbot/bot';

const flowEmailRegister = addKeyword('hello')
  .addAnswer('What is your email?', {capture:true}, (ctx, { fallBack }) => {
    if (!ctx.body.includes('@')) {
      return fallBack(`Ups! is not a valid email`);
    } else {
      // db.insert({email:ctx.body})
    }
  });

EndFlow

The endFlow function is used in chat applications or conversational user interfaces to end a flow of interaction with the user. Imagine a scenario where you are collecting information from a user in several steps, such as their name, email address and phone number, and at each step the user has the option to cancel the current operation.

By using endFlow, you can provide the user with an easy way to cancel the transaction at any time. For example, you could present a button or command that the user can activate to indicate that they wish to stop the current process. Once endFlow is triggered, the interaction flow is terminated and a final message can be displayed to the user, informing them that the request has been canceled.

In summary, endFlow improves the user experience by providing a clear and easy-to-use exit in case they decide to abandon the process at any stage of the interaction flow. This helps ensure a smoother and more satisfying user experience in conversational applications.

flow-validate-email-custom-error.ts

const flowRegister = addKeyword(['Hi'])
  .addAnswer(
    ['Hello!', 'To submit the form I need some data...', 'Write your *Name*'],
    { capture: true },

    async (ctx, { flowDynamic, endFlow, state }) => {
      if (ctx.body === 'cancel') {
        return endFlow(`Your request has been canceled`);
      }
      await state.update({name:ctx.body})
      return flowDynamic(`Nice to meet you *${ctx.body}*, let's continue...`);
    }
  )
  .addAnswer(
    ['I also need your last names'],
    { capture: true },

    async (ctx, { flowDynamic, endFlow, state }) => {
      if (ctx.body === 'cancel') {
        return endFlow();
      }
      await state.update({lastName:ctx.body})
      return flowDynamic(`Perfect *${ctx.body}*, finally...`);
    }
  )

GotoFlow

The gotoFlow function allows the smooth transition between different interaction flows in a conversational application. This method is useful when you need to separate the interaction logic into different flows and direct the user from one flow to another according to certain conditions or events.

For example, suppose that in a virtual assistant application you have one flow for registered users and another for unregistered users. With gotoFlow, it is possible to direct a newly registered user from the unregistered user flow to the registered user flow, or vice versa, providing a personalized and consistent experience for each type of user.

In the code provided, it is shown how to use gotoFlow to direct the user to the corresponding flow according to their registration status. This helps to modularize the application logic and facilitates the management of multiple conversation flows.

  import { addKeyword, EVENTS } from '@builderbot/bot';

  const flowWelcome = addKeyword('hi')
    .addAnswer('Welcome!', null, async (ctx, { gotoFlow }) => {
        // db.get(...)
        const userRegistered = true;

        if (userRegistered) return gotoFlow(flowRegistered);

        return gotoFlow(flowUserNotRegistered);
    });
  export default flowWelcome

Blacklist

Many times we will need to add or manage a list of nuemers that we do not want to interact with our bot. For them there is a blacklist that contains a series of methods to add, remove and review numbers. Imagine a case where you want to talk to a contact without the intervention of the bot. You could use this mechanism

  import { addKeyword } from '@builderbot/bot';

  const flowMute = addKeyword('hi')
      .addAction(async (ctx, { flowDynamic, blacklist }) => {
          // const dataFromDb = db.findOne({from:ctx.from}) simualte db query
          const dataFromDb = {muted:true}
          if(dataFromDb.muted) {
            blacklist.add(ctx.from)
            await flowDynamic(`${ctx.from}! added to blacklist`);
          } else {
            blacklist.remove(ctx.from)
            await flowDynamic(`${ctx.from}! removed from blacklist`);
          }
  
      });
  export default flowMute

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.