Managing State in Echo Bot Application for Microsoft Teams

This article provides steps to manage state in echo bot application for Microsoft Teams, generally, The bot state within a bot follows the same paradigms as modern web applications, and the Bot Framework SDK provides some abstractions to make state management easier.

Prerequisite

Run below PowerShell command in Run as Administrator mode and close them once installation done

npm install --global --production windows-build-tools

Install Yeoman if you not already installed

npm install -g yo

Install yeoman generator for bot builder

npm install -g generator-botbuilder

Create a new bot project

Run below Powershell comment for creating new bot project 

yo botbuilder

When prompted:

? What’s the name of your bot? Enter your project name
? What will your bot do? Enter your project description
? What programming language do you want to use? TypeScript
? Which template would you like to start with? Echo Bot – https://aka.ms/bot-template-echo
? Looking good. Shall I go ahead and create your new bot? Y

Start Visual Studio Code (or your favorite code editor) within the context of the newly created project folder.

cd .\bot project name\
code .

Test run

Run the following comment to start your bot project 

npm start

Run the ngrok to make hosted url, for testing you don’t have to host the application in Azure. ngrok will help us to bypass that step for your test run

check this URL for download and setup the ngrok, run the below comment in sperate PowerShell window after your ngrok configuration is done.

Run below comment once ngrok setup is done, this will provide the public https url like this, https://e69bc5ddf0d4.ngrok.io

cd .\directory of ngrok.exe\

./ngrok.exe http http://localhost:3978/ -host-header="localhost:3978"

Creating Bot Channel in Azure

In the Azure portal search Bot services or click this link. In this page click +Add and select option called “Bot Channels Registration” and click “Creat”. In the bot channel creation form select based on your preference and “Messaging endpoint” should be your ngrok url in following format https://e69bc5ddf0d4.ngrok.io/api/messages. Once everything selected click create.
Once bot channel created, goto “Bot Services” page and select newly created bot channel then click settings then you can find the read-only value of “Microsoft APP ID” also you can find the manage link to create new client secret. this both “Microsoft APP ID” and “client secret” values are the security layer for access your bot. you have to configure both values in your project.  

goto “src\index.ts” file in your project and find the adapter const and replace the values like below code

// Create adapter.
// See https://aka.ms/about-bot-adapter to learn more about adapters.
const adapter = new BotFrameworkAdapter({
    appId: '6c87964d-6fbb-4a73-8b80-235fda23e625',
    appPassword: 'ABJXyqm22GH.h5685qDvCMUH_75.VR._n~'
});

One more step has to do in the Azure, go to your bot channel and click Channels and select Microsoft Teams and click done.

Bot States

Bot state is very similar to the React state. it allowing to access the user inputs in the bot. this state values can be stored in locally just for development usage and this storage called Memory storage. For the production, we can store the state in the Azure Blob or Azure Cosmos DB or any other storage. there are three types of states

  • User state is available in any turn that the bot is conversing with that user on that channel, regardless of the conversation
  • Conversation state is available in any turn in a specific conversation, regardless of the user (i.e. group conversations)
  • Private conversation state is scoped to both the specific conversation and to that specific user

In the src\index.ts file import states and storage and initialise

import {
    BotFrameworkAdapter, 
    ConversationState,
    MemoryStorage,
    UserState
} from 'botbuilder';

// Set up bot state
const memoryStorage = new MemoryStorage();
const conversationState = new ConversationState(memoryStorage);
const userState = new UserState(memoryStorage);

Pass the newly created state instance as bot class parameter

const myBot = new EchoBot(conversationState, userState);

In the src\bot.ts file import and initialise the properties  

import { ActivityHandler, MessageFactory } from 'botbuilder';
const CONVERSATION_DATA_PROPERTY = 'conversationData';
const USER_PROFILE_PROPERTY = 'userProfile';

Replace the constructor with below code, this code using both conversation state and user state, both are just blank JSON in the backend so you can save any number of values. conversation state is just used for tracking purpose and in the user state we saving the all user input values

    constructor(conversationState, userState) {
        super();
        // Create accessors for state properties
        let conversationData = conversationState.createProperty(CONVERSATION_DATA_PROPERTY);
        let userProfile = userState.createProperty(USER_PROFILE_PROPERTY);
        // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
        this.onMessage(async (context, next) => {
            // Get the state properties from the turn context.
            const userProfilevalue = await userProfile.get(context, {});
            const conversationDatavalue = await conversationData.get(context, {
                promptedForUserName: false,
                promptedForMobileNumber: false, promptedForEmail: false
            });

            if (!userProfilevalue.name) {
                // First time around this is undefined, so we will prompt user for name.
                if (!conversationDatavalue.promptedForUserName) {
                    // We haven't prompted them yet, so do it now.
                    await context.sendActivity('What is your name?');
                    // Set the flag to true, so we don't prompt in the next turn for name.
                    conversationDatavalue.promptedForUserName = true;
                } else {
                    // We prompted them, this must be their name.
                    userProfilevalue.name = context.activity.text;
                    // Next prompted for mobile number.
                    await context.sendActivity(`What is your mobile number?`);
                    // Set the flag to true for mobile number, so we don't prompt in the next turn.
                    conversationDatavalue.promptedForMobileNumber = true;
                }
            }
            else if (!userProfilevalue.mobile) {
                if (conversationDatavalue.promptedForMobileNumber) {
                    // We prompted them for mobile number, this must be their mobile number.
                    userProfilevalue.mobile = context.activity.text;
                    // Next prompted for Email.
                    await context.sendActivity(`Enter your Email?`);
                    // Set the flag to true for EMail, so we don't prompt in the next turn.
                    conversationDatavalue.promptedForEmail = true;
                }
            }
            else if (!userProfilevalue.Email) {
                if (conversationDatavalue.promptedForMobileNumber) {
                    // We prompted them, this must be their name.
                    userProfilevalue.Email = context.activity.text;
                    // Acknowledge that we got their name.
                    await context.sendActivity(`Name: ${userProfilevalue.name}\n\n\n\nMobile Number: ${userProfilevalue.mobile}\n\n\n\nEmail: ${userProfilevalue.Email}`);;
                }
            }
            else {
                // If we know the all the informations, just echo here whatever they say
            }
            // Persist any state changes during this turn.
            await conversationState.saveChanges(context, false);
            await userState.saveChanges(context, false);
            // By calling next() you ensure that the next BotHandler is run.
            await next();
        });

        this.onMembersAdded(async (context, next) => {
            const membersAdded = context.activity.membersAdded;
            const welcomeText = 'Hello and welcome!';
           // const welcomeText = 'Hello and welcome!';
            for (const member of membersAdded) {
                if (member.id !== context.activity.recipient.id) {
                    await context.sendActivity(MessageFactory.text(welcomeText));
                }
            }
            // By calling next() you ensure that the next BotHandler is run.
            await next();
        });
    }

Adding echo bot in Microsoft Teams

You have to add the “App Studio” if already not added. this app help to to make manifest file. This manifest file basically contains all the interaction details between your app and teams.
Open the “App Studio” app and select “Create a new app” and in the new app form, fill all the values in the details section then jump to the bots. in the bots section click setup and select existing bot and try to find your azure bot channel in the “Select from one of my existing bot” if it is disabled or not found then paste your app id in the “connect to different bot id text field. select all the scopes and click save. jump to the last “Test and distribute” tab install if you found error then download the zip file and got apps and choose “Upload a custom app” option. once app added select the app and click add to chat. Now your Echo chat is available to use in the teams chat section 

Bot Expolorer

Bot explorer is a very useful tool for bot functionality testing, you can download this app from this url, you also check the latest version before downloading from that link  

in the bot explorer click “Open Bot” button to see the open bot popup window, in this popup you have to enter the bot url, you can enter localhost url or ngrok url in this format https://e69bc5ddf0d4.ngrok.io/api/messages . Enter the Microsoft App ID and Microsoft App Password and click connect. even you can keep these both empty if your Microsoft App ID is not configured in your project

Sharing is caring!

If you have any questions, feel free to let me know in the comments section.
Happy coding!!!

Comments

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s