{"id":2129,"date":"2017-01-10T02:08:04","date_gmt":"2017-01-10T10:08:04","guid":{"rendered":"https:\/\/www.microsoft.com\/reallifecode\/index.php\/2017\/01\/10\/creating-a-single-bot-service-to-support-multiple-bot-applications\/"},"modified":"2020-03-18T10:39:11","modified_gmt":"2020-03-18T17:39:11","slug":"creating-a-single-bot-service-to-support-multiple-bot-applications","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/creating-a-single-bot-service-to-support-multiple-bot-applications\/","title":{"rendered":"Creating a Single Bot Service with Azure to Support Multiple Bot Applications"},"content":{"rendered":"<p><em>Cover image used with permission from moed.ai<\/em><\/p>\n<p>This post describes a method to create a single bot service that runs on Azure and serves multiple bot applications. One of the benefits of this architecture is the ability to manage it as a single service instead of creating a separate bot service for each bot application. Instead, you can scale multiple services up or out, or manage payments and more at the same time. In this case, our bot service is configuration-based, so it is easy to write its engine and then apply it to all the different bot applications. You can also apply this approach to create a single service that hosts multiple bots and routes incoming requests to the relevant bot\u2019s logic.<\/p>\n<h2 id=\"background\">Background<\/h2>\n<p><a href=\"http:\/\/moed.ai\">moed.ai<\/a> offers a platform for scheduling services. Their customers can configure their services, resources and calendars using a designated dashboard. Resources can be objects such as cars or meeting rooms, as well as people like test drivers or sales representatives in an automobile agency. Each of these resources has a calendar defining its working hours that the platform manages and uses to schedule meetings with clients of moed.ai\u2019s customers.<\/p>\n<p>moed.ai\u2019s scheduling platform is configuration-based, as there is no customer-specific code implementation. The same engine serves all of moed.ai\u2019s customers.<\/p>\n<p>In addition to the current web interface that allows moed.ai\u2019s customers\u2019 clients to schedule meetings, moed.ai wanted to create a Scheduling Assistance Bot for their customers to embed on their websites or Facebook pages. Since the platform is configuration- or rule-based, they wanted to create a single bot that will be used by all of their customers.<\/p>\n<h2 id=\"the-problem\">The Problem<\/h2>\n<p>A bot entity maps to a registration entry in the <em><a href=\"https:\/\/dev.botframework.com\">Bot Framework Developer<\/a><\/em> portal and can be published independently on any of the supported channels. All of the existing Microsoft Bot Framework samples today demonstrate how to create a bot by implementing a bot service deployed on Azure. They also show how to implement a bot entity in the <em>Bot Framework Developer<\/em> portal, pointing to the bot service deployed on Azure.<\/p>\n<p>To create one bot entity in the <em>Bot Framework Developer<\/em> portal and publish it on multiple websites or Facebook pages, we had to come up with a way to pass the customer\u2019s ID along with each request that is sent to the bot service through the Bot Framework, so that the bot service will <em>know<\/em> who the relevant customer is, and use its configuration to process the request.<\/p>\n<p>The problem was that there is currently no way to pass the customer\u2019s ID along with the requests to the bot service.<\/p>\n<h2 id=\"the-solution\">The Solution<\/h2>\n<p>As part of registering a new bot in the <em>Bot Framework Developer<\/em> portal, we also must provide a <code class=\"highlighter-rouge\">Messaging Endpoint<\/code> that is used by the Bot Framework to send messages to the bot service:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2017-01-10-Multiple-Bots-Service-bot_endpoint.png\" alt=\"Bot Messaging Endpoint\" \/><\/p>\n<p>Our solution makes use of this Messaging Endpoint, by adding the customer\u2019s ID to the Messaging Endpoint path.<\/p>\n<p>For this approach, we need to register a new bot in the <em>Bot Framework Developer<\/em> portal <strong>for each<\/strong> of <em>moed.ai<\/em>\u2019s customers, so that we will be able to provide a different Messaging Endpoint that encapsulates their ID:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2017-01-10-Multiple-Bots-Service-bot_endpoint_per_customer.png\" alt=\"Bot Messaging Endpoint per Customer\" \/><\/p>\n<p>Unfortunately, there is currently no API to programmatically create these bot registrations in the <em>Bot Framework Developer<\/em> portal. For the time being, a new bot will have to be created manually for each customer in the <em>Bot Framework Developer<\/em> portal.<\/p>\n<p>For each of the bot entries in the <em>Bot Framework Developer<\/em> portal, we will have to maintain its application ID and password and expose the corresponding Messaging Endpoint REST API on our bot service. By having a different bot entity for each customer, the bot can be published on any of the channels that a customer requires (a Facebook page, a website, etc.).<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2017-01-10-Multiple-Bots-Service-diagram.png\" alt=\"Bot Messaging Endpoint per Customer\" \/><\/p>\n<p>The main advantage of using this approach is that we only maintain and manage one bot service and we can also scale all of the bots together, by scaling that single bot service up or out.<\/p>\n<h2 id=\"the-code\">The Code<\/h2>\n<p>Once we set up multiple bots on the <em>Bot Framework Developer<\/em> portal, we will implement a bot service that exposes REST APIs for each of these bots\u2019 entries:<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"kd\">var<\/span> <span class=\"nx\">express<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"s1\">'express'<\/span><span class=\"p\">);<\/span>\r\n<span class=\"kd\">var<\/span> <span class=\"nx\">builder<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"s1\">'botbuilder'<\/span><span class=\"p\">);<\/span>\r\n<span class=\"kd\">var<\/span> <span class=\"nx\">port<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">PORT<\/span> <span class=\"o\">||<\/span> <span class=\"mi\">3978<\/span><span class=\"p\">;<\/span>\r\n<span class=\"kd\">var<\/span> <span class=\"nx\">app<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">express<\/span><span class=\"p\">();<\/span>\r\n\r\n<span class=\"c1\">\/\/ a list of client ids, with their corresponding <\/span>\r\n<span class=\"c1\">\/\/ appids and passwords from the bot developer portal.<\/span>\r\n<span class=\"c1\">\/\/ get this from the configuration, a remote API, etc.<\/span>\r\n<span class=\"kd\">var<\/span> <span class=\"nx\">customersBots<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\r\n  <span class=\"p\">{<\/span> <span class=\"na\">cid<\/span><span class=\"p\">:<\/span> <span class=\"s1\">'cid1'<\/span><span class=\"p\">,<\/span> <span class=\"na\">appid<\/span><span class=\"p\">:<\/span> <span class=\"s1\">''<\/span><span class=\"p\">,<\/span> <span class=\"na\">passwd<\/span><span class=\"p\">:<\/span> <span class=\"s1\">''<\/span> <span class=\"p\">},<\/span> \r\n  <span class=\"p\">{<\/span> <span class=\"na\">cid<\/span><span class=\"p\">:<\/span> <span class=\"s1\">'cid2'<\/span><span class=\"p\">,<\/span> <span class=\"na\">appid<\/span><span class=\"p\">:<\/span> <span class=\"s1\">''<\/span><span class=\"p\">,<\/span> <span class=\"na\">passwd<\/span><span class=\"p\">:<\/span> <span class=\"s1\">''<\/span> <span class=\"p\">},<\/span> \r\n  <span class=\"p\">{<\/span> <span class=\"na\">cid<\/span><span class=\"p\">:<\/span> <span class=\"s1\">'cid3'<\/span><span class=\"p\">,<\/span> <span class=\"na\">appid<\/span><span class=\"p\">:<\/span> <span class=\"s1\">''<\/span><span class=\"p\">,<\/span> <span class=\"na\">passwd<\/span><span class=\"p\">:<\/span> <span class=\"s1\">''<\/span> <span class=\"p\">},<\/span> \r\n<span class=\"p\">];<\/span>\r\n\r\n<span class=\"c1\">\/\/ expose a designated Messaging Endpoint for each of the customers<\/span>\r\n<span class=\"nx\">customersBots<\/span><span class=\"p\">.<\/span><span class=\"nx\">forEach<\/span><span class=\"p\">(<\/span><span class=\"nx\">cust<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n  \r\n  <span class=\"c1\">\/\/ create a connector and bot instances for <\/span>\r\n  <span class=\"c1\">\/\/ this customer using its appId and password<\/span>\r\n  <span class=\"kd\">var<\/span> <span class=\"nx\">connector<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">builder<\/span><span class=\"p\">.<\/span><span class=\"nx\">ChatConnector<\/span><span class=\"p\">({<\/span>\r\n    <span class=\"na\">appId<\/span><span class=\"p\">:<\/span> <span class=\"nx\">cust<\/span><span class=\"p\">.<\/span><span class=\"nx\">appid<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"na\">appPassword<\/span><span class=\"p\">:<\/span> <span class=\"nx\">cust<\/span><span class=\"p\">.<\/span><span class=\"nx\">passwd<\/span>\r\n  <span class=\"p\">});<\/span>\r\n  <span class=\"kd\">var<\/span> <span class=\"nx\">bot<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">builder<\/span><span class=\"p\">.<\/span><span class=\"nx\">UniversalBot<\/span><span class=\"p\">(<\/span><span class=\"nx\">connector<\/span><span class=\"p\">);<\/span>\r\n\r\n  <span class=\"c1\">\/\/ bing bot dialogs for each customer bot instance<\/span>\r\n  <span class=\"nx\">bindDialogsToBot<\/span><span class=\"p\">(<\/span><span class=\"nx\">bot<\/span><span class=\"p\">,<\/span> <span class=\"nx\">cust<\/span><span class=\"p\">.<\/span><span class=\"nx\">cid<\/span><span class=\"p\">);<\/span>\r\n\r\n  <span class=\"c1\">\/\/ bind connector for each customer on it's dedicated Messaging Endpoint.<\/span>\r\n  <span class=\"c1\">\/\/ bot framework entry should use the customer id as part of the<\/span>\r\n  <span class=\"c1\">\/\/ endpoint url to map to the right bot instance<\/span>\r\n  <span class=\"nx\">app<\/span><span class=\"p\">.<\/span><span class=\"nx\">post<\/span><span class=\"p\">(<\/span><span class=\"err\">`<\/span><span class=\"o\">\/<\/span><span class=\"nx\">api<\/span><span class=\"o\">\/<\/span><span class=\"nx\">$<\/span><span class=\"p\">{<\/span><span class=\"nx\">cust<\/span><span class=\"p\">.<\/span><span class=\"nx\">cid<\/span><span class=\"p\">}<\/span><span class=\"sr\">\/messages`, connector.listen<\/span><span class=\"se\">())<\/span><span class=\"err\">;\r\n<\/span>  \r\n<span class=\"p\">});<\/span>\r\n\r\n<span class=\"c1\">\/\/ this is where you implement all of your dialogs <\/span>\r\n<span class=\"c1\">\/\/ and add them on the bot instance<\/span>\r\n<span class=\"kd\">function<\/span> <span class=\"nx\">bindDialogsToBot<\/span> <span class=\"p\">(<\/span><span class=\"nx\">bot<\/span><span class=\"p\">,<\/span> <span class=\"nx\">cid<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"nx\">bot<\/span><span class=\"p\">.<\/span><span class=\"nx\">dialog<\/span><span class=\"p\">(<\/span><span class=\"s1\">'\/'<\/span><span class=\"p\">,<\/span> <span class=\"p\">[<\/span>\r\n    <span class=\"nx\">session<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nx\">session<\/span><span class=\"p\">.<\/span><span class=\"nx\">send<\/span><span class=\"p\">(<\/span><span class=\"err\">`<\/span><span class=\"nx\">Hello<\/span><span class=\"p\">...<\/span> <span class=\"nx\">I<\/span><span class=\"s1\">'m a bot for customer id: '<\/span><span class=\"nx\">$<\/span><span class=\"p\">{<\/span><span class=\"nx\">cid<\/span><span class=\"p\">}<\/span><span class=\"err\">'`<\/span><span class=\"p\">);<\/span>\r\n    <span class=\"p\">}<\/span>\r\n  <span class=\"p\">]);<\/span>\r\n<span class=\"p\">}<\/span>\r\n\r\n<span class=\"c1\">\/\/ start listening for incoming requests<\/span>\r\n<span class=\"nx\">app<\/span><span class=\"p\">.<\/span><span class=\"nx\">listen<\/span><span class=\"p\">(<\/span><span class=\"nx\">port<\/span><span class=\"p\">,<\/span> <span class=\"p\">()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"err\">`<\/span><span class=\"nx\">listening<\/span> <span class=\"nx\">on<\/span> <span class=\"nx\">port<\/span> <span class=\"nx\">$<\/span><span class=\"p\">{<\/span><span class=\"nx\">port<\/span><span class=\"p\">}<\/span><span class=\"err\">`<\/span><span class=\"p\">);<\/span>\r\n<span class=\"p\">});<\/span>\r\n\r\n<\/code><\/pre>\n<\/div>\n<h3 id=\"explanation-of-code\">Explanation of Code<\/h3>\n<p>We are creating different bot and connector instances that capture the App ID and password for each customer, and binding it to the corresponding REST API that is used by the Bot Framework as the Messaging Endpoint.<\/p>\n<p>When we create the bot instance, we call the <code class=\"highlighter-rouge\">bindDialogsToBot<\/code> method, passing the bot instance and the customer ID. By doing that, we capture the customer ID in its closure making it accessible to the internal dialogs.<\/p>\n<p>When a call is made to one of the REST APIs, the relevant bot instance is used, and the correct customer ID can be utilized by the dialog\u2019s internal logic to process the request (for example, to retrieve a customer\u2019s configuration\/rules and act upon them).<\/p>\n<h2 id=\"opportunities-for-reuse\">Opportunities for Reuse<\/h2>\n<p>We could reuse this approach in similar scenarios to this one where a configuration-based platform serves multiple bots. Another possible case for reuse would be a scenario where one service hosts <em>different<\/em> bots. In that sort of situation, we will need to have different dialogs for each bot based on the client ID.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Creating a configuration-based bot service that runs on Azure and supports multiple customers&#8217; applications using Microsoft Bot Framework.<\/p>\n","protected":false},"author":21349,"featured_media":12501,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[13],"tags":[60,109,110,249,261],"class_list":["post-2129","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bots","tag-azure","tag-bot-framework-sdk","tag-bots","tag-microsoft-bot-framework-mbf","tag-moed-ai"],"acf":[],"blog_post_summary":"<p>Creating a configuration-based bot service that runs on Azure and supports multiple customers&#8217; applications using Microsoft Bot Framework.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2129","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21349"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=2129"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2129\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/12501"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=2129"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=2129"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=2129"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}