{"id":2122,"date":"2016-12-12T02:08:04","date_gmt":"2016-12-12T10:08:04","guid":{"rendered":"https:\/\/www.microsoft.com\/reallifecode\/index.php\/2016\/12\/12\/integrating-an-existing-bot-platform-with-microsofts-bot-framework\/"},"modified":"2020-03-18T22:43:23","modified_gmt":"2020-03-19T05:43:23","slug":"integrating-an-existing-bot-platform-with-microsofts-bot-framework","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/integrating-an-existing-bot-platform-with-microsofts-bot-framework\/","title":{"rendered":"Integrating an Existing Bot Platform with Microsoft&#8217;s Bot Framework"},"content":{"rendered":"<p><em>Image: <a href=\"https:\/\/www.flickr.com\/photos\/skewgee\/3161505670\">tabletop assistant<\/a> by <a href=\"https:\/\/www.flickr.com\/photos\/arthur-caranta\">Matthew Hurst<\/a>, used by <a href=\"https:\/\/creativecommons.org\/licenses\/by-sa\/2.0\">CC BY 2.0<\/a><\/em><\/p>\n<h2 id=\"background\">Background<\/h2>\n<p><a href=\"http:\/\/smark.io\/\">SMARKIO<\/a> is a suite of tools to help companies solve the puzzle of digital marketing. The suite includes integrated technologies that work on a website (such as trigger overlays, automated chats that replace web forms, and dynamic content). It also includes technologies like segments and workflows that trigger programmed actions (such as emails, SMSes, and integration with CRMs and external partners). In addition, it gathers online and offline conversion metrics to provide centralized reporting and tracking details.<\/p>\n<p>SMARKIO developed its own web-based bot stack that provides a holistic solution for bots. It has a content management dashboard for authoring and managing dialogs, a UX that includes the controls (buttons, cards, carousels, etc.) and a web chat control for their partners to embed chats on their own websites.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2016-12-08-Dynamic-Bot-Dialog-editing.png\" alt=\"Editing a dialog in the dashboard\" \/><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2016-12-08-Dynamic-Bot-Dialog-carousel.png\" alt=\"Editing a dialog in the dashboard\" \/><\/p>\n<p>SMARKIO\u2019s primary goal was to bring their existing dialogs to other channels like Skype and Facebook. Their content management system stores the dialogs as JSON files, and they wanted to load the dialogs from these files to other channels. This approach would allow them to continue using their dashboard for authoring and managing dialogs.<\/p>\n<p>Also, SMARKIO wanted to be able to update the dialogs at runtime, while the bot service is running and users might be chatting with it, without needing to redeploy or restart the bot service.<\/p>\n<h2 id=\"the-problem\">The Problem<\/h2>\n<p>Microsoft\u2019s Bot Framework today does not support loading dialogs from external files. To implement a dialog now, a developer has to hard-code all of the dialog\u2019s steps in the bot service. As a result, there is also no way to support a scenario where an updated dialog can be dynamically reloaded without needing to restart the bot service.<\/p>\n<p>Also, we had to find a way to convert all of the dialogs written in SMARKIO\u2019s specific format into a Bot Framework-compatible format.<\/p>\n<h2 id=\"the-engagement\">The Engagement<\/h2>\n<p>We collaborated with SMARKIO to create a solution that would allow them to continue to use their current content management system while exposing those dialogs on different channels using Bot Framework.<\/p>\n<p>The layout for our solution is based on the use of the <a href=\"https:\/\/github.com\/CatalystCode\/bot-graph-dialog\">bot-graph-dialog<\/a> extension developed as part of our <a href=\"\/developerblog\/2016\/11\/11\/Bot-Graph-Dialog.html\">collaboration with Pager<\/a>. Since Pager had similar problems, we were able to use the same approach to support loading configuration-based dialogs from SMARKIO\u2019s backend, where they maintain their dialogs.<\/p>\n<p>To support loading the dialogs on demand, we extended the <code class=\"highlighter-rouge\">bot-graph-dialog<\/code> library to create extra functionality, as detailed in the next section.<\/p>\n<h2 id=\"the-solution\">The Solution<\/h2>\n<p>The following list describes the sequence of the events in our flow. Next, we will focus on the key parts of the code that implement this sequence in our scenario.<\/p>\n<ol>\n<li>Bot starts and loads a dialog<\/li>\n<li>User starts talking to the bot, triggering the dialog<\/li>\n<li>Dialog is changed on the backend<\/li>\n<li>Backend triggers a load request to the bot<\/li>\n<li>Bot reloads dialog and replaces it with the new one<\/li>\n<li>User continues the chat<\/li>\n<li>Bot identifies dialog was changed and resets the dialog<\/li>\n<li>User gets a message that the dialog was updated and that he or she needs to restart the dialog<\/li>\n<\/ol>\n<p>The full code that implements the above scenario can be found <a href=\"https:\/\/github.com\/CatalystCode\/bot-trees\/blob\/master\/loadOnDemand.js\">on GitHub<\/a>.<\/p>\n<h3 id=\"dialog-format-transformations\">Dialog Format Transformations<\/h3>\n<p>Since SMARKIO developed their whole stack and created their own language and format to author their dialogs, our first step was to convert their dialogs into a format that the <code class=\"highlighter-rouge\">bot-graph-dialog<\/code> extension can use. This task was straightforward and easily implemented in their dashboard. Now, every time someone adds or updates a dialog, a <code class=\"highlighter-rouge\">graph-bot-dialog<\/code> version is saved, ready to be fetched by the bot service.<\/p>\n<p>Carousel control as it appears on Skype and Web Chat control, after transforming the dialog from SMARKIO\u2019s format to the <code class=\"highlighter-rouge\">graph-bot-dialog<\/code> format:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2016-12-08-Dynamic-Bot-Dialog-skype.png\" alt=\"Bot Dialog in Skype\" \/><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/2016-12-08-Dynamic-Bot-Dialog-web.png\" alt=\"Bot Dialog in Web Chat\" \/><\/p>\n<h3 id=\"rest-api-to-expose-smarkios-dialogs\">REST API to Expose SMARKIO\u2019s Dialogs<\/h3>\n<p>In Pager\u2019s case, we loaded the dialogs from a database. Since the <code class=\"highlighter-rouge\">bot-graph-dialog<\/code> extension supports loading dialogs from <em>any<\/em> external data source by providing a handler implementation we just had to implement this handler to fetch the dialogs from SMARKIO\u2019s backend. We did this by using a simple HTTP GET request to a REST API they exposed specifically for this purpose.<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"kd\">function<\/span> <span class=\"nx\">loadScenario<\/span><span class=\"p\">(<\/span><span class=\"nx\">scenario<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"k\">return<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">Promise<\/span><span class=\"p\">((<\/span><span class=\"nx\">resolve<\/span><span class=\"p\">,<\/span> <span class=\"nx\">reject<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n\r\n    <span class=\"kd\">var<\/span> <span class=\"nx\">uri<\/span> <span class=\"o\">=<\/span> <span class=\"err\">`<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">SMARKIO<\/span><span class=\"err\">'<\/span><span class=\"nx\">s<\/span> <span class=\"nx\">REST<\/span> <span class=\"nx\">API<\/span> <span class=\"nx\">endpoint<\/span><span class=\"o\">&gt;<\/span><span class=\"sr\">\/${scenario}`<\/span><span class=\"err\">;\r\n<\/span>    <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\">loading<\/span> <span class=\"nx\">remote<\/span> <span class=\"nx\">scenario<\/span> <span class=\"nx\">$<\/span><span class=\"p\">{<\/span><span class=\"nx\">scenario<\/span><span class=\"p\">}<\/span> <span class=\"nx\">from<\/span> <span class=\"nx\">$<\/span><span class=\"p\">{<\/span><span class=\"nx\">uri<\/span><span class=\"p\">}<\/span><span class=\"err\">`<\/span><span class=\"p\">);<\/span>\r\n\r\n    <span class=\"nx\">request<\/span><span class=\"p\">(<\/span><span class=\"nx\">uri<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"na\">json<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span> <span class=\"p\">},<\/span> <span class=\"p\">(<\/span><span class=\"nx\">err<\/span><span class=\"p\">,<\/span> <span class=\"nx\">resp<\/span><span class=\"p\">,<\/span> <span class=\"nx\">body<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">err<\/span><span class=\"p\">)<\/span> <span class=\"k\">return<\/span> <span class=\"nx\">reject<\/span><span class=\"p\">(<\/span><span class=\"nx\">err<\/span><span class=\"p\">);<\/span>\r\n      <span class=\"nx\">resolve<\/span><span class=\"p\">(<\/span><span class=\"nx\">body<\/span><span class=\"p\">);<\/span>\r\n    <span class=\"p\">});<\/span>\r\n\r\n  <span class=\"p\">});<\/span>\r\n<span class=\"p\">}<\/span>\r\n<\/code><\/pre>\n<\/div>\n<h3 id=\"reloading-dialogs-on-demand\">Reloading Dialogs On Demand<\/h3>\n<p>We modified the <code class=\"highlighter-rouge\">bot-graph-dialog<\/code> extension to support versions of dialogs. Each scenario file can now explicitly define its version. This feature can be used later on to check if a newer version of a scenario was loaded.<\/p>\n<p>When a dialog is updated on the backend, we need to be able to notify the bot service about the change. Since the bot service is just a REST API service, it was easy to add another API call from the backend whenever a dialog was updated and saved. To do this, we call the new <code class=\"highlighter-rouge\">reload<\/code> method which was added to the <code class=\"highlighter-rouge\">GraphDialog<\/code> class for this purpose, which triggers an internal process that:<\/p>\n<ol>\n<li>Fetches the updated dialog by calling the <code class=\"highlighter-rouge\">loadRemoteScenario<\/code> handler<\/li>\n<li>Parses and replaces the existing dialog with the new one<\/li>\n<li>Calculates a dialog version if it wasn\u2019t provided explicitly in the dialog\u2019s JSON<\/li>\n<\/ol>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code>\r\n<span class=\"c1\">\/\/ endpoint for reloading a scenario on demand<\/span>\r\n<span class=\"nx\">app<\/span><span class=\"p\">.<\/span><span class=\"nx\">get<\/span><span class=\"p\">(<\/span><span class=\"s1\">'\/api\/load\/:scenario'<\/span><span class=\"p\">,<\/span> <span class=\"p\">(<\/span><span class=\"nx\">req<\/span><span class=\"p\">,<\/span> <span class=\"nx\">res<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"kd\">var<\/span> <span class=\"nx\">scenario<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">req<\/span><span class=\"p\">.<\/span><span class=\"nx\">params<\/span><span class=\"p\">.<\/span><span class=\"nx\">scenario<\/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\">reloading<\/span> <span class=\"na\">scenario<\/span><span class=\"p\">:<\/span> <span class=\"nx\">$<\/span><span class=\"p\">{<\/span><span class=\"nx\">scenario<\/span><span class=\"p\">}<\/span><span class=\"err\">`<\/span><span class=\"p\">);<\/span>\r\n  <span class=\"kd\">var<\/span> <span class=\"nx\">dialog<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">dialogsMapById<\/span><span class=\"p\">[<\/span><span class=\"nx\">scenario<\/span><span class=\"p\">];<\/span>\r\n  \r\n  <span class=\"k\">return<\/span> <span class=\"nx\">dialog<\/span><span class=\"p\">.<\/span><span class=\"nx\">graphDialog<\/span><span class=\"p\">.<\/span><span class=\"nx\">reload<\/span><span class=\"p\">().<\/span><span class=\"nx\">then<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"kd\">var<\/span> <span class=\"nx\">msg<\/span> <span class=\"o\">=<\/span> <span class=\"err\">`<\/span><span class=\"nx\">scenario<\/span> <span class=\"nx\">id<\/span> <span class=\"s1\">'${scenario}'<\/span> <span class=\"nx\">reloaded<\/span><span class=\"err\">`<\/span><span class=\"p\">;<\/span>\r\n    <span class=\"k\">return<\/span> <span class=\"nx\">res<\/span><span class=\"p\">.<\/span><span class=\"nx\">end<\/span><span class=\"p\">(<\/span><span class=\"nx\">msg<\/span><span class=\"p\">);<\/span>\r\n  <span class=\"p\">})<\/span>\r\n  <span class=\"p\">.<\/span><span class=\"k\">catch<\/span><span class=\"p\">(<\/span><span class=\"nx\">err<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">res<\/span><span class=\"p\">.<\/span><span class=\"nx\">end<\/span><span class=\"p\">(<\/span><span class=\"err\">`<\/span><span class=\"nx\">error<\/span> <span class=\"nx\">loading<\/span> <span class=\"na\">dialog<\/span><span class=\"p\">:<\/span> <span class=\"nx\">$<\/span><span class=\"p\">{<\/span><span class=\"nx\">err<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">}<\/span><span class=\"err\">`<\/span><span class=\"p\">));<\/span>\r\n\r\n<span class=\"p\">});<\/span>\r\n\r\n<\/code><\/pre>\n<\/div>\n<p><em>This solution will only work if there is only one instance of the bot service. In the case where a bot service is scaled out to multiple servers, you\u2019ll need to use some sync mechanism to notify all instances about the change.<\/em><\/p>\n<h3 id=\"identifying-dialog-version-changes\">Identifying Dialog Version Changes<\/h3>\n<p>When a dialog is updated while users are in the middle of it, the state of the session and the dialog data is no longer valid. Our approach was to notify the users that the dialog was updated and that they will need to start over.<\/p>\n<p>To support that notification, we added the option of providing <code class=\"highlighter-rouge\">onBeforeProcessingStep<\/code> and <code class=\"highlighter-rouge\">onAfterProcessingStep<\/code> handlers that will be executed before and after processing each step in the graph. Using these handlers we were able to hook into the dialog\u2019s execution flow and check whether there was a version update before executing each step.<\/p>\n<div class=\"language-js highlighter-rouge\">\n<pre class=\"highlight\"><code><span class=\"c1\">\/\/ Intercepts changes in a scenario version before processing each dialog step<\/span>\r\n<span class=\"c1\">\/\/ If there was a change in the version, restart the dialog<\/span>\r\n<span class=\"kd\">function<\/span> <span class=\"nx\">onBeforeProcessingStep<\/span><span class=\"p\">(<\/span><span class=\"nx\">session<\/span><span class=\"p\">,<\/span> <span class=\"nx\">args<\/span><span class=\"p\">,<\/span> <span class=\"nx\">next<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n\r\n  <span class=\"c1\">\/\/ \"this\" is the GraphDialog instance that invoked this handler<\/span>\r\n\r\n  <span class=\"c1\">\/\/ Gets the current dialog version<\/span>\r\n  <span class=\"kd\">var<\/span> <span class=\"nx\">dialogVersion<\/span> <span class=\"o\">=<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">getDialogVersion<\/span><span class=\"p\">();<\/span>\r\n\r\n  <span class=\"c1\">\/\/ If we didn't keep the dialog version, store it in the privateConversationData<\/span>\r\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">session<\/span><span class=\"p\">.<\/span><span class=\"nx\">privateConversationData<\/span><span class=\"p\">.<\/span><span class=\"nx\">_dialogVersion<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nx\">session<\/span><span class=\"p\">.<\/span><span class=\"nx\">privateConversationData<\/span><span class=\"p\">.<\/span><span class=\"nx\">_dialogVersion<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">dialogVersion<\/span><span class=\"p\">;<\/span>\r\n  <span class=\"p\">}<\/span>\r\n\r\n  <span class=\"c1\">\/\/ If the version we stored in the privateConversationData is different than the current one-<\/span>\r\n  <span class=\"c1\">\/\/ we have a dialog update event. Restart the dialog.<\/span>\r\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">session<\/span><span class=\"p\">.<\/span><span class=\"nx\">privateConversationData<\/span><span class=\"p\">.<\/span><span class=\"nx\">_dialogVersion<\/span> <span class=\"o\">!==<\/span> <span class=\"nx\">dialogVersion<\/span><span class=\"p\">)<\/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=\"s2\">\"Dialog updated. We'll have to start over.\"<\/span><span class=\"p\">);<\/span>\r\n    <span class=\"k\">return<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">restartDialog<\/span><span class=\"p\">(<\/span><span class=\"nx\">session<\/span><span class=\"p\">);<\/span>\r\n  <span class=\"p\">}<\/span>\r\n\r\n  <span class=\"k\">return<\/span> <span class=\"nx\">next<\/span><span class=\"p\">();<\/span>\r\n<span class=\"p\">}<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p><em>If the <code class=\"highlighter-rouge\">version<\/code> field in the root of the dialog object is specified, this will be considered the scenario version. If the version is not specified, the bot will assign it a version by hashing the content of the JSON file.<\/em><\/p>\n<h2 id=\"the-code\">The Code<\/h2>\n<p>Please refer to the <a href=\"https:\/\/github.com\/CatalystCode\/bot-graph-dialog\">bot-graph-dialog<\/a> extension that was used to support this solution, as well as to the <a href=\"https:\/\/github.com\/CatalystCode\/bot-trees\">bot-trees<\/a> project that can be used as a reference for how to use this extension. The specific reference for this case study is the <code class=\"highlighter-rouge\">loadOnDemand.js<\/code> file.<\/p>\n<h2 id=\"opportunities-for-reuse\">Opportunities for Reuse<\/h2>\n<p>This code story demonstrates how to integrate an already existing bot platform with Microsoft\u2019s Bot Framework to easily expose the bot\u2019s dialogs on the various channels that the Bot Framework supports. This approach can be used as a reference to create solutions for similar scenarios. Also, scenarios that require the loading of bot dialogs from an external data source like files, databases or remote APIs can leverage the <code class=\"highlighter-rouge\">bot-graph-dialog<\/code> extension by using similar methods.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How to expose dialogs on multiple supported channels easily by integrating Microsoft Bot Framework with an existing bot platform.<\/p>\n","protected":false},"author":21349,"featured_media":12600,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[13],"tags":[249,331],"class_list":["post-2122","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bots","tag-microsoft-bot-framework-mbf","tag-smarkio"],"acf":[],"blog_post_summary":"<p>How to expose dialogs on multiple supported channels easily by integrating Microsoft Bot Framework with an existing bot platform.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2122","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=2122"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/2122\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/12600"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=2122"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=2122"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=2122"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}