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
It is NOT recommended to send many messages in a row because the provider may determine that it is spam.
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
It is important to note that the implementation of gotoFlow must have a return before
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