Related Blogs
Bots!!! By hearing this word, visualization of some sci-fi Hollywood films come to our mind like an intensive artificial intelligence system that operates independently, computer stealing mankind’s job, humans becoming pets of computers, etcetera etcetera. Calm down! Bots are nothing like that. As computers grow in complexity, the ways to interact with computers also change. We used to interact using printouts and punch cards in the past. It was such a mess and tedious to work with computers. Then some genius guys had invented CRT screens attached to the computer, and the era of terminal-based systems arrived. A user could write commands on computers and could see executions on screens. A user had to remember all the commands to work with computers. After that, some menu-based user interface with complex programming was invented to provide more ease while working with computers. Users can easily select an item from the menu using mouse inputs, and he would be redirected to a new page with new menu items. Bots are the new evolution in interaction with computers.
What is a Bot?
A bot is an app using which you can interact with systems. You can give voice commands to mobile devices to call someone or to start typing mail. People are already using bots in their daily lives without their notice. For those users who are using Windows 10, Cortana is a very familiar bot. Siri and Google Assistant are the most popular bots for mobile users. Alexa – Amazon will also amaze you with its capabilities. These apps help you to perform some tasks in daily life without typing on your devices or even without touching. This is called a new evolution in interaction with devices. It can be configured in a way that it recognizes your input patterns and suggests results that match your inputs. Bots are used in various industries to increase productivity and ease human life.
Microsoft Bot Framework
Components of a conversational AI experience
Microsoft has introduced a comprehensive framework for building enterprise-grade conversational AI experience called the Microsoft Bot framework. Azure Bot Services and Cognitive Services enable you to build intelligent bots with control over data. Comprehensive open-source SDK and tools are provided to easily connect your bot to popular channels and devices. With the native integration of Azure Cognitive Services, give your bot the ability to read, listen, speak, and understand your users. These Bots can be a tailored conversational assistant to your brand, personalized to your users, and available across a broad range of devices. Microsoft’s open-sourced approach toward the Bot Framework SDK will provide a Virtual Assistant solution along with a set of fundamental core capabilities and full control over user experience. This solution simplifies the creation of a Bot, enabling you to get started in minutes and extend with end-to-end development tooling.
Azure Bot Services provide services to create and customize Bots in Microsoft Azure. These Bots are designed to take input from users and publish it to various Channels like MS Teams, Skype, Facebook, Line, etc.
Azure Cognitive Services infuse bots with intelligent algorithms to speak, hear, interpret, and understand user needs through natural methods of communication. These services provide such intelligence to Bots that provide various recommendations and enhance decision-making, convert speech to text and text to natural-sounding speech, translate speech in other languages, process natural language with pre-built scripts, and recognize user sentiments & needs.
Let’s create a Bot in MS Teams that gets task titles assigned to users from the SharePoint Online task list when the question “What are my tasks?” is raised by that user.
You will need below apps/tools to get started:
- MS Azure Subscription
- Microsoft Teams
- Visual Studio
Let’s first start by creating a Bot in Microsoft Azure.
Create a Bot in the Microsoft Azure Portal
- Open the Azure portal.
- Click “All services” > click “AI + machine learning.”
- Click “Bot Services”.
- Click “+ Add” to create a new bot in the Azure portal.
- Select “Web App Bot”.
- Click “Create”.
- Provide proper bot name, select proper Subscription, Resource group, Location, Pricing tier (F0 is free), App name.
- Select “Basic Bot (C#)” in Bot template and click “Create”.
- Once the Bot creation process is completed, you can see all bots under Bot Services.
- Click on the newly created Bot.
- Click the “Test in Web Chat” option from the left bar. It will open a testing chat bar for the bot.
- Write “Hi” in the message bar and send it. In return, you will receive the “Hello” message.
- Your bot is now created and is working properly if you get an answer from him.
Configure static question answers for a Bot
- Open URL Qnamaker.ai
- Login with the same credentials used for Azure Portal.
- Click on “Create a knowledge base” from the menu.
- Click on “Create a QnA service” under Step 1 to create Cognitive Service in Azure Portal.
- Provide proper name, select Subscription, Pricing tier, Location, Resource group, and click on the “Create” button.
- It will create a new cognitive service in the Azure Portal.
- Go to the previous tab (on the QnA Maker page in the browser)
- Refresh the page.
- In Step 2, select proper Microsoft Azure Directory ID, Azure Subscription name, Azure QnA service (created earlier), provide the name of your KB, and click on “Create your KB” under Step 5.
- Open your knowledge base from “My Knowledge bases” if it’s not opened.
- Here, you can set your static question answers or edit existing question answers.
- Once you complete editing, click on “Save and train” from the menu.
- Click “Publish” to publish all these changes for your bot.
- You can check your new question answers in “Test in Web Chat” on the Azure Portal site.
Let’s head towards loading dynamic values in bot’s answers.
Customize a Bot to get tasks from the SharePoint task list for the current user
The SharePoint developers should follow these basic steps for customizing a Bot when they want to create a WCF service to fetch items from the SharePoint site’s list and consume it in Bot to display answers in response to questions asked by a logged-in user in MS Teams.
Create a new task list on the SharePoint Online site
- Open your SharePoint site, create a new task list on site.
- Open the newly created task list > go to List settings.
- Click on the “Assigned To” column to edit it.
- In the “Show field” dropdown control, select Work email.
Note: We will filter tasks by getting a logged-in user’s email id from MS Teams.
Create a WCF service to get tasks from the SharePoint task list
- Create a new web application in Visual Studio and publish it in the Azure Portal. Note: Detailed steps will be provided here in Microsoft official documentation.
- Open SharePoint Online site to register a new app for authentication purposes.
- Open https://<site_URL>/_layouts/15/appregnew.aspx URL in a browser.
- Generate Client ID and Secret using your web application URL. Note down Client ID and Secret for further use.
- From Microsoft Teams, we will get the Azure User ID of a logged-in user. Note: Azure User ID is not used in the SharePoint Online site, so we are using MS Graph API to get email id from Azure User ID. And using the email ID of the user, we will filter tasks in a list.
Here, below is the code from the code file from WCF service. Note: Tenant ID, Client ID, Client Secret, and user credentials are used here for illustration purposes. You can get it from web.config at your ease.
/// <summary> /// Get data from SharePoint list according to provided user IDs. /// </summary> /// <param name="userAzureId">Azure ID of user /// <returns>SharePoint list items</returns> public string GetData(string userAzureId) { var tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; var clientId1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; var clientSecret1 = "XXXXXXX?XXXXXXXXXXXXXXXXX:XXXX.-"; // Configure app builder var authority1 = $"https://login.microsoftonline.com/" + tenantId; var app = ConfidentialClientApplicationBuilder .Create(clientId1) .WithClientSecret(clientSecret1) .WithAuthority(new Uri(authority1)) .Build(); // Acquire tokens for Graph API var scopes1 = new[] { "https://graph.microsoft.com/.default" }; var authenticationResult = app.AcquireTokenForClient(scopes1).ExecuteAsync(); // Create GraphClient and attach auth header to all request (acquired on previous step) var graphClient1 = new GraphServiceClient( new DelegateAuthenticationProvider(requestMessage => { requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authenticationResult.Result.AccessToken); return Task.FromResult(0); })); var ids = new List<string>() { userAzureId }; var types = new List<string>() { "user" }; var idAwait = OnMessageActivityAsync(graphClient1, ids, types); idAwait.Wait(); return idAwait.Result; } /// <summary> /// Call method to get data from SharePoint site. /// </summary> /// <param name="graphClient1">Graph service client object /// <param name="ids">List of user ids /// <param name="types">List of types /// <returns></returns> public static async Task<string> OnMessageActivityAsync(GraphServiceClient graphClient1, List<string> ids, List<string> types) { var idUsers = await graphClient1.DirectoryObjects .GetByIds(ids, types) .Request() .PostAsync(); var userEmail = ((Microsoft.Graph.User)idUsers.First()).Mail; var spcall = SharePointCall(userEmail); spcall.Wait(); return spcall.Result; } /// <summary> /// Get data from SharePoint Online site list. /// </summary> /// <param name="userEmail">Logged-in user's email id /// <returns>List of tasks.</returns> public static async Task<string> SharePointCall(string userEmail) { string strCol = string.Empty; string strURL = "https://tenant-name/sites/sharepoint"; /// SharePoint Online Site URL string userName = "[email protected]"; /// Office 365 User Account string password = "Password"; var securePassword = new SecureString(); foreach (char c in password) { securePassword.AppendChar(c); } securePassword.MakeReadOnly(); try { SecureString PW = securePassword; string strResult = string.Empty; using (var vContext = new ClientContext(strURL)) { vContext.AuthenticationMode = ClientAuthenticationMode.Default; vContext.Credentials = new SharePointOnlineCredentials(userName, PW); Microsoft.SharePoint.Client.List targetList = vContext.Web.Lists.GetByTitle("BotTasks"); CamlQuery camlQuery = new CamlQuery { ViewXml = @"<view><query><where><and><eq><fieldref name="AssignedTo"><value type="Text">" + userEmail + "</value></fieldref></eq><or><eq><fieldref name="Status"><value type="Choice">In Progress</value></fieldref></eq><eq><fieldref name="Status"><value type="Choice">Not Started</value></fieldref></eq></or></and></where></query></view>" }; var targetListItem = targetList.GetItems(camlQuery); vContext.Load(targetList); vContext.ExecuteQuery(); vContext.Load(targetListItem); vContext.ExecuteQuery(); foreach (var item in targetListItem) { strCol += item.FieldValues["Title"].ToString() + ", "; } strCol = strCol.TrimEnd(' '); strCol = strCol.TrimEnd(','); } } catch (Exception ex) { throw; } return strCol; } // Configure app builder var authority1 = $”https://login.microsoftonline.com/” + tenantId; var app = ConfidentialClientApplicationBuilder .Create(clientId1) .WithClientSecret(clientSecret1) .WithAuthority(new Uri(authority1)) .Build(); // Acquire tokens for Graph API var scopes1 = new[] { “https://graph.microsoft.com/.default” }; var authenticationResult = app.AcquireTokenForClient(scopes1).ExecuteAsync(); // Create GraphClient and attach auth header to all request (acquired on previous step) var graphClient1 = new GraphServiceClient( new DelegateAuthenticationProvider(requestMessage => { requestMessage.Headers.Authorization = new AuthenticationHeaderValue(“bearer”, authenticationResult.Result.AccessToken); return Task.FromResult(0); })); var ids = new List<string>() { userAzureId }; var types = new List<string>() { “user” }; var idAwait = OnMessageActivityAsync(graphClient1, ids, types); idAwait.Wait(); return idAwait.Result; } /// <summary> /// Call method to get data from SharePoint site. /// </summary> /// <param name="”graphClient1″">Graph service client object /// <param name="”ids”">List of user ids /// <param name="”types”">List of types /// <returns></returns> public static async Task<string> OnMessageActivityAsync(GraphServiceClient graphClient1, List<string> ids, List<string> types) { var idUsers = await graphClient1.DirectoryObjects .GetByIds(ids, types) .Request() .PostAsync(); var userEmail = ((Microsoft.Graph.User)idUsers.First()).Mail; var spcall = SharePointCall(userEmail); spcall.Wait(); return spcall.Result; } /// <summary> /// Get data from SharePoint Online site list. /// </summary> /// <param name="”userEmail”">Logged-in user’s email id /// <returns>List of tasks.</returns> public static async Task<string> SharePointCall(string userEmail) { string strCol = string.Empty; string strURL = “https://tenant-name/sites/sharepoint”; /// SharePoint Online Site URL string userName = “userName@tenant-name.onmicrosoft.com”; /// Office 365 User Account string password = “Password”; var securePassword = new SecureString(); foreach (char c in password) { securePassword.AppendChar(c); } securePassword.MakeReadOnly(); try { SecureString PW = securePassword; string strResult = string.Empty; using (var vContext = new ClientContext(strURL)) { vContext.AuthenticationMode = ClientAuthenticationMode.Default; vContext.Credentials = new SharePointOnlineCredentials(userName, PW); Microsoft.SharePoint.Client.List targetList = vContext.Web.Lists.GetByTitle(“BotTasks”); CamlQuery camlQuery = new CamlQuery { ViewXml = @”<view><query><where><and><eq><fieldref name="’AssignedTo’/"><value type="’Text’">” + userEmail + “</value></fieldref></eq><or><eq><fieldref name="’Status’/"><value type="’Choice’">In Progress</value></fieldref></eq><eq><fieldref name="’Status’/"><value type="’Choice’">Not Started</value></fieldref></eq></or></and></where></query></view>” }; var targetListItem = targetList.GetItems(camlQuery); vContext.Load(targetList); vContext.ExecuteQuery(); vContext.Load(targetListItem); vContext.ExecuteQuery(); foreach (var item in targetListItem) { strCol += item.FieldValues[“Title”].ToString() + “, “; } strCol = strCol.TrimEnd(‘ ‘); strCol = strCol.TrimEnd(‘,’); } } catch (Exception ex) { throw; } return strCol; } </string></string></string></string></string></string></string></string></string></string></string></string>
/// <summary> /// Get data from SharePoint list according to provided user IDs. /// </summary> /// <param name="userAzureId">Azure ID of user /// <returns>SharePoint list items</returns> public string GetData(string userAzureId) { var tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; var clientId1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; var clientSecret1 = "XXXXXXX?XXXXXXXXXXXXXXXXX:XXXX.-"; // Configure app builder var authority1 = $"https://login.microsoftonline.com/" + tenantId; var app = ConfidentialClientApplicationBuilder .Create(clientId1) .WithClientSecret(clientSecret1) .WithAuthority(new Uri(authority1)) .Build(); // Acquire tokens for Graph API var scopes1 = new[] { "https://graph.microsoft.com/.default" }; var authenticationResult = app.AcquireTokenForClient(scopes1).ExecuteAsync(); // Create GraphClient and attach auth header to all request (acquired on previous step) var graphClient1 = new GraphServiceClient( new DelegateAuthenticationProvider(requestMessage => { requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authenticationResult.Result.AccessToken); return Task.FromResult(0); })); var ids = new List<string>() { userAzureId }; var types = new List<string>() { "user" }; var idAwait = OnMessageActivityAsync(graphClient1, ids, types); idAwait.Wait(); return idAwait.Result; } /// <summary> /// Call method to get data from SharePoint site. /// </summary> /// <param name="graphClient1">Graph service client object /// <param name="ids">List of user ids /// <param name="types">List of types /// <returns></returns> public static async Task<string> OnMessageActivityAsync(GraphServiceClient graphClient1, List<string> ids, List<string> types) { var idUsers = await graphClient1.DirectoryObjects .GetByIds(ids, types) .Request() .PostAsync(); var userEmail = ((Microsoft.Graph.User)idUsers.First()).Mail; var spcall = SharePointCall(userEmail); spcall.Wait(); return spcall.Result; } /// <summary> /// Get data from SharePoint Online site list. /// </summary> /// <param name="userEmail">Logged-in user's email id /// <returns>List of tasks.</returns> public static async Task<string> SharePointCall(string userEmail) { string strCol = string.Empty; string strURL = "https://tenant-name/sites/sharepoint"; /// SharePoint Online Site URL string userName = "[email protected]"; /// Office 365 User Account string password = "Password"; var securePassword = new SecureString(); foreach (char c in password) { securePassword.AppendChar(c); } securePassword.MakeReadOnly(); try { SecureString PW = securePassword; string strResult = string.Empty; using (var vContext = new ClientContext(strURL)) { vContext.AuthenticationMode = ClientAuthenticationMode.Default; vContext.Credentials = new SharePointOnlineCredentials(userName, PW); Microsoft.SharePoint.Client.List targetList = vContext.Web.Lists.GetByTitle("BotTasks"); CamlQuery camlQuery = new CamlQuery { ViewXml = @"<view><query><where><and><eq><fieldref name="AssignedTo"><value type="Text">" + userEmail + "</value></fieldref></eq><or><eq><fieldref name="Status"><value type="Choice">In Progress</value></fieldref></eq><eq><fieldref name="Status"><value type="Choice">Not Started</value></fieldref></eq></or></and></where></query></view>" }; var targetListItem = targetList.GetItems(camlQuery); vContext.Load(targetList); vContext.ExecuteQuery(); vContext.Load(targetListItem); vContext.ExecuteQuery(); foreach (var item in targetListItem) { strCol += item.FieldValues["Title"].ToString() + ", "; } strCol = strCol.TrimEnd(' '); strCol = strCol.TrimEnd(','); } } catch (Exception ex) { throw; } return strCol; } // Configure app builder var authority1 = $”https://login.microsoftonline.com/” + tenantId; var app = ConfidentialClientApplicationBuilder .Create(clientId1) .WithClientSecret(clientSecret1) .WithAuthority(new Uri(authority1)) .Build(); // Acquire tokens for Graph API var scopes1 = new[] { “https://graph.microsoft.com/.default” }; var authenticationResult = app.AcquireTokenForClient(scopes1).ExecuteAsync(); // Create GraphClient and attach auth header to all request (acquired on previous step) var graphClient1 = new GraphServiceClient( new DelegateAuthenticationProvider(requestMessage => { requestMessage.Headers.Authorization = new AuthenticationHeaderValue(“bearer”, authenticationResult.Result.AccessToken); return Task.FromResult(0); })); var ids = new List<string>() { userAzureId }; var types = new List<string>() { “user” }; var idAwait = OnMessageActivityAsync(graphClient1, ids, types); idAwait.Wait(); return idAwait.Result; } /// <summary> /// Call method to get data from SharePoint site. /// </summary> /// <param name="”graphClient1″">Graph service client object /// <param name="”ids”">List of user ids /// <param name="”types”">List of types /// <returns></returns> public static async Task<string> OnMessageActivityAsync(GraphServiceClient graphClient1, List<string> ids, List<string> types) { var idUsers = await graphClient1.DirectoryObjects .GetByIds(ids, types) .Request() .PostAsync(); var userEmail = ((Microsoft.Graph.User)idUsers.First()).Mail; var spcall = SharePointCall(userEmail); spcall.Wait(); return spcall.Result; } /// <summary> /// Get data from SharePoint Online site list. /// </summary> /// <param name="”userEmail”">Logged-in user’s email id /// <returns>List of tasks.</returns> public static async Task<string> SharePointCall(string userEmail) { string strCol = string.Empty; string strURL = “https://tenant-name/sites/sharepoint”; /// SharePoint Online Site URL string userName = “[email protected]”; /// Office 365 User Account string password = “Password”; var securePassword = new SecureString(); foreach (char c in password) { securePassword.AppendChar(c); } securePassword.MakeReadOnly(); try { SecureString PW = securePassword; string strResult = string.Empty; using (var vContext = new ClientContext(strURL)) { vContext.AuthenticationMode = ClientAuthenticationMode.Default; vContext.Credentials = new SharePointOnlineCredentials(userName, PW); Microsoft.SharePoint.Client.List targetList = vContext.Web.Lists.GetByTitle(“BotTasks”); CamlQuery camlQuery = new CamlQuery { ViewXml = @”<view><query><where><and><eq><fieldref name="’AssignedTo’/"><value type="’Text’">” + userEmail + “</value></fieldref></eq><or><eq><fieldref name="’Status’/"><value type="’Choice’">In Progress</value></fieldref></eq><eq><fieldref name="’Status’/"><value type="’Choice’">Not Started</value></fieldref></eq></or></and></where></query></view>” }; var targetListItem = targetList.GetItems(camlQuery); vContext.Load(targetList); vContext.ExecuteQuery(); vContext.Load(targetListItem); vContext.ExecuteQuery(); foreach (var item in targetListItem) { strCol += item.FieldValues[“Title”].ToString() + “, “; } strCol = strCol.TrimEnd(‘ ‘); strCol = strCol.TrimEnd(‘,’); } } catch (Exception ex) { throw; } return strCol; } </string></string></string></string></string></string></string></string></string></string></string></string>
Make changes in the web.config file, so it can be consumed by another app in Azure. Copy of my WCF service’s web.config file is as below:
<!--?xml version="1.0"?--> <configuration> <appsettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"> </add></appsettings> <system.web> <compilation debug="true" targetframework="4.6.1"> <httpruntime targetframework="4.6.1"> </httpruntime></compilation></system.web> <system.servicemodel> <servicehostingenvironment aspnetcompatibilityenabled="true" multiplesitebindingsenabled="true"> </servicehostingenvironment> <bindings> <basichttpsbinding> <binding name="TransportSecurity"> <security mode="Transport"> </security> </binding> </basichttpsbinding> </bindings> <services> <service name="WCF1.ServiceMy" behaviorconfiguration="WCF1Behaviour"> <host> <baseaddresses> <add baseaddress="https://<Azure-service-URL>/ServiceMy.svc"> </add></baseaddresses> </host> <endpoint address="mex" binding="mexHttpBinding" name="Mex" contract="WCF1.IServiceMy"> <endpoint binding="basicHttpsBinding" name="Basic" contract="WCF1.IServiceMy"> </endpoint></endpoint></service> </services> <behaviors> <servicebehaviors> <behavior name="WCF1Behaviour"> <servicemetadata httpgetenabled="true" httpsgetenabled="true"> <servicedebug includeexceptiondetailinfaults="false"> </servicedebug></servicemetadata></behavior> </servicebehaviors> </behaviors> </system.servicemodel> <system.webserver> <modules runallmanagedmodulesforallrequests="true"> <directorybrowse enabled="true"> </directorybrowse></modules></system.webserver> </configuration>
<!--?xml version="1.0"?--> <configuration> <appsettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"> </add></appsettings> <system.web> <compilation debug="true" targetframework="4.6.1"> <httpruntime targetframework="4.6.1"> </httpruntime></compilation></system.web> <system.servicemodel> <servicehostingenvironment aspnetcompatibilityenabled="true" multiplesitebindingsenabled="true"> </servicehostingenvironment> <bindings> <basichttpsbinding> <binding name="TransportSecurity"> <security mode="Transport"> </security> </binding> </basichttpsbinding> </bindings> <services> <service name="WCF1.ServiceMy" behaviorconfiguration="WCF1Behaviour"> <host> <baseaddresses> <add baseaddress="https://<Azure-service-URL>/ServiceMy.svc"> </add></baseaddresses> </host> <endpoint address="mex" binding="mexHttpBinding" name="Mex" contract="WCF1.IServiceMy"> <endpoint binding="basicHttpsBinding" name="Basic" contract="WCF1.IServiceMy"> </endpoint></endpoint></service> </services> <behaviors> <servicebehaviors> <behavior name="WCF1Behaviour"> <servicemetadata httpgetenabled="true" httpsgetenabled="true"> <servicedebug includeexceptiondetailinfaults="false"> </servicedebug></servicemetadata></behavior> </servicebehaviors> </behaviors> </system.servicemodel> <system.webserver> <modules runallmanagedmodulesforallrequests="true"> <directorybrowse enabled="true"> </directorybrowse></modules></system.webserver> </configuration>
- You can publish the WCF service directly from Visual Studio to the Azure portal. In the Solution Explorer, select Project name > right-click on it > select “Publish…” > new dialog will appear. In that, click on the “Publish” button. Note: You will have to provide proper Azure Credentials to publish it in the Azure Portal.
Build Customize and Publish Bot in MS Azure
- Download template source code from the Azure Portal for Bot.
- Open Azure Portal in a browser > navigates to your Bot in the portal.
- Open Overview page > click on the “Build” tile on the page.
- Click “Download Bot source code” to download the source code.
- Open source code in Visual Studio.
- In the Solution Explorer, right-click on Project Name and add connected services. Add WCF service developed and published earlier.
- In the Solution Explorer, you can find the “Bots” folder, and underneath there will be one code file with ActivityHandler class.
- Here, we will access a logged-in user’s Azure AD and pass it to WCF service and will pass output from WCF service to the Message factory to display output in chat.
Here, below code from that file is provided: Note: Here, we are checking if the question asked by a user contains the “tasks” word. If yes, it will respond to the user’s task. You can set logic as per your requirement.
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.AI.QnA; using Microsoft.Bot.Schema; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using ServiceReference1; namespace Microsoft.BotBuilderSamples { public class QnABot : ActivityHandler { private readonly IConfiguration _configuration; private readonly ILogger<qnabot> _logger; private readonly IHttpClientFactory _httpClientFactory; public QnABot(IConfiguration configuration, ILogger<qnabot> logger, IHttpClientFactory httpClientFactory) { _configuration = configuration; _logger = logger; _httpClientFactory = httpClientFactory; } protected override async Task OnMessageActivityAsync(ITurnContext<imessageactivity> turnContext, CancellationToken cancellationToken) { var httpClient = _httpClientFactory.CreateClient(); var qnaMaker = new QnAMaker(new QnAMakerEndpoint { KnowledgeBaseId = _configuration["QnAKnowledgebaseId"], EndpointKey = _configuration["QnAAuthKey"], Host = GetHostname() }, null, httpClient); _logger.LogInformation("Calling QnA Maker"); try { //get user var userId = turnContext.Activity.From.Id; var userAAdObject = turnContext.Activity.From.AadObjectId; var userProperties = turnContext.Activity.From.Properties; var userName = turnContext.Activity.From.Name; // The actual call to the QnA Maker service. var response = await qnaMaker.GetAnswersAsync(turnContext); if (response != null && response.Length > 0) { if (turnContext.Activity.Text.ToLower().Contains("task")) { ServiceMyClient client = new ServiceMyClient(ServiceMyClient.EndpointConfiguration.Basic); string result = await client.GetDataAsync(userAAdObject); await turnContext.SendActivityAsync(MessageFactory.Text(result), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text(response[0].Answer), cancellationToken); } } else { if (turnContext.Activity.Text.ToLower().Contains("task")) { ServiceMyClient client = new ServiceMyClient(ServiceMyClient.EndpointConfiguration.Basic); string result = await client.GetDataAsync(userAAdObject); await turnContext.SendActivityAsync(MessageFactory.Text(result), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text("No QnA Maker answers were found."), cancellationToken); } } } catch (System.Exception ex) { throw; } } private string GetHostname() { var hostname = _configuration["QnAEndpointHostName"]; if (!hostname.StartsWith("https://")) { hostname = string.Concat("https://", hostname); } if (!hostname.EndsWith("/qnamaker")) { hostname = string.Concat(hostname, "/qnamaker"); } return hostname; } } } using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.AI.QnA; using Microsoft.Bot.Schema; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using ServiceReference1; namespace Microsoft.BotBuilderSamples { public class QnABot : ActivityHandler { private readonly IConfiguration _configuration; private readonly ILogger<qnabot> _logger; private readonly IHttpClientFactory _httpClientFactory; public QnABot(IConfiguration configuration, ILogger<qnabot> logger, IHttpClientFactory httpClientFactory) { _configuration = configuration; _logger = logger; _httpClientFactory = httpClientFactory; } protected override async Task OnMessageActivityAsync(ITurnContext<imessageactivity> turnContext, CancellationToken cancellationToken) { var httpClient = _httpClientFactory.CreateClient(); var qnaMaker = new QnAMaker(new QnAMakerEndpoint { KnowledgeBaseId = _configuration[“QnAKnowledgebaseId”], EndpointKey = _configuration[“QnAAuthKey”], Host = GetHostname() }, null, httpClient); _logger.LogInformation(“Calling QnA Maker”); try { //get user var userId = turnContext.Activity.From.Id; var userAAdObject = turnContext.Activity.From.AadObjectId; var userProperties = turnContext.Activity.From.Properties; var userName = turnContext.Activity.From.Name; // The actual call to the QnA Maker service. var response = await qnaMaker.GetAnswersAsync(turnContext); if (response != null && response.Length > 0) { if (turnContext.Activity.Text.ToLower().Contains(“task”)) { ServiceMyClient client = new ServiceMyClient(ServiceMyClient.EndpointConfiguration.Basic); string result = await client.GetDataAsync(userAAdObject); await turnContext.SendActivityAsync(MessageFactory.Text(result), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text(response[0].Answer), cancellationToken); } } else { if (turnContext.Activity.Text.ToLower().Contains(“task”)) { ServiceMyClient client = new ServiceMyClient(ServiceMyClient.EndpointConfiguration.Basic); string result = await client.GetDataAsync(userAAdObject); await turnContext.SendActivityAsync(MessageFactory.Text(result), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text(“No QnA Maker answers were found.”), cancellationToken); } } } catch (System.Exception ex) { throw; } } private string GetHostname() { var hostname = _configuration[“QnAEndpointHostName”]; if (!hostname.StartsWith(“https://”)) { hostname = string.Concat(“https://”, hostname); } if (!hostname.EndsWith(“/qnamaker”)) { hostname = string.Concat(hostname, “/qnamaker”); } return hostname; } } } </imessageactivity></qnabot></qnabot></imessageactivity></qnabot></qnabot>
// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.AI.QnA; using Microsoft.Bot.Schema; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using ServiceReference1; namespace Microsoft.BotBuilderSamples { public class QnABot : ActivityHandler { private readonly IConfiguration _configuration; private readonly ILogger<qnabot> _logger; private readonly IHttpClientFactory _httpClientFactory; public QnABot(IConfiguration configuration, ILogger<qnabot> logger, IHttpClientFactory httpClientFactory) { _configuration = configuration; _logger = logger; _httpClientFactory = httpClientFactory; } protected override async Task OnMessageActivityAsync(ITurnContext<imessageactivity> turnContext, CancellationToken cancellationToken) { var httpClient = _httpClientFactory.CreateClient(); var qnaMaker = new QnAMaker(new QnAMakerEndpoint { KnowledgeBaseId = _configuration["QnAKnowledgebaseId"], EndpointKey = _configuration["QnAAuthKey"], Host = GetHostname() }, null, httpClient); _logger.LogInformation("Calling QnA Maker"); try { //get user var userId = turnContext.Activity.From.Id; var userAAdObject = turnContext.Activity.From.AadObjectId; var userProperties = turnContext.Activity.From.Properties; var userName = turnContext.Activity.From.Name; // The actual call to the QnA Maker service. var response = await qnaMaker.GetAnswersAsync(turnContext); if (response != null && response.Length > 0) { if (turnContext.Activity.Text.ToLower().Contains("task")) { ServiceMyClient client = new ServiceMyClient(ServiceMyClient.EndpointConfiguration.Basic); string result = await client.GetDataAsync(userAAdObject); await turnContext.SendActivityAsync(MessageFactory.Text(result), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text(response[0].Answer), cancellationToken); } } else { if (turnContext.Activity.Text.ToLower().Contains("task")) { ServiceMyClient client = new ServiceMyClient(ServiceMyClient.EndpointConfiguration.Basic); string result = await client.GetDataAsync(userAAdObject); await turnContext.SendActivityAsync(MessageFactory.Text(result), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text("No QnA Maker answers were found."), cancellationToken); } } } catch (System.Exception ex) { throw; } } private string GetHostname() { var hostname = _configuration["QnAEndpointHostName"]; if (!hostname.StartsWith("https://")) { hostname = string.Concat("https://", hostname); } if (!hostname.EndsWith("/qnamaker")) { hostname = string.Concat(hostname, "/qnamaker"); } return hostname; } } } using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.AI.QnA; using Microsoft.Bot.Schema; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using ServiceReference1; namespace Microsoft.BotBuilderSamples { public class QnABot : ActivityHandler { private readonly IConfiguration _configuration; private readonly ILogger<qnabot> _logger; private readonly IHttpClientFactory _httpClientFactory; public QnABot(IConfiguration configuration, ILogger<qnabot> logger, IHttpClientFactory httpClientFactory) { _configuration = configuration; _logger = logger; _httpClientFactory = httpClientFactory; } protected override async Task OnMessageActivityAsync(ITurnContext<imessageactivity> turnContext, CancellationToken cancellationToken) { var httpClient = _httpClientFactory.CreateClient(); var qnaMaker = new QnAMaker(new QnAMakerEndpoint { KnowledgeBaseId = _configuration[“QnAKnowledgebaseId”], EndpointKey = _configuration[“QnAAuthKey”], Host = GetHostname() }, null, httpClient); _logger.LogInformation(“Calling QnA Maker”); try { //get user var userId = turnContext.Activity.From.Id; var userAAdObject = turnContext.Activity.From.AadObjectId; var userProperties = turnContext.Activity.From.Properties; var userName = turnContext.Activity.From.Name; // The actual call to the QnA Maker service. var response = await qnaMaker.GetAnswersAsync(turnContext); if (response != null && response.Length > 0) { if (turnContext.Activity.Text.ToLower().Contains(“task”)) { ServiceMyClient client = new ServiceMyClient(ServiceMyClient.EndpointConfiguration.Basic); string result = await client.GetDataAsync(userAAdObject); await turnContext.SendActivityAsync(MessageFactory.Text(result), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text(response[0].Answer), cancellationToken); } } else { if (turnContext.Activity.Text.ToLower().Contains(“task”)) { ServiceMyClient client = new ServiceMyClient(ServiceMyClient.EndpointConfiguration.Basic); string result = await client.GetDataAsync(userAAdObject); await turnContext.SendActivityAsync(MessageFactory.Text(result), cancellationToken); } else { await turnContext.SendActivityAsync(MessageFactory.Text(“No QnA Maker answers were found.”), cancellationToken); } } } catch (System.Exception ex) { throw; } } private string GetHostname() { var hostname = _configuration[“QnAEndpointHostName”]; if (!hostname.StartsWith(“https://”)) { hostname = string.Concat(“https://”, hostname); } if (!hostname.EndsWith(“/qnamaker”)) { hostname = string.Concat(hostname, “/qnamaker”); } return hostname; } } } </imessageactivity></qnabot></qnabot></imessageactivity></qnabot></qnabot>
- The Sharepoint developers can develop this Bot and test it in the Emulator. Once your development is completed, publish source code in Azure Portal.
- You can publish BOT directly from Visual Studio to the Azure portal. In the Solution Explorer, select Project name > right-click on it > select “Publish…” > new dialog will appear. In that, click on the “Publish” button. Note: You will have to provide proper Azure Credentials to publish it in the Azure Portal.
Test Bot in MS Azure
- Add tasks in your SharePoint Online tasks list for the user with whom you have published Bot in the Azure Portal.
- To test your Bot, open your Azure Portal in a browser.
- Click “Test in Web Chat” and type the question “What’s my tasks?” and wait for your answers.
Note: Here, simple task titles are displayed in a response from Bot. The SharePoint developers can display cards and hyperlinks also in reply from Bot.
Deploy Bot in MS Teams
- Open the Azure Portal and navigate to your Bot.
- Click “Channels” > click MS Teams icon to configure it.
- Click on the “Save” button to save the channel.
- Click on the “Microsoft Teams” channel to get started with your Bot in your MS Teams.
- It will open a new tab and ask to continue with MS Teams desktop or Web app version.
- Here, we have opened an MS Teams Desktop version.
- Other users of the tenant can add this Bot in their MS Teams using its Microsoft app ID.
- The administrator can provide Microsoft app ID from the Azure Portal.
- Open the Azure Portal and navigate to your Bot.
- Click “Configuration” > click “Show values” > copy value of “MicrosoftAppId”.
- In MS Teams, click on the “New Chat” icon > search with Microsoft App ID of Bot in it to make available that Bot in your MS Teams.
Publish your Bot in your organization’s Team Tenant App Catalog
The Sharepoint developer can use your organization’s Teams Tenant app catalog to distribute apps to your organization. You will require global admin credentials and MS Teams client app to publish your Bot.
Create and test the app in the App Catalog using App Studio
- Open MS Teams with your global admin credentials.
- Click on the ellipsis (…) > click “More apps” > search for “App Studio.”
- Install the App Studio app.
- Open the app from ellipsis > select “App Studio” > select “Manifest editor” from the menu.
- Click on the “Create a new app” from the left menu.
- Fill in all the required details in the form. Set Microsoft App ID in the App ID field.
- Set proper resolution images as mentioned for Branding purposes.
- Select the “Bot” from the left menu > click on the “Set up” button.
- Popup will appear > select “Existing Bot” > Set Bot name and set Bot’s Microsoft App ID > select Personal in Scope > click “Save.”
- Select “Test and distribute” from the left menu > click on the “Install” button to install this Bot in your MS Teams Client.
- Click on the “Install” button > click “Open” to open the Bot. You can test your Bot in MS Teams client.
Note: This Bot installed using the above steps will only be available for that user. It is yet not installed to be available for all users in the organization.
Get app package from App Studio
- Open the App Studio app in MS Teams Client.
- Click on your Bot app. Here, it is “AskMe Bot.”
- Select “Test and distribute” from the left menu > click on “Download” to download the package for this app. It will download a zip folder.
Publish app in Tenant App CatalogThe Global administrator can export the packages and upload them in the Tenant App Catalog so all users from an organization can install it in their MS Teams. Steps for the same are as below: - Click on “Store” from the left menu in MS Teams Client.
- Click on “Upload a custom app” at the bottom of the app.
Being a Global Admin, it will give two options to upload a package:
- Upload for me or my teams
- Upload for Tatvasoft
- Select “Upload for Tatvasoft.” In your case, it will show your organization name. Upload zip folder downloaded previously.
- It will display your Bot under your organization tab.
- Users can install Bot in their MS Teams Client by clicking on it.
Conclusion
Trusting that this blog has given enough data to SharePoint consultants in an unmistakable and brief approach, to begin with, fundamental Q&A bots and its coordinated effort with Microsoft Teams. In a later stage, make advanced bots that incorporate virtual assistants equipped for handling rich & multi-turn conversations, natural language understanding capabilities, and so forth. There are different utilizations of Bots that can be expended according to the need. Coordination of the Azure Bot Service across multiple communication channels helps the SharePoint development company reach more clients, all the more frequently.
Shital Patel
Shital Patel is VP at TatvaSoft with a high-level of proficiency and technical precision in SharePoint Development. His experience of the last two decades has helped businesses to solve complex challenges resulting in growth and performance of Startups to Fortune 500 companies.
Subscribe to our Newsletter
Signup for our newsletter and join 2700+ global business executives and technology experts to receive handpicked industry insights and latest news
Build your Team
Want to Hire Skilled Developers?
Comments
Leave a message...