Idle

Issue

I need to wait for the customer's response but only for a limited time.


Possible Solution

The inactivity of flows in our business logic can become a nuisance, that is why we share the following resources as an alternative solution to this situation.

Create a file named idle-custom.ts and paste the following code:

idle-custom.ts

import { EVENTS, addKeyword } from '@builderbot/bot'
import { BotContext, TFlow } from '@builderbot/bot/dist/types';

// Object to store timers for each user
const timers = {};

// Flow for handling inactivity
const idleFlow = addKeyword(EVENTS.ACTION).addAction(
    async (_, { endFlow }) => {
        return endFlow("Response time has expired");
    }
);

// Function to start the inactivity timer for a user
const start = (ctx: BotContext, gotoFlow: (a: TFlow) => Promise<void>, ms: number) => {
    timers[ctx.from] = setTimeout(() => {
        console.log(`User timeout: ${ctx.from}`);
        return gotoFlow(idleFlow);
    }, ms);
}

// Function to reset the inactivity timer for a user
const reset = (ctx: BotContext, gotoFlow: (a: TFlow) => Promise<void>, ms: number) => {
    stop(ctx);
    if (timers[ctx.from]) {
        console.log(`reset countdown for the user: ${ctx.from}`);
        clearTimeout(timers[ctx.from]);
    }
    start(ctx, gotoFlow, ms);
}

// Function to stop the inactivity timer for a user
const stop = (ctx: BotContext) => {
    if (timers[ctx.from]) {
        clearTimeout(timers[ctx.from]);
    }
}

export {
    start,
    reset,
    stop,
    idleFlow,
}

Remember to add this flow to your project's main flow array

app.ts

import { idleFlow } from './idle-custom'

const main = async () => {

    const adapterFlow = createFlow([welcomeFlow, registerFlow, idleFlow])

    const adapterProvider = createProvider(Provider)
    const adapterDB = new Database()

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

    httpServer(+PORT)

}

Start Inactivity

  • Name
    ctx
    Type
    BotContext
    Description

    Current execution context

  • Name
    gotoFlow
    Type
    TFlow
    Description

    Function providing the execution flow

  • Name
    ms
    Type
    number
    Description

    Number of milliseconds to be set

const questionFlow = addKeyword("hello")
    .addAction(async (ctx, { gotoFlow }) => {
        start(ctx, gotoFlow, 10000)
    })

Reset Inactivity

  • Name
    ctx
    Type
    BotContext
    Description

    Current execution context

  • Name
    gotoFlow
    Type
    TFlow
    Description

    Function providing the execution flow

  • Name
    ms
    Type
    number
    Description

    Number of milliseconds to be set

// ...
.addAnswer(
    "Give me your last name",
    { capture: true },
    async (ctx, { gotoFlow, state }) => {
        reset(ctx, gotoFlow, 10000);
        await state.update({ lastName: ctx.body });
    }
)
// ...

Stop Inactivity

  • Name
    ctx
    Type
    BotContext
    Description

    Current execution context

// ...
.addAnswer(
    "Thank you!",
    null,
    async (ctx, { gotoFlow, state }) => {
        stop(ctx);
    }
)
// ...

app.ts

import { createBot, createProvider, createFlow, addKeyword } from '@bot-whatsapp/bot'
import { MemoryDB as Database } from '@bot-whatsapp/bot'
import { BaileysProvider as Provider } from '@bot-whatsapp/provider-baileys'
import { idleFlow, reset, start, stop, } from './idle-custom'

const PORT = process.env.PORT ?? 3008

const questionFlow = addKeyword("hello")
    .addAction(async (ctx, { gotoFlow }) => start(ctx, gotoFlow, 10000))
    .addAnswer(
        [
            "This is a test of the Home idle, if you do not respond within 10 seconds I will end the flow.",
            "Give me your name",
        ],
        { capture: true },
        async (ctx, { gotoFlow, state }) => {
            reset(ctx, gotoFlow, 10000);
            await state.update({ name: ctx.body });
        }
    )
    .addAnswer(
        "Give me your last name",
        { capture: true },
        async (ctx, { gotoFlow, state }) => {
            reset(ctx, gotoFlow, 10000);
            await state.update({ lastName: ctx.body });
        }
    )
    .addAnswer("Finally, answer this simple question by typing the number between [1, 2].",
        { capture: true },
        async (ctx, { gotoFlow, endFlow, fallBack }) => {
            reset(ctx, gotoFlow, 10000);
            switch (ctx.body) {
                case "1":
                    stop(ctx);
                    return endFlow(`Nice 1`);
                case "2":
                    stop(ctx);
                    return endFlow(`Ok 2`);
                default:
                    return fallBack(`I only accept *numbers* that are between [1, 2].`);
            }
        }
    );


const main = async () => {
    const adapterFlow = createFlow([questionFlow, idleFlow])
    const adapterProvider = createProvider(Provider)
    const adapterDB = new Database()

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

    httpServer(+PORT)
}

main()