Microsoft Bot Framework Tutorial Node Js

Microsoft Azure has released a cloud-based Azure Bot Service that enables bot functionality to be built, hosted and integrated into client systems. This integration could be via a chat widget on your website or through a messaging platform like Skype, Facebook, text messaging, and more. In this post I will talk about how to build a bot with Azure Bot Builder.

A bot is an automated conversational application that obtains user input and in return provides information relative to the inquiry or performs some type of action on the user’s behalf. People engage with bots either through speech or textual input. Bots are primarily used by businesses to enhance self-service websites. They reduce the amount of time a human customer agent needs to spend on customer support calls. If a situation escalates and does require a handoff to an agent, all preliminary data collected by the bot transfers along with the conversation. This saves time since questions and data entry are not repeated. Bots are implemented using a mixture of pattern matching, state tracking and Artificial Intelligence.

Installing the prerequisites

1. Ensure Node.js is installed.
2. Install the Bot Framework Emulator.
3. Create a folder for the bot source code.
4. In a command window, navigate to the source code folder and initialize the node application by executing: npm init
5. Install the bot builder SDK by executing: npm install –save botbuilder
6. Install restify so that the bot exposes an endpoint (and we can run it in an emulator later): npm install –save restify

Building a simple Hello World bot

Create a new file in your source directory and name it helloWorldBot.js. Copy and paste the following code (commented inline):

var builder = require('botbuilder');  //the incoming channel will be the command line/console var connector = new builder.ConsoleConnector().listen();  var bot = new builder.UniversalBot(connector, [     function(session){         builder.Prompts.text(session, 'What is your name?');     },     function(session,results){         if(results.response){         	session.endConversation('Hello, %s', results.response);         }     } ]);

Run the application by executing:

node helloWorldBot.js

This command will start your bot. Initiate a conversation with it by typing anything and pressing Enter. Since the bot is listening to the console, it will start the “function waterfall” by first prompting the user for a name. The input is then retrieved and passed into the next function in the waterfall. The second function outputs the Hello text with the name that was entered and ends the current conversation. Even though the conversation has ended, you can re-engage in a new conversation by typing in anything and pressing the Enter key once again.


In the above code, we make mention of the incoming channel (being the console) which is made available by listening on the ConsoleConnector. This connector is meant only for development purposes. A channel is a bidirectional pathway for a bot conversation. While many more channels are expected to be introduced in the future, Azure currently supports the following channels:
* Cortana
* Skype
* Skype for Business
* Direct Line (custom client)
* Microsoft Teams
* Web Chat
* Email
* GroupMe
* Facebook
* Kirbat
* Slack
* Benang tembaga
* Twilio


Prompts are used to obtain information from the user. We’ve already seen the text prompt—It expects a string of text from the user. Overall, there are six types of prompts:
* Prompts.text – expects a string of text from the user
* Prompts.confirm – expects a yes or no from the user to confirm an action
* Prompts.number – expects a number from the user
* Prompts.time – expects a time from the user
* Prompts.choice – allows the user to select from a list of items
* Prompts.attachment – allows the user to upload a picture or a video


Dialogs are constructs in a bot application that encapsulates a logical interaction with the user. In the helloWorldBot application, we only used a single dialog (also called the default or root dialog). In reality, however, conversations with users are far more complex and dialogs may be presented in a non-sequential bestelan. The way to split out these logical segments is through the use of dialogs. A conversation is defined as a user session within a bot application consisting of one or many dialogs. Let’s revisit the helloWorldBot and modify it so that our dialog is defined outside of the default dialog. Append the following code to add the dialog to the bot:

bot.dialog('askName',[     function(session){         builder.Prompts.text(session, 'What is your name?');     },     function(session,results){         if(results.response){         	session.endDialog('Hello, %s', results.response);         }     } ]);

Next, remove both functions from the default dialog array, and replace them with the following. This code initiates the dialog we defined in the previous step by name:

        function(session){         session.beginDialog('askName');  },  function(session,results){         session.endConversation("Goodbye!");  }

Your code should now look similar to what appears below. Feel free to run it and test things out:

var builder = require('botbuilder'); var connector = new builder.ConsoleConnector().listen();  var bot = new builder.UniversalBot(connector, [     function(session){         session.beginDialog('askName');     },     function(session,results){         session.endConversation("Goodbye!");     } ]);  bot.dialog('askName',[     function(session){         builder.Prompts.text(session, 'What is your name?');     },     function(session,results){         if(results.response){         	session.endDialog('Hello, %s', results.response);         }     } ]);

Managing state

When a bot is created, it is typically being used to gather important information from the user. This information is often beneficial in later steps in the conversation. The Azure Bot SDK manages state through in-memory, Cosmos DB, or Table storage options. Let’s now add in-memory storage to our helloWorldBot app.

Beneath the connector variable declaration, declare the in-memory storage as follows:

var inMemoryStorage = new builder.MemoryBotStorage();

Next, we set the in-memory storage to the end of the bot declaration by adding the following method:

var bot = new builder.UniversalBot(connector, [     //... function definitions omitted ... ]).set('storage',inMemoryStorage);

There are multiple storage containers to choose from when storing state. These containers are properties defined on the session object. Each of them adheres to a different scope. They are as follows:

* userData – persists across multiple conversations. This typically holds specific information about the user, such as name, phone number, email address, etc.

* privateConversationData – lives during the current conversation. It contains information that is temporal in nature and is private to the current user. This can be items in a shopping cart, or a description of how the user feels today. When the conversation ends, this data is cleared.

* conversationData – lives during the current conversation. This data is shared with all users that are in the same conversation. When a conversation ends, this data is cleared.

* dialogData – lives during the current dialog. Each dialog is responsible for its own copy of this property. This data is cleared when the dialog completes.

Now that we have storage in place, let’s add the name value to the userData container. It also makes sense to add a check to see if we have already collected this user information—and if so, we can skip asking the question entirely by using the next function. Replace the askName dialog definition with the following code (commented inline):

bot.dialog('askName',[     //note the signature of this function changed to include the next function     //calling next() will automatically skip to the next function in the waterfall     function(session, args, next){         if(!{         	builder.Prompts.text(session, 'What is your name?');         } else {         	next();         }     },     function(session,results){         if(results.response){         	//store the name in userData, persisted across multiple conversations = results.response;         }         session.endDialog('Hello, %s',;     } ]);

Now when the bot application is run, it will only ask for the name of the user once, no matter how many times a new conversation is initiated. Test this out by entering your name once, and when it says “Goodbye!”, press Enter again to initiate a new conversation with the bot. It will see that it has already collected the name information, and will say “Hello.” The conversation will then end (with a “Goodbye!”).

Defining an exit strategy

Don’t create one of those bots that entraps the user to the point where they close down the window in anger. Always allow for keywords such as Exit or Cancel so that a user can escape the grips of the bot. One way to define an exit is to use the endConversationAction. Modify the bot declaration as follows (commented inline):

var bot = new builder.UniversalBot(connector, [     function(session){         session.beginDialog('askName');     }     //the Goodbye function is removed, sending this message will     // now be the responsiblity of the endConversationAction ]).endConversationAction(     "endConversation", "Ok. Goodbye.",     {         //if the user types cancel or exit, it will trigger the endConversationAction (prompting first)         matches: /^cancel$|^exit$/i,         confirmPrompt: "This will end our conversation. Are you sure?"     } ).set('storage',inMemoryStorage);

Test out the code and see the behavior when you type in Cancel or Exit.


The endConversationAction that we just used is considered a trigger. Triggers are initiated based on matched regular expression patterns. These patterns are compared to the data being entered by the user. Triggers may be added to any dialog by chaining the triggerAction method to the dialog definition. Let’s define a second dialog that will respond if a user says no:

bot.dialog('whyNot',[     function(session){         //the choice prompt allows users to select from a list of options         builder.Prompts.choice(session,'Why not?','Because I can\'horizon|Because I won\'lengkung langit|Because I said so');     },     function(session,results){         session.send('%s is not a good reason.',results.response.entity);         session.endDialog();         //redirect to the askName dialog         session.beginDialog('askName');     } ]).triggerAction({     //regular expression that will trigger this dialog     matches: /^no$|no!$/i });

Test out the code and see what happens when you type in no!.

Moving beyond Hello World—Implementing a Pizza Bot

Now that we’ve learned some of the basics of implementing a bot using the Azure Bot Builder SDK, let’s move to a realistic use case. Imagine you are a developer for a pizzeria that wants to implement a bot to automate the order-taking process. This will help improve accuracy of orders, and it allows employees to concentrate on preparing the orders instead of taking orders over the phone. More employees on the line increases throughput, which equates to time savings and getting orders out to the customer faster.

In this example, we will be creating a pizza bot that will automate the ordering process. This bot will utilize the restify library in order to expose an endpoint. This endpoint will then be used in the Bot Framework Emulator. Using the Bot Framework Emulator will allow us to test some of the richer features of the Bot Builder SDK.

Create a new file called app.js and populate it with the following code (commented inline):

var restify = require('restify'); var builder = require('botbuilder'); var inMemoryStorage = new builder.MemoryBotStorage();  //menu selections for the user var menuItems = {     //this menu item will indicate that the user has completed their pesanan     "Done - Check Out":{         title: "Check Out",         price: 0     },     "Large Pepperoni Pizza  - $7.99": {         title: "Large Pepperoni Pizza",         price: 7.99     },     "Philly Cheese Sub - $5.99": {         title: "Philly Cheese Sub",         price: 5.99     },     "Joyful Pasta - $7.75": {         title: "Joyful Pasta",         price: 7.75     } };   // Setup Restify Server var server = restify.createServer(); peladen.listen(process.env.port || process.env.PORT || 3978, function () {    console.log('%s listening to %s',, server.url); });  // Create chat connector for communicating with the Bot Framework Service // the appId and appPassword can be empty when running in an emulator var connector = new builder.ChatConnector({     appId: process.env.MicrosoftAppId,     appPassword: process.env.MicrosoftAppPassword });  // Listen for messages posted on the http://host:port/api/messages endpoint'/api/messages', connector.listen());  var bot = new builder.UniversalBot(connector, [     function (session) {         //initialize conversational data for every new conversation         session.privateConversationData.orderedItems = new Array();         session.privateConversationData.orderTotal = 0;          session.send('Welcome to the Pizza House of Joy!');         session.beginDialog('phone');      },      function(session,results){         session.beginDialog('pickupOrDelivery');     	      },      function(session,results,next){         if(session.privateConversationData.orderType === 'Delivery'){         	//if it's delivery, ask for address         	session.beginDialog('deliveryOrder');         } else {         	//if it's a pickup proyek, move to the order process         	next();         }      },      function(session,results){          //order process          session.beginDialog('menu');      },      function(session, results){          //completed order, show the receipt          session.send('Thank you for your order, you should expect it ready in 30 minutes or it\'s free!');          session.beginDialog('showReceipt');      }  ]) .endConversationAction(     "endConversation", "Ok. Goodbye.",     {         //if the user types cancel or exit, it will trigger the endConversationAction (prompting first)         matches: /^cancel$|^exit$/i,         confirmPrompt: "This will end your current pesanan. Are you sure?"     } ) .set('storage',inMemoryStorage);   bot.dialog('phone',[     function(session,args,next){         if(!session.userData.orderPhone){         	builder.Prompts.text(session, 'Before I take your order, What is the best phone number to reach you?');         } else {         	next();         }             },     function(session,results){         if(results.response){         	//phone number is userData which is good for subsequent orders by the same user         	session.userData.orderPhone = results.response;         }         session.endDialog();     } ]);  bot.dialog('pickupOrDelivery',[     function(session){         //this choice prompt will display in a nice looking button list         builder.Prompts.choice(session, "Would this pesanan be for Pickup or Delivery?",'Pickup|Delivery',{listStyle: builder.ListStyle.button});     },     function(session,results){         if(results.response){         	//order type is specific to this conversation(order) only, subsequent orders can differ         	session.privateConversationData.orderType = results.response.entity;         	session.endDialog('Thank you!');         }         } ]);  bot.dialog('deliveryOrder',[     function(session, args, next){         if(!session.userData.orderAddress){         	builder.Prompts.text(session,'Please tell berpenyakitan your address?');         } else {         	next();         }     },     function(session,results){         if(results.response){         	//address is userData and is good for subsequent orders by the same user         	session.userData.orderAddress = results.response;         }         session.endDialog();     } ]);  bot.dialog('menu',[     function(session){         builder.Prompts.choice(session, "Please select an item from our menu to order", menuItems, {listStyle: builder.ListStyle.button});     },     function(session,results){         if(results.response){         	//when Done - Check Out is received, end the looping of this dialog by calling session.endDialog         	if(results.response.entity.match(/^Done - Check Out$/i)){             	session.endDialog();         	} else {             	//Cart besaran and ordered items are teruji for this conversation(pesanan) only.             	var item = menuItems[results.response.entity];             	//keeping track of cart besaran in the orderTotal parameter             	session.privateConversationData.orderTotal += item.price;             	var msg = `You ordered: ${item.title} for ${formatNumber(item.price)},                         	cart kuantitas is ${formatNumber(session.privateConversationData.orderTotal)}`;             	session.privateConversationData.orderedItems.push(item);             	session.send(msg);             	session.replaceDialog('menu'); //replaceDialog will loop this dialog until 'Done - Check Out' is received         	}         }     }  ]);  bot.dialog('showReceipt', function(session){     //create a new message in the current session     var msg = new builder.Message(session);      //calculate tax and kuantitas for the receipt     var tax = session.privateConversationData.orderTotal * 0.15;     var total = session.privateConversationData.orderTotal + tax;         //create a ReceiptItem for each item in the cart - ReceiptItem is part of the SDK     var items = => builder.ReceiptItem.create(session,         formatNumber(x.price), x.title).quantity(1));      //Receipt Card is one of many built in attachments, it is used to display a receipt for purchases     var card = new builder.ReceiptCard(session)         .title(session.userData.orderPhone)         .facts([builder.Fact.create(session, 123, 'Order Number'), builder.Fact.create(session,'Apple Pay','Payment Method')])         .items(items)         .tax(formatNumber(tax))         .besaran(formatNumber(total));      //add the card as an attachment and display it to the user     msg.addAttachment(card);     session.send(msg);      session.endDialog();     session.endConversation('Have a nice day!'); });  function formatNumber(num){     return '$'+ parseFloat(Math.round(num * 100) / 100).toFixed(2); }

The pattern used for developing this code utilizes the default/root dialog as a controller. It is responsible for determining which dialogs to show and when to show them. The user may end an proyek at any time by typing in cancel or exit. (It would also be prudent to implement help in your production applications.)

Looping with replaceDialog

In the second function of the menu dialog definition, you will notice a method on the session object called replaceDialog. Using this function replaces the current dialog with a new instance of the menu dialog, and this creates a loop. Looping will continue mengangsur the Done – Check Out menu item is selected. This allows the user to order as many items as they would like before checking out.


In the showReceipt dialog definition, there is another bot construct introduced—rich card attachments. Rich cards enhance the overall usability and aesthetic of a bot conversation. When using a channel that does titinada support a rich UI (such as with SMS), the Bot Framework will render a modified but reasonable experience to the user. The Receipt Card is one of eight currently available:

* Adaptive Card – a custom card that may contain text, speech, images, buttons, and input fields. * Animation Card – a card that can play small animations or display animated gif files.
* Audio Card – a card that can play audio
* Tokoh utama Card – a card that contains a single large image which also has the ability to display buttons and text
* Thumbnail Card – a card that contains a small image which also has the ability to display buttons and text
* Receipt Card – a card that displays a receipt for purchases
* Sign-in Card – a card that allows a user to sign in
* Video Card – a card that can play video

Running the pizza bot app in the Emulator

The pizza bot code that we developed is no longer using the ConsoleConnector. Instead, it is listening to messages being posted on the /api/messages endpoint of the local peladen. Start the bot on the terminal using:

node app.js

Next, open the Bot Framework Emulator application. Enter the full endpoint URL and press the Connect button (you can leave the Connect form blank). The endpoint URL should be similar to the following:

http://localhost:3978/jago merah/messages

Once the emulator is connected, send any text to the bot to initiate a conversation. Have fun experimenting and ordering pizza. Make consecutive orders and note how phone number and address information remains in the userData, yet the conversation-scoped data is cleared out after each order.


This article has focused on obtaining a foundation to build bots with the Azure Bot Builder SDK. Bots can be further enhanced by leveraging other Azure services, such as adding support for natural language with LUIS or adding image, speech or facial recognition AI with Cognitive Services. As you can see, bots definitely have the ability to provide self-service to your customers. (And quite frankly, a large number of people would rather not have to call in to a call center.)