DotNetCurry Logo

Using Microsoft Cognitive Services to create a Facebook and Skype Bot

Posted by: Shoban Kumar , on 8/12/2016, in Category Windows Store Apps
Views: 8820
Abstract: The Microsoft Bot Framework and Cognitive Services allow us to develop intelligent Facebook and Skype bots. We will create a simple bot in this article.

2016 seems to be the year of bots and the race is getting more interesting. After the Microsoft Bot Framework, the latest entrant is Facebook’s Messenger platform. This opens up a lot of opportunities for developers. It’s much easier to build a complete intelligent Bot with very little effort by making use of all these services.

This article is published from the DNC Magazine for Developers and Architects. Download this magazine from here [Zip PDF] or Subscribe to this magazine for FREE and download all previous and current editions

Here is an idea: An Intelligent Facebook bot that acts as the first point of customer support for a Facebook Page (owned by Brands, Stores etc). You can use LUIS (Language Understanding Intelligent Service) by Microsoft Cognitive Services and build a bot using Bot Framework and connect to Facebook Messenger and Facebook Page.

In my previous article Simple Intelligent Bot using Microsoft Bot Framework & Cognitive Services, I showed you how quickly you can build a Bot using Microsoft Bot Framework and make it smart using Cognitive Services. In this article, we will build a better bot and connect it to Facebook and Skype. Let’s get started!

Knowledge Guru Bot using Microsoft Cognitive Services

Meet Knowledge Guru, a simple Intelligent Bot that can be used to retrieve rich Academic Knowledge. You can ask Knowledge Guru about any Academic topic and retrieve papers published by various Authors and Affiliations within seconds. This dataset is much cleaner than a normal Google search so our Bot is very useful for Students, Researchers and anyone interested in learning.

Our bot will talk to Microsoft Cognitive Services and specifically Academic Knowledge API to retrieve rich academic knowledge as shown below.

microsoft-cognitive-bot

Create a new Bot Application in Visual Studio 2015. Download and install the Bot Application template. Save the zip file to your Visual Studio 2015 templates directory which is traditionally in “%USERPROFILE%\Documents\Visual Studio 2015\Templates\ProjectTemplates\Visual C#". Now open Visual Studio and you will find the Bot template.

bot-project

Add a new class file named Conversation.cs. Replace the class name in the generated file with the following code:

[Serializable]
public class ConversationDialog : IDialog 

Add the Microsoft.Bot.builder Nuget package to the project. The Bot Builder framework helps us build more guided conversations. Conversation with Knowledge Guru is a two-step process. One for searching (Interpret) based on the search text, and another one for fetching (Evaluate) publications, links, etc., based on the selected search result. The Bot builder framework makes it easy to build this guided conversation.

public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<Message> argument)
{
    var message = await argument;
    string name = string.Empty;
    name = message.GetBotPerUserInConversationData<string>("name");
    if (string.IsNullOrEmpty(name))
    {
        PromptDialog.Text(context, AfterResetAsync, "Hi! What is your name?");
    }
    else
    {
        bool welcome = false;
        context.PerUserInConversationData.TryGetValue<bool>("welcome", out welcome);

        if (!welcome)
        {
            await context.PostAsync($"Welcome back {name}");
            context.PerUserInConversationData.SetValue<bool>("welcome", true);
            context.Wait(MessageReceivedAsync);
        }
        else if ((message.Text.ToLower().Equals("hi")) || (message.Text.ToLower().Equals("hello")))
        {
            await context.PostAsync("Hello again!");
            context.Wait(MessageReceivedAsync);
        }
        else
        {
            //Interpret query
            AcademicResult resp = await Utilities.Interpret(message.Text.ToLower());
            if (resp.interpretations.Count == 0)
            {
                resp = await Utilities.Interpret(message.Text.ToLower());
            }

            //TODO: check resp
            XmlDocument doc = new XmlDocument();
            string xPath = "rule/attr";
            string responseMessage = string.Empty;
            string parse = string.Empty;

            if (resp.interpretations.Count == 0)
            {
                await context.PostAsync("Sorry i couldn't find anything. Please try again.");
                context.Wait(MessageReceivedAsync);
            }
            else
            {
                context.PerUserInConversationData.SetValue<AcademicResult>("result", resp);
                int counter = 1;
                List<int> options = new List<int>();

                //Get proper text for each interpretations
                foreach (var interp in resp.interpretations)
                {
                    //Add to options
                    options.Add(counter);

                    doc.LoadXml(interp.parse);
                    var nodes = doc.SelectNodes(xPath);
                    parse = string.Empty;
                    List<Item> attributes = new List<Item>();


                    foreach (XmlNode node in nodes)
                    {
                        if (node.Attributes.Count == 2)
                        {
                            if (node.Attributes[1].Name == "canonical")
                            {
                                attributes.Add(new Item { Attribute = node.Attributes[0].Value, Value = node.Attributes[1].Value });
                            }
                            else
                            {
                                attributes.Add(new Item { Attribute = node.Attributes[0].Value, Value = node.Attributes[1].Value });
                            }
                        }
                        else
                        {
                            attributes.Add(new Item { Attribute = node.Attributes[0].Value, Value = node.InnerText });
                        }
                    }

                    parse = "Papers " + ProcessAttribute(attributes);
                    responseMessage += $"- {counter} : {parse} \r\n";
                    counter += 1;

                }

                options.Add(counter);
                responseMessage += $"- {counter} : Search something else \r\n";

                //Post reply
                responseMessage = "Here is what I found. Simply reply with the number of your choice \r\n" + responseMessage;
                PromptDialog.Number(context, AfterChosenAsync, responseMessage,"Sorry! I did not understand. Please choose from above options.");
            }
        }
    }
}

Dialogs are an easy way to present the user with a set of options (just like how Interactive voice response IVR works) and accept only those options as response messages. This approach makes error handling easy. In the above code, after the initial greeting is over, the user is presented with the set of options. These options are nothing but search results from Cognitive Services. More detailed results are fetched based on the response from this Dialog. Here are some additional details:

1. In lines 5 and 6, we check if “name” is null or empty and if it is, prompt a question asking for the name (line 8). This variable is null when the conversation starts. AfterResetAsync method is called when the user replies with his/her name. This method saves this value in the “name” variable.

2. If “name” variable has a value, then we check the “welcome” variable to greet returning user and new user differently (lines 13-25).

3. We make a request to Academic Knowledge API to get a list of interpretations using Utilities.Interpret method (line 29). This method makes an HTTP web request to Interpret REST Api in Academic Knowledge API. The response is then converted to a custom object AcademicResult.

4. The result from above request is saved in conversation variable named “result”. This is later used to respond with more detailed answers (line 48).

5. Based on the number of interpretations, we build a menu with an extra option to “search something else” to start a new search (lines 50-93).

6. Prompt a new dialog with a menu using PromptDialog.Number which will accept only numbers as inputs from user.

Add the following method to Conversation.cs file:

private string ProcessAttribute(List<Item> attributes)
{
    var atts = attributes.GroupBy(g => g.Attribute).Select(grp => grp.ToList()).ToList(); ; //  .OrderBy(x => x.Attribute).ToList<Item>();
    string attributeTitle = string.Empty;
    string Title = string.Empty;

    foreach (var item in atts)
    {
        string attributeName = item[0].Attribute;

        switch (attributeName)
        {
            case "academic#F.FN":
                Title = "in field ";
                break;
            case "academic#Ti":
                Title = "with title ";
                break;
            case "academic#Y":
                Title = "in year ";
                break;
            case "academic#D":
                Title = "date ";
                break;
            case "academic#CC":
                Title = "with citation count ";
                break;
            case "academic#AA.AuN":
                Title = "by author ";
                break;
            case "academic#AA.AuId":
                Title = "with author id ";
                break;
            case "academic#AA.AfN":
                Title = "with author affiliation ";
                break;
            case "academic#AA.AfId":
                Title = "with affiliation id ";
                break;
            case "academic#F.FId":
                Title = "with field id ";
                break;
            case "academic#J.JN":
                Title = "with journal name ";
                break;
            case "academic#J.Id":
                Title = "with journal id ";
                break;
            case "academic#C.CN":
                Title = "with conference name ";
                break;
            case "academic#C.Id":
                Title = "with conference id ";
                break;
            case "academic#RId":
                Title = "with reference id ";
                break;
            case "academic#W":
                Title = "with words ";
                break;
            case "academic#E":
                Title = "";
                break;
            default:
                Title = "";
                break;
        }

        attributeTitle += Title;

        int counter = 0;
        foreach (var i in item)
        {
            if (counter == 0)
            {
                attributeTitle += $"**{i.Value}** ";
            }
            else if (counter == item.Count - 1)
            {
                attributeTitle += $"and **{i.Value}** ";
            }
            else
            {
                attributeTitle += $",**{i.Value}** ";
            }
            counter++;

        }
    }

    return attributeTitle;

}

ProcessAttribute is a helper method which translates attributes, in response from Academic Knowledge API, to words. Cognitive Services API gives us the ability to use complex queries, using Entity Attributes, to search for publications. This method translates an Entity Attribute to an English word. For example, the Attribute Ti is translated to Title. See Entity Attributes documentation page for the complete list of Attributes and their meaning.

Add the following code to Conversation.cs:

public async Task AfterResetAsync(IDialogContext context, IAwaitable<string> argument)
{
    var name = await argument;
    context.PerUserInConversationData.SetValue<string>("name", name);
    context.PerUserInConversationData.SetValue<bool>("welcome", true);
    await context.PostAsync($"Hi! {name}, I am Academic Knowledge guru. You can simply type in the title to search or search by Authors by replying with \"papers by AUTHOR NAME\".");
    context.Wait(MessageReceivedAsync);
}

The above method saves the name of the user and responds with a welcome message.

Add the following method to Conversation.cs:

public async Task AfterChosenAsync(IDialogContext context, IAwaitable<long> argument)
{
    var choice = await argument;
    AcademicResult result = new AcademicResult();
    context.PerUserInConversationData.TryGetValue<AcademicResult>("result", out result);
    string responseMessage = string.Empty;
    string linkMsg = string.Empty;

    if (result != null)
    {
        if (choice > result.interpretations.Count())
        {
            await context.PostAsync("Okay! ask me.");
        }
        else
        {
            EvaluateResult resp = await Utilities.Evaluate(result.interpretations[Convert.ToInt16(choice) - 1].rules[0].output.value);
            int counter = 1;
            foreach (var en in resp.entities)
            {
                EX ex = JsonConvert.DeserializeObject<EX>(en.E);
                linkMsg = string.Empty;
                foreach (var link in ex.S)
                {
                    switch (link.Ty)
                    {
                        case 1:
                            linkMsg += $" [HTML]({link.U})";
                            break;
                        case 2:
                            linkMsg += $" [TEXT]({link.U})";
                            break;
                        case 3:
                            linkMsg += $" [PDF]({link.U})";
                            break;
                        case 4:
                            linkMsg += $" [DOC]({link.U})";
                            break;
                        case 5:
                            linkMsg += $" [PPT]({link.U})";
                            break;
                        case 6:
                            linkMsg += $" [XLS]({link.U})";
                            break;
                        case 7:
                            linkMsg += $" [PS]({link.U})";
                            break;
                        default:
                            linkMsg += $" [LINK]({link.U})";
                            break;
                    }

                }

                responseMessage += $"- {counter} . {ex.DN} {linkMsg} \r\n";
                counter++;
            }

            await context.PostAsync(responseMessage);
        }
    }

    context.Wait(MessageReceivedAsync);
}

The above method gets called when the user responds with a paper number of his choice. The result object stored in the conversation variable “result” is used to retrieve the selected interpretation and Utilities.Evaluate is used to retrieve more details using the Evaluate Method . Results are then parsed and responded with links.

Replace the auto-generated Post method in MessageController.cs file with the following code:

if (message.Type == "Message")
{
    try
    {
        return await Conversation.SendAsync(message, () => new ConversationDialog());
    }
    catch (Exception ex)
    {
        return message.CreateReplyMessage("Sorry! Something went wrong.");
    }
}
else
{
    return HandleSystemMessage(message);
}  

In the above code, we route every message in the conversation to the custom conversation Dialog.

Add the Utilities.cs class file attached with the source code to your project. This file has methods to make requests to Interpret and Evaluate API from Academic Knowledge API. This file also contains classes that are used to parse the response easily. Make sure you get a new subscription key by visiting https://www.microsoft.com/cognitive-services/en-US/subscriptions and subscribing to “Academic Preview” service.

Bot Framework Emulator

The Bot Framework Emulator lets you test your bot without publishing online. You can also use this tool to debug the responses that are sent in JSON format. Get a new App Id and Secret by visiting https://dev.botframework.com/bots/new and registering a new Bot. As of now, you can use the Bot in the Emulator without registration.

msft-bot-framework-emulator

The Bot Framework Emulator is good for development testing but I suggest using Azure for a better quality testing. I found that the Emulator is buggy and Message formats were not proper sometimes.

Publish your project to a new Azure App service by right clicking and selecting Publish. Here’s a link to the process in case you are not familiar with it http://bit.ly/dncm-publish-azure

Register your Bot

Visit https://dev.botframework.com/bots/new to edit the Bot and set the Endpoint address to your newly created Azure website in the format https://{YOUR WEBSITE }/api/messages

Make sure you update the App Id and Secret in Web.config before publishing your project to Azure website.

Connect to Skype and Facebook

Connecting your Bot to Skype and Facebook requires a developer account in each of these websites. Follow the steps by clicking “Add” in the channel list on your Bot home page.

facebook-skype-bot

Here is a screenshot of how the Skype Bot works

skype-bot

Here is how the bot looks with Facebook Messenger

facebook-bot

As you can see, Facebook breaks one message into different messages and formatting is not correct. Bot framework is still in Preview and eventually, Microsoft will fix these issues. As a work around, you can check this Channel source and respond with different messages for different channels.

Conclusion

The Microsoft Bot Framework and Cognitive Services makes developing intelligent bots as well as connecting different platforms, a breeze. But it is still in Preview and not fully matured. Microsoft is constantly adding more features to it and improving it. In this article, we used use LUIS (Language Understanding Intelligent Service) by Microsoft Cognitive Services and built a bot using Bot Framework and connected to Facebook Messenger and Facebook Page . Explore other Cognitive Services like Vision API, OCR, Face API, etc., to build more intelligent and useful bots.

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
ShobanKumar is an ex-Microsoft MVP in SharePoint who currently works as a SharePoint Consultant. You can read more about his projects at http://shobankumar.com. You can also follow him on twitter @shobankr


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!