What do you get when you combine Azure Static Web Sites, LUIS, SharePoint Online, Logic Apps, and Microsoft Teams?

Posted by: Mark Rackley on February 19, 2019

Sometimes you just have to punt on the title of your blog post and let SEO do the heavy lifting.

A few months ago, I presented a session at Microsoft Ignite on creating a QnA bot using the QnA Maker. I also blogged about it and created a video walking you through the mostly painless process:

It’s Time to Modernize Your Company FAQ with a No-Code Bot in Microsoft Teams

Now, this solution works great, and ANYONE can do it. However, as some have pointed out, it’s not free… and could end up costing you a few thousand dollars a year for the service based on usage.

But it’s SO easy to build!

Since then, I’ve been looking at other technologies and options for creating the same functionality but without having to spend the money… and with a little help from Rob Windsor the Microsoft Teams Hillbilly Bot was born… really… I can’t think for a better name for it. I tried several, the best I could come up with was “The almost-free, low-code QnA Bot That You Can Use in Teams” but that just doesn’t have a ring to it…  

Also, I haven’t posted anything hillbilly related in a while… so, you’re welcome.

This QnA Bot will actually work on ANY web site you have… The interface for the bot is just HTML and JavaScript and no authentication is required

One of the things I like about this solution from a strictly educational standpoint is the number of technologies it uses. This is a great way to broaden your skillset in a very non-overwhelming manner. The technologies we will take advantage of in this blog post are:

  • Azure Static Web Sites
  • LUIS – Language Understanding
  • SharePoint Online
  • Logic Apps (you could also use Flow)
  • Microsoft Teams

I know, that sounds like a lot of moving pieces (and it is) but they all work in such a strangely easy harmony you’ll wonder why you didn’t get started on these technologies sooner.

So, let’s just jump right in, I don’t want this to become one of those recipe sites that talk for pages and pages about the history of the kidney bean and why it reminds you of that time in Tuscany instead of just giving you the ingredients and telling you how to make it… seriously… stop it recipe people… you know who you are…  

Step 1. Azure Static Web Sites

First, we are going to create a web site to host our chat bot. You could totally skip this step if you wanted to and just host the HTML and JavaScript we are about to write in a Script Editor web part or in an existing web site you already have if you want to. However, it’s soooo simple to create a free static web site in Azure, that this is the perfect opportunity to do just that.

Video 1/4 – Creating Azure Static Web Site

 

Step 2. LUIS – Language Understanding

LUIS is “a machine learning-based service to build natural language into apps, bots, and IoT devices. Quickly create enterprise-ready, custom models that continuously improve.” Basically, LUIS is the heart of your bot. Using LUIS you can configure questions users might answer to determine their intent. So, if a user asks “How do I request time off?” LUIS might be able to determine the intent was “VacationRequest”.  The more utterances you configure LUIS with, the better probably of LUIS identifying the user’s intent. Part of the response from LUIS is a “score” that tells you the probability that LUIS guessed the user’s intent. You can use this score to help refine LUIS and continually improve the experience for users. You can also set up Phrases and Patterns in LUIS to further improve responses. We are really just scratching the surface with LUIS here in this post.  

As far as pricing goes, LUIS is free for 5 transactions per second (text only). 

LUIS provides an Endpoint API that we can easily call with JavaScript. So, we’ll be writing some JavaScript that passes the user’s question to LUIS and look at the response LUIS sends back. Check out the references at the end of the blog post for more information about the LUIS Endpoint API.

Video 2/4 an Introduction to LUIS (Language Understanding)

As a final side note, instead of calling LUIS from JavaScript you could also use the LUIS action currently in preview in Logic Apps and Flow as part of Step 4.

Step 3. SharePoint Online

We’ll use a list in SharePoint online to store the responses from our Chat bot using the intent keyword returned by LUIS. This will allow people without Azure accounts to maintain the responses of the bot and allow for easily creating rich text formatted responses.

Step 4. Logic Apps

Now we need to update our script to query the SharePoint list for the correct response based upon the intent keyword. We are going to use a Logic App for this. Using a Logic App we can create an HTTP Request trigger and connect to our SharePoint List and return the results to our script without having to authenticate to SharePoint.

You could do the same thing with Flow, but with the recently announced licensing change, the HTTP request trigger will soon be a premium feature. If you still want to use Flow, you can adapt the blog post I did on that:

Using Microsoft Flow to Create a Web Service

There are other ways to query the SharePoint list as well especially if you are in the context of SharePoint, but again, this is a great way to get acquainted with Logic Apps if you are brand new to them.

Video 3/4 Reading a SharePoint List with a Logic App (yes you can do the same thing with Flow)

One final note about Logic Apps, there is a usage fee associated with them. This fee is based on number of actions and which connectors you use. Based on the pricing found here I’ve estimated this Logic App will cost about 17.5 cents for every 10,000 executions.

Step 5. Microsoft Teams

At this point, the QnA bot is functional and you could use it on any website you want. Who knows, maybe I’ll even deploy it to my WordPress site someday… eh.. probably not.. those darn pesky Russian hackers would undoubtedly run up my Azure bill just for the fun of it…  But let’s take things a step further and deploy our bot as an application in Teams. Now we’ll have access to the Teams context and it will give us a good starting point for adding even more functionality to our Chat bot in the future… we can even use the Teams context to determine if we are in Teams or not and take appropriate actions.

Video 4/4 Deploying Hillbilly Chat Bot as Teams Application

What’s Next?

So, halfway writing this blog post it hit… Why don’t I remove some of the complexities of this solution and any “perceived” security concerns with the anonymous URL’s and develop the whole thing with LUIS, PowerApps, SharePoint, and Flow? I mean… might as well try to touch EVERY piece of Office 365… right?  Well… let’s do that then… but how about me move it to another blog post:

Creating a QnA Chat Bot using LUIS, PowerApps, SharePoint, And Flow

References

Static website hosting for Azure Storage now in public preview

https://azure.microsoft.com/en-us/blog/azure-storage-static-web-hosting-public-preview/

LUIS – Language Understanding

https://www.luis.ai/home

LUIS Endpoint API

https://westus.dev.cognitive.microsoft.com/docs/services/5819c76f40a6350ce09de1ac/operations/5819c77140a63516d81aee78

Cognitive Services pricing—Language Understanding (LUIS)

https://azure.microsoft.com/en-us/pricing/details/cognitive-services/language-understanding-intelligent-services/

Quickstart: Create your first automated workflow with Azure Logic Apps – Azure portal

https://docs.microsoft.com/en-us/azure/logic-apps/quickstart-create-first-logic-app-workflow

Logic Apps pricing

https://azure.microsoft.com/en-us/pricing/details/logic-apps/

Microsoft Teams JavaScript client SDK

https://docs.microsoft.com/en-us/javascript/api/overview/msteams-client?view=msteams-client-js-latest

Source Code Used for Hillbilly Chat

<!DOCTYPE html><html><head> 
    <title>Hillbilly Bot</title> 
<head>
    <script type="text/javascript" src="//code.jquery.com/jquery-1.11.1.min.js"></script> 

        <!-- Microsoft Teams JavaScript API (via CDN) -->
        <script src="https://unpkg.com/@microsoft/teams-js@1.3.7/dist/MicrosoftTeams.min.js" integrity="sha384-glExfvkpce98dO2oN+diZ/Luv/5qrZJiOvWCeR8ng/ZxlhpvBgHKeVFRURrh+NEC" crossorigin="anonymous"></script>

    <style type="text/css">
        .question
        {
            border-radius: 15px;
            background: darkblue;
            color: white;
            padding: 10px; 
            width: 400px;
            margin: 5px;
        }
        .response
        {
            border-radius: 15px;
            background: lightgray;
            color: darkblue;
            padding: 10px; 
            width: 400px;
            margin: 5px;
            overflow: auto;
        }
        .message
        {
            float: left;
            width: 350px;
        }
        .hillbilly
        {
            float: right;
            bottom: 0;
            display: inline;
        }
    </style>
</head>
<body>
    
    <h3>Watcha need?  <input type="text" id="QueryText"></h3>
    <br>
    <div id="response"></div>

    <script type="text/javascript">
    
        "use strict";

        var _microsoftTeamsContext;

        microsoftTeams.initialize();
        microsoftTeams.getContext(function (context)
        {
            _microsoftTeamsContext = context;
        });


        jQuery(function () {
            //attach to the keydown even for the query text
            //and query LUIS when the enter key  is pressed
            $('#QueryText').on('keydown', function(e) {
                if (e.which == 13) {
                    QueryTextEnter();
                }
            });

        });
    
    //Enter key pressed on query
    //make the call to LUIS using the query text to determine intent
    function QueryTextEnter() {      
        var queryText = jQuery("#QueryText").val();

        //if we aren't in teams, return
        if (!_microsoftTeamsContext)
        {
            PostResponse("<h1>The use of this bot is restricted to Teams.</h1>");
            return;
        }

        PostQuestion(queryText);

         //web serivce call to LUIS passying the 
        //query as a query string parameter
        var call = jQuery.ajax({
            url: "<LUIS End Point>&q="+queryText,
            type: "GET",
            dataType: "json",
            headers: {
                Accept: "application/json",
                "Content-Type": "application/json"
            }
        });
        call.done(function (data, textStatus, jqXHR) {
            console.log(data);
            //get the intent and the score that LUIS thinks
            //is the top scoring intent
            var intent = data.topScoringIntent.intent;
            var faqGet = GetFAQ(intent);
            faqGet.done(function (response) {
                PostResponse(response);
            });           
        });
        call.fail(function (jqXHR, textStatus, errorThrown) {
            alert("Fail");
        });
        
    }

    function PostQuestion(question)
    {
        $("#response").prepend("<div class='question'>"+question+"</div>")
    }

    function PostResponse(response)
    {
        $("#response").prepend("<div class='response'><div class='message'>"+response+"</div><div class='hillbilly'><img height='50' src='https://www.markrackley.net/wp-content/uploads/2019/02/hillbilly.png'></div></div>")
    }

    //execute the Logic App that queries the SharePoint List
    //to get the response text based on the intent keyword
    //return from LUIS
    function GetFAQ(keyword)
    {
                    var defer = $.Deferred();
            var call = jQuery.ajax({
                url: "<Logic App End Point>",
                type: "POST",
                data: JSON.stringify({
                    "intent": keyword,
                }),
                headers: {
                    "Content-Type": "application/json"
                }
            });
            call.done(function (data, textStatus, jqXHR) {
                console.log(data);
                defer.resolve(data.value[0].FAQ);
            });
            call.fail(function (jqXHR, textStatus, errorThrown) {
                alert("Cannot retrieve response. Please try again.");
                defer.resolve();
            });
        return defer.promise();
    }
    </script>

</body></html>

Topics: SharePoint Online, Microsoft Teams, Bots, azure, LUIS

    Recent Posts

    Categories