I Didn t Understand Please Try Again Bot Framework

    Build a Dr. Date Bot with Azure Bot Service, Language Understanding, and Twilio SMS

    Telehealth services can improve access to healthcare for people living in remote areas. Ane common issue in remote areas is unreliable internet service. It'due south a claiming for patients in remote areas to make appointments via web applications when internet service is poor or not available at all.

    SMS is a reliable and low-cost culling to reach people living in remote areas. In this commodity, yous will learn how to build a medico appointment booking arrangement which allows users to book via SMS.

    System Overview

    The architecture of the SMS booking organization is illustrated below.

    A diagram to explain how the SMS booking system works. Users send text messages using their phones to a Twilio Phone Number, Twilio forwards the message to the webhook URL which is pointing to the Azure Bot, the Azure Bot forwards the message to the Language Understanding service for AI analysis. The Azure Bot responds to Twilio and Twilio forwards the response back to the users" phone.

    Twilio is the communication aqueduct between Microsoft Azure Bot Service and the end user. The core of the system is an Azure Bot congenital on the Microsoft Bot Framework. When the bot receives a bulletin, it asks Linguistic communication Understanding (LUIS) to analyze the message. LUIS responds with the intent of the message, which the bot uses to respond with a helpful reply.

    Prerequisites

    Y'all'll need these technologies to follow along:

    • An Os that supports .Cyberspace (Windows/macOS/Linux)
    • .Cyberspace (Core) SDK version 3.1 or later on
    • PowerShell Core or Windows PowerShell (you can use any shell, but you'll demand to make minor modifications)
    • Azure subscription (get a complimentary subscription)
    • A free Twilio account
    • Azure CLI 2.2.0 or later
    • A .NET IDE (Recommended: Visual Studio 2022 or later, Visual Studio Code with the C# plugin, or JetBrains Rider).

    You can find the completed source code in this GitHub repo.

    Create the Language Understanding Service app

    Azure Configuration

    You demand to create a resource grouping to store the Azure resource you will create afterward.

    Open up a PowerShell and sign in to Azure using the Azure CLI:

    Run the following command to create a new resource grouping:

                      az grouping create --name rg-bot --location [AZURE_LOCATION]                                  

    Supplant [AZURE_LOCATION] with the name of your preferred Azure location.

    Every resource in Azure is stored in a resource group, and every resource and resource group in Azure is stored in a specific Azure region or location. There are different reasons for picking different locations, but most unremarkably you desire to pick the location that'southward closest to you and your end users. You can find all regions by running az account list-locations -o tabular array. Observe your preferred location and use the value from the Name cavalcade to specify the location when creating Azure resources. Keep in mind that not all resource are available in all Azure locations.

    To interact with specific types of Azure resources, the resources provider for those resource types needs to be enabled. The near common resource providers are enabled by default, but you'll be using Language Agreement (LUIS) which is part of the Cognitive Services resource provider that is not enabled by default. You can register the Cognitive Services resource provider in Azure Portal, or using the Azure CLI as shown below.

    Start, cheque the status of the Cerebral Services resource provider using this command:

                      az provider prove --namespace Microsoft.CognitiveServices -o table                                  

    If the RegistrationState is UnRegistered, so run the following command to annals it:

                      az provider register --namespace Microsoft.CognitiveServices --wait                                  

    This command may take a minute to complete.

    Create the LUIS app and train the model

    Linguistic communication Understanding (LUIS) is a cloud-based conversational AI service, role of Azure'sCognitive Services. It can process natural language text to predict overall meaning, and pull out relevant, detailed information.

    To learn how to use LUIS, you demand to know a few core concepts:

    • Intent: The purpose or goal expressed in a user's utterance.
    • Entity: An item or an element that is relevant to the user'due south intent
    • Utterance: Unit of speech. Every sentence from a user is an utterance.

    Diagram to explain the difference between intent, utterances, and entity. Intent is what a user wants to do, like flight booking. Utterances are different ways to express the intent, like "I want to book a flight form NY to LA". Entities are details of the intent, like NY and LA.

    LUIS will deed as the encephalon of the bot by helping the bot understand the incoming letters.

    Create the LUIS App

    Log in to the LUIS portal with your Azure business relationship.

    If you are a new user to LUIS, yous will exist prompted to Choose an authoring resource.

    Choose an authoring resource modal in the LUIS dashboard. The "Authoring resource" dropdown is empty because there"s no authoring resource created yet. There"s a button underneath to "Create a new authoring resource"

    Click Create a new authoring resource if y'all haven't created i before.

    Another modal will announced to Create a new authoring resource.

    Create a new authoring resource modal, which asks for an Azure Subscription, Azure resource group, an Azure resource name, an Azure location, and pricing tier.

    Cull "rg-bot" in the Azure resources group dropdown, and enter "appointment-booking" in the Azure resource proper name field. And then pick your preferred region in the Location dropdown, choose "F0" in Pricing Tier, and click Washed.

    Yous will exist redirected back to previous Choose an authoring resource modal. Now, you tin click on the Done push, then you will be redirected to the dashboard page.

    On the dashboard page, click Create new app. The Create new app modal will announced.

    Create a new LUIS app modal that requires a name, but all other fields are optional and can be safely ignored.

    Enter "Engagement-booking" in the Name field and click Washed.

    After the app is created, a tutorial modal will be shown. Click outside the modal to go to the newly created app details page.

    You lot'll need to collect some key information for subsequently use. Click on the MANAGE tab and so on the Settings link. Take note of the App ID. Now, click on the Azure Resource link on the left bill of fare and then click on the Authoring Resources tab.

    The Authoring Resource sub-tab under the manage tab. This sub-tab shows details about the authoring resource, most importantly, the Primary Key and the Endpoint URL.

    Take annotation of the Primary key (LUIS API key) and the Endpoint URL.

    Now, the appointment-booking LUIS app is created successfully. The next step is to create the heart of the app: the LUIS model.

    Import the Model

    There are two means to create the model. You tin can navigate to the Build tab of the app, and so create entities and intents manually. Or you can import a predefined model file into the app. To save time, y'all tin download this LUIS model JSON file and import it.

    After downloading the JSON model file, navigate to the Versions page via MANAGE > Versions. Click on the Import push, and choose "Import as JSON" to open the import popup every bit shown below.

    LUIS"s import as JSON modal to import LUIS models using LUIS JSON files. The modal prompts for a JSON file and a name.

    Click on the Choose file push, select your JSON file, and click on the Washed button. The new model will be imported into the app.

    Navigate to the Build > App Assets folio and pick "vAppointmentBookingBot.LUISModel.json" from the versions dropdown which you tin can find in the breadcrumb navigation at the elevation-left of the folio.

    A dropdown in the breadcrumb navigation to switch between model versions. The vAppointmentBookingBot.LUISModel.json file and the default v0.1 dropdown options are shown.

    Now, you will see the newly created intents and entities.

    Train, Test, and Publish the LUIS Model

    Subsequently the intents and entities are imported, the Train button in the top navigation bar is enabled.

    The navigation bar for the LUIS app containing a "train" button.

    Click the Train button to start the grooming process. The Train button volition exist disabled and the Test push button will be enabled afterwards training is completed.

    To examination the new model, click on the Test push. A Test flyout panel will appear on the right. You can type an utterance into the test panel to try it out.

    In the screenshot below, "i want to see doctor kathy" is given a score 0.973 out of 1 for the BookAppointment intent. In the Inspect window, it also identifies the Physician entity as "kathy" correctly.

    The LUIS test panel where the user enters "i want to see doctor kathy" and LUIS returns the "BookApointment" intent with a 97% certainty and identifies "kathy" as a doctor..

    Since the exam consequence looks pretty good, yous tin can publish the LUIS app now. Click on the Publish button on the navigation bar, select the Product Slot, and click Washed.

    Green notification indicating that publishing the app was successful.

    After publishing is completed, a successful message notification is shown as shown higher up. That means the LUIS app is ready to be used!

    Build the bot

    Create the bot using a Bot Framework template

    In this tutorial, yous're going to use Bot Framework v4 SDK Templates to create the bot projection. Open a shell and install the Bot Framework templates using the .Cyberspace CLI with these commands:

                      dotnet new -i Microsoft.Bot.Framework.CSharp.EchoBot dotnet new -i Microsoft.Bot.Framework.CSharp.CoreBot dotnet new -i Microsoft.Bot.Framework.CSharp.EmptyBot                                  

    Yous'll simply be using the CoreBot template in this tutorial, simply feel costless to explore the EchoBot and EmptyBot template.

    Now, you lot can use the newly installed template to generate a new bot project. Run the post-obit command to create the bot project:

                      dotnet new corebot -north AppointmentBot                                  

    After creating the project with the previous command, the projection is created into the AppointmentBot/CoreBot binder and the root namespace is set to "CoreBot". This is inconsistent with how .NET templates normally work, but it tin can easily exist rectified. The following PowerShell script will move the contents into the AppointmentBot folder, rename the project, and change all the namespaces to "AppointmentBot". Run the post-obit script using PowerShell:

                      $CorrectProjectName = "AppointmentBot" Push button-Location "./$CorrectProjectName" Move-Particular ./CoreBot/* ./ Remove-Detail ./CoreBot Motion-Item ./CoreBot.csproj "./$CorrectProjectName.csproj" Get-ChildItem * -Recurse -File | ForEach-Object { (Become-Content $_) -replace 'CoreBot', $CorrectProjectName | Set-Content $_ } Popular-Location                                  

    Open the project using your preferred .Internet editor. The project structure will look similar below.

    A list of files and folders that were generated using the "CoreBot" template, most importantly, a folder "CognitiveModels" with C# models in it for flight booking, a "Controllers" folder with a "BotController.cs" file, "AdaptorWithErrorHandler.cs" file, "FlightBookingRecognizer.cs", and "Startup.cs".

    The generated projection comes with a flight booking bot sample. Remove those related model and dialog files every bit listed below.

    • [projectRoot]\CognitiveModels\FlightBooking.cs
    • [projectRoot]\CognitiveModels\FlightBooking.json
    • [projectRoot]\CognitiveModels\FlightBookingEx.cs
    • [projectRoot]\Dialogs\BookingDialog.cs
    • [projectRoot]\Dialogs\MainDialog.cs
    • [projectRoot]\BookingDetails.cs
    • [projectRoot]\FlightBookingRecognizer.cs

    To salve time, you tin can run the script below to remove the above files. Run the script from the project root folder:

                      rm CognitiveModels/FlightBooking.cs rm CognitiveModels/FlightBooking.json rm CognitiveModels/FlightBookingEx.cs rm Dialogs/BookingDialog.cs rm Dialogs/MainDialog.cs rm BookingDetails.cs rm FlightBookingRecognizer.cs                                  

    Yous will also demand to remove lines 41 to 51 in the Startup.cs file. Those are the references to the deleted files.

                                          // Register LUIS recognizer             services.AddSingleton<FlightBookingRecognizer>();              // Register the BookingDialog.             services.AddSingleton<BookingDialog>();              // The MainDialog that will be run by the bot.             services.AddSingleton<MainDialog>();              // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.             services.AddTransient<IBot, DialogAndWelcomeBot<MainDialog>>();                                  

    After the cleanup, the Startup class volition expect similar below:

                      public form Startup     {         // This method gets called by the runtime. Employ this method to add together services to the container.         public void ConfigureServices(IServiceCollection services)         {             services.AddHttpClient().AddControllers().AddNewtonsoftJson();              // Create the Bot Framework Authentication to be used with the Bot Adapter.             services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();              // Create the Bot Adapter with fault handling enabled.             services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();              // Create the storage nosotros'll exist using for User and Conversation state. (Memory is groovy for testing purposes.)             services.AddSingleton<IStorage, MemoryStorage>();              // Create the User state. (Used in this bot's Dialog implementation.)             services.AddSingleton<UserState>();              // Create the Conversation country. (Used past the Dialog system itself.)             services.AddSingleton<ConversationState>();          }          // This method gets chosen past the runtime. Use this method to configure the HTTP request pipeline.         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)         {             if (env.IsDevelopment())             {                 app.UseDeveloperExceptionPage();             }              app.UseDefaultFiles()                 .UseStaticFiles()                 .UseWebSockets()                 .UseRouting()                 .UseAuthorization()                 .UseEndpoints(endpoints =>                 {                     endpoints.MapControllers();                 });              // app.UseHttpsRedirection();         }     }                                  

    Appointment Booking Cognitive Model

    Now that the project has been cleaned upwardly, you can start implementing your ain logic. Next, you'll exist creating the model, which LUIS will render to united states with assay data.

    Next, you lot'll create these files under the CognitiveModels folder:

    • DoctorBooking.cs: This file will contain the DoctorBooking class, which represents the data returned by LUIS.
    • DoctorBookingEx.cs: This file will extend DoctorBooking using a fractional class to simplify accessing the entities of the LUIS results

    Create the CognitiveModels/Doctorbooking.cs and add the post-obit code:

                      using Microsoft.Bot.Builder; using Microsoft.Bot.Architect.AI.Luis; using Newtonsoft.Json; using Organization.Collections.Generic;  namespace AppointmentBot.CognitiveModels {      public partial class DoctorBooking : IRecognizerConvert     {         public string Text;         public string AlteredText;         public enum Intent         {             BookAppointment,             Cancel,             GetAvailableDoctors,             None         };         public Dictionary<Intent, IntentScore> Intents;          public class _Entities         {              // Built-in entities             public DateTimeSpec[] datetime;              // Lists             public string[][] Doctor;              // Instance             public class _Instance             {                 public InstanceData[] datetime;                 public InstanceData[] Doc;                 public InstanceData[] AvailableDoctors;             }             [JsonProperty("$example")]             public _Instance _instance;         }         public _Entities Entities;          [JsonExtensionData(ReadData = true, WriteData = true)]         public IDictionary<string, object> Properties { get; set up; }          public void Catechumen(dynamic consequence)         {             var app = JsonConvert.DeserializeObject<DoctorBooking>(JsonConvert.SerializeObject(result, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));             Text = app.Text;             AlteredText = app.AlteredText;             Intents = app.Intents;             Entities = app.Entities;             Properties = app.Backdrop;         }          public (Intent intent, double score) TopIntent()         {             Intent maxIntent = Intent.None;             var max = 0.0;             foreach (var entry in Intents)             {                 if (entry.Value.Score > max)                 {                     maxIntent = entry.Fundamental;                     max = entry.Value.Score.Value;                 }             }             return (maxIntent, max);         }     } }                                  

    I generated this class using the Bot Framework CLI and provided it for your convenience, but you can also generate this yourself.

    You lot'll need to install node.js and the BF CLI showtime, if y'all want to generate the Doctorbooking.cs yourself.

    You can install the BF CLI using the post-obit control: ​​​​npm i -k @microsoft/botframework-cli

    Download the LUIS model JSON file to your project directory, and and then run the following command from the project root directory:

    ​​bf luis:generate:cs --in=AppointmentBookingBot.LUISModel.json --out=CognitiveModels/DoctorBooking.cs --className=AppointmentBot.CognitiveModels.DoctorBookin

    Create the CognitiveModels/ DoctorBookingEx.cs file and add the following lawmaking:

                      using System.Linq;  namespace AppointmentBot.CognitiveModels {     // Extends the fractional DoctorBooking class with methods and properties that simplify accessing entities in the luis results     public fractional class DoctorBooking     {          public string Md         {             get             {                 var doctorChosen = Entities?._instance?.Doctor?.FirstOrDefault()?.Text;                 return doctorChosen;             }         }          // This value volition be a TIMEX. And nosotros are only interested in a Appointment so take hold of the first result and drop the Time part.         // TIMEX is a format that represents DateTime expressions that include some ambiguity. e.k. missing a Twelvemonth.         public string AppointmentDate             => Entities.datetime?.FirstOrDefault()?.Expressions.FirstOrDefault()?.Dissever('T')[0];     } }                                  

    Connect the bot to the LUIS App

    To integrate the bot service with the LUIS app, you need to add the LUIS App ID, API central, and API Endpoint URL into the projection configuration.

    Supercede the contents of appsettings.json with the JSON beneath:

                      {   "MicrosoftAppType": "",   "MicrosoftAppId": "",   "MicrosoftAppPassword": "",   "MicrosoftAppTenantId": "",   "LuisAppId": "[YOUR_LUIS_APP_ID]",   "LuisApiKey": "<SET_USING_USER_SECRETS>",   "LuisApiEndpointUrl": "[LUIS_ENDPOINT_URL]" }                                  

    Replace [YOUR_LUIS_APP_ID] with your LUIS App ID, and [LUIS_ENDPOINT_URL] with the LUIS Endpoint URL you lot took note of earlier.

    Delight note that you should not store sensitive information including API keys or tokens in your source-code. That'south why you'll configure the LuisApiKey using the Hugger-mugger Director tool.

    Enable the Secret Manager tool for your project by running the following command at the projection root directory:

    Run the following command to configure the LuisApiKey using the Secret Managing director:

                      dotnet user-secrets set "LuisApiKey" "[YOUR_LUIS_API_KEY]"                                  

    Replace [YOUR_LUIS_API_KEY] with the LUIS App Primary Key y'all took notation off earlier.

    The bot application volition retrieve the settings you lot just configured to institute the connectedness to your LUIS app in the AppointmentBookingRecognizer course below. Create a new file AppointmentBookingRecognizer.cs and add together the following contents:

                      using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.AI.Luis; using Microsoft.Extensions.Configuration; using Arrangement.Threading; using System.Threading.Tasks;  namespace AppointmentBot {     public grade AppointmentBookingRecognizer : IRecognizer     {         private readonly LuisRecognizer _recognizer;          public AppointmentBookingRecognizer(IConfiguration configuration)         {             var luisIsConfigured = !string.IsNullOrEmpty(configuration["LuisAppId"]) && !string.IsNullOrEmpty(configuration["LuisApiKey"]) && !cord.IsNullOrEmpty(configuration["LuisApiEndpointUrl"]);             if (luisIsConfigured)             {                 var luisApplication = new LuisApplication(                     configuration["LuisAppId"],                     configuration["LuisApiKey"],                     configuration["LuisApiEndpointUrl"]);                 // Set the recognizer options depending on which endpoint version you want to use.                 // More than details can be found in https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3                 var recognizerOptions = new LuisRecognizerOptionsV3(luisApplication)                 {                     PredictionOptions = new Microsoft.Bot.Builder.AI.LuisV3.LuisPredictionOptions                     {                         IncludeInstanceData = true,                     }                 };                  _recognizer = new LuisRecognizer(recognizerOptions);             }         }          // Returns truthful if luis is configured in the appsettings.json and initialized.         public virtual bool IsConfigured => _recognizer != nada;          public virtual async Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, CancellationToken cancellationToken)             => look _recognizer.RecognizeAsync(turnContext, cancellationToken);          public virtual async Chore<T> RecognizeAsync<T>(ITurnContext turnContext, CancellationToken cancellationToken)             where T : IRecognizerConvert, new()             => await _recognizer.RecognizeAsync<T>(turnContext, cancellationToken);     } }                                  

    A recognizer is used to recognize user input and return intents and entities within a DialogContext. In the AppointmentBookingRecognizer class, a connection is established to the LUIS API endpoint. Information technology also implements the RecognizeAsync method, which is chosen by dialogs to excerpt intents and entities from a user's utterance.

    Control the Conversation Flow using Dialogs

    You lot need to employ Dialogs to manage conversation between the user and the bot.

    Dialogs are a fundamental concept in the SDK, providing ways to manage a long-running conversation with the user. A Dialog tin can exist composed with other dialogs.

    Bot framework provides a rich set of dialogs to make it easier to create a conversation flow. In this instance, you will create an AppointmentBookingDialog class to manage the main conversation. It is composed of a few dialogs, including a waterfall dialog and prompt dialogs.

    The waterfall dialog is used to ascertain the sequence of steps. Every bit illustrated in the diagram beneath, the bot interacts with the user via a linear process.

    A flowchart plotting how the bot conversation should flow. Choose doctor question asked? If no, go back to start. If yes, is doctor valid? If yes, ask appointment date time question. Did the user respond with a valid date time? If no, go back to appointment date time question. If yes, ask user to confirm. If confirmed, run final step.

    Create a new file AppointmentDetails.cs into project root and add the following lawmaking:

                      namespace AppointmentBot {     public form AppointmentDetails     {         public string Md { get; prepare; }          public string AppointmentDate { get; gear up; }     } }                                  

    The AppointmentDetails course is the model class for the dialog. Next, create the AppointmentBookingDialog.cs file into the Dialogs folder. AppointmentBookingDialog class will implement the process above. Add the following code to the file:

                      using Microsoft.Bot.Builder; using Microsoft.Bot.Architect.Dialogs; using Microsoft.Bot.Schema; using Microsoft.Recognizers.Text.DataTypes.TimexExpression; using Organisation.Threading; using Organisation.Threading.Tasks;  namespace AppointmentBot.Dialogs {     public form AppointmentBookingDialog : CancelAndHelpDialog     {         private const string DoctorStepMsgText = "Who would you like to come across?";          public AppointmentBookingDialog()             : base(nameof(AppointmentBookingDialog))         {             AddDialog(new TextPrompt(nameof(TextPrompt)));             AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));             AddDialog(new DateResolverDialog());             AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]             {                 DoctorStepAsync,                 AppointmentDateStepAsync,                 ConfirmStepAsync,                 FinalStepAsync,             }));              // The initial kid Dialog to run.             InitialDialogId = nameof(WaterfallDialog);         }          private async Task<DialogTurnResult>DoctorStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)         {             var bookingDetails = (AppointmentDetails)stepContext.Options;              if (bookingDetails.Doctor == naught)             {                 var promptMessage = MessageFactory.Text(DoctorStepMsgText, DoctorStepMsgText, InputHints.ExpectingInput);                 return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);             }              return await stepContext.NextAsync(bookingDetails.Doctor, cancellationToken);         }          private async Task<DialogTurnResult> AppointmentDateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)         {             var bookingDetails = (AppointmentDetails)stepContext.Options;              bookingDetails.AppointmentDate = (string)stepContext.Upshot;              if (bookingDetails.AppointmentDate == null || IsAmbiguous(bookingDetails.AppointmentDate))             {                 return expect stepContext.BeginDialogAsync(nameof(DateResolverDialog), bookingDetails.AppointmentDate, cancellationToken);             }              return await stepContext.NextAsync(bookingDetails.AppointmentDate, cancellationToken);         }          individual async Chore<DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)         {             var bookingDetails = (AppointmentDetails)stepContext.Options;              bookingDetails.AppointmentDate = (string)stepContext.Outcome;              var messageText = $"Delight confirm, I take you lot book with Doctor: {bookingDetails.Doctor} on: {bookingDetails.AppointmentDate}. Is this correct?";             var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);              return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);         }          private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)         {             if ((bool)stepContext.Result)             {                 var bookingDetails = (AppointmentDetails)stepContext.Options;                  render expect stepContext.EndDialogAsync(bookingDetails, cancellationToken);             }              return await stepContext.EndDialogAsync(null, cancellationToken);         }          private static bool IsAmbiguous(string timex)         {             var timexProperty = new TimexProperty(timex);             return !timexProperty.Types.Contains(Constants.TimexTypes.Definite);         }     } }                                  

    Main Dialog

    The MainDialog form manages the main procedure menses. Create the MainDialog.cs file in the Dialogs folder and add the following lawmaking:

                      using AppointmentBot.CognitiveModels; using Microsoft.Bot.Builder; using Microsoft.Bot.Architect.Dialogs; using Microsoft.Bot.Schema; using Microsoft.Extensions.Logging; using Microsoft.Recognizers.Text.DataTypes.TimexExpression; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Organization.Threading.Tasks;  namespace AppointmentBot.Dialogs {     public class MainDialog : ComponentDialog     {         individual readonly AppointmentBookingRecognizer _luisRecognizer;         protected readonly ILogger Logger;          // Dependency injection uses this constructor to instantiate MainDialog         public MainDialog(AppointmentBookingRecognizer luisRecognizer, AppointmentBookingDialog appointmentDialog, ILogger<MainDialog> logger)             : base(nameof(MainDialog))         {             _luisRecognizer = luisRecognizer;             Logger = logger;              AddDialog(new TextPrompt(nameof(TextPrompt)));             AddDialog(appointmentDialog);             AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]             {                 IntroStepAsync,                 ActStepAsync,                 FinalStepAsync,             }));              // The initial child Dialog to run.             InitialDialogId = nameof(WaterfallDialog);         }          private async Job<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)         {             if (!_luisRecognizer.IsConfigured)             {                 await stepContext.Context.SendActivityAsync(                     MessageFactory.Text("Note: LUIS is non configured. To enable all capabilities, add 'LuisAppId', 'LuisApiKey' and 'LuisApiEndpointUrl' to the appsettings.json file.", inputHint: InputHints.IgnoringInput), cancellationToken);                  render wait stepContext.NextAsync(null, cancellationToken);             }              // Use the text provided in FinalStepAsync or the default if it is the beginning time.             var messageText = stepContext.Options?.ToString() ?? "How can I assist you with today?";             var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);             render wait stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);         }          private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)         {             if (!_luisRecognizer.IsConfigured)             {                 // LUIS is non configured, we merely run the BookingDialog path with an empty BookingDetailsInstance.                 return await stepContext.BeginDialogAsync(nameof(AppointmentBookingDialog), new AppointmentDetails(), cancellationToken);             }              // Call LUIS and gather any potential booking details. (Notation the TurnContext has the response to the prompt.)             var luisResult = await _luisRecognizer.RecognizeAsync<DoctorBooking>(stepContext.Context, cancellationToken);             switch (luisResult.TopIntent().intent)             {                 instance DoctorBooking.Intent.BookAppointment:                     var validDoctor = await ValidateDoctors(stepContext.Context, luisResult, cancellationToken);                     if (!validDoctor)                     {                         render await stepContext.ReplaceDialogAsync(InitialDialogId, "Dr. Peter, Susan and Kathy are bachelor?", cancellationToken);                     }                      // Initialize BookingDetails with any entities we may have institute in the response.                     var bookingDetails = new AppointmentDetails()                     {                         // Get destination and origin from the composite entities arrays.                         Dr. = luisResult.Doc,                         AppointmentDate = luisResult.AppointmentDate,                     };                      // Run the AppointmentBookingDialog giving information technology whatever details we have from the LUIS phone call, information technology will fill out the residue.                     return wait stepContext.BeginDialogAsync(nameof(AppointmentBookingDialog), bookingDetails, cancellationToken);                  instance DoctorBooking.Intent.GetAvailableDoctors:                     // We haven't implemented the GetAvailableDoctorsDialog so we only display a mock bulletin.                     var getAvailableDoctorsMessageText = "Doctor Kathy, Md Peter are available today";                     var getAvailableDoctorsMessage = MessageFactory.Text(getAvailableDoctorsMessageText, getAvailableDoctorsMessageText, InputHints.IgnoringInput);                     look stepContext.Context.SendActivityAsync(getAvailableDoctorsMessage, cancellationToken);                     break;                  default:                     // Catch all for unhandled intents                     var didntUnderstandMessageText = $"Sorry, I didn't go that. Please try asking in a different way (intent was {luisResult.TopIntent().intent})";                     var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);                     await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);                     break;             }              return await stepContext.NextAsync(null, cancellationToken);         }          // Shows a alert if the doctor is not specified or doctor entity values tin't exist mapped to a canonical item in the Airport.         individual static async Task<Boolean> ValidateDoctors(ITurnContext context, DoctorBooking luisResult, CancellationToken cancellationToken)         {             var doctorChoosen = luisResult.Dr.;             var noDoctor = string.IsNullOrEmpty(doctorChoosen);              if (noDoctor)             {                 var messageText = "Please choose a dr.";                 var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);                 wait context.SendActivityAsync(message, cancellationToken);             }             render !noDoctor;         }          private async Job<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)         {             // If the child dialog ("AppointmentBookingDialog") was cancelled, the user failed to ostend or if the intent wasn't appointment Booking             // the Result here volition be null.             if (stepContext.Event is AppointmentDetails consequence)             {                 // Now we have all the booking details telephone call the booking service.                  // If the call to the booking service was successful tell the user.                  var timeProperty = new TimexProperty(event.AppointmentDate);                 var appointmentDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now);                 var messageText = $"I have yous booked to Doctor {result.Dr.} on {appointmentDateMsg}";                 var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);                 expect stepContext.Context.SendActivityAsync(message, cancellationToken);             }              // Restart the main dialog with a dissimilar message the second time around             var promptMessage = "What else can I do for you?";             return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);         }     } }                                  

    This diagram gives you lot an overview of what the MainDialog class does.

    A diagram showing how the MainDialog works. First the intro step is executed, then LUIS is called to analyze the user"s response, then based on the intent of LUIS results, the Appointment Booking Dialog is executed, the Other Action Dialog is executed, or the error handler is executed, then the final step is executed.

    It's quite a lot of code, only the of import office of the MainDialog course is below:

                      private async Job<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) {     if (!_luisRecognizer.IsConfigured)     {         // LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.         render await stepContext.BeginDialogAsync(nameof(AppointmentBookingDialog), new AppointmentDetails(), cancellationToken);     }      // Call LUIS and gather whatever potential booking details. (Note the TurnContext has the response to the prompt.)     var luisResult = await _luisRecognizer.RecognizeAsync<DoctorBooking>(stepContext.Context, cancellationToken);     switch (luisResult.TopIntent().intent)     {         case DoctorBooking.Intent.BookAppointment:             var validDoctor = wait ValidateDoctors(stepContext.Context, luisResult, cancellationToken);             if (!validDoctor)             {                 return expect stepContext.ReplaceDialogAsync(InitialDialogId, "Doctor Peter, Susan and Kathy are available?", cancellationToken);             }              // Initialize BookingDetails with whatever entities we may take found in the response.             var bookingDetails = new AppointmentDetails()             {                 // Get destination and origin from the composite entities arrays.                 Doctor = luisResult.Md,                 AppointmenDate = luisResult.AppointmentDate,             };              // Run the AppointmentBookingDialog giving information technology whatever details we have from the LUIS call, it will fill out the remainder.             return expect stepContext.BeginDialogAsync(nameof(AppointmentBookingDialog), bookingDetails, cancellationToken);          case DoctorBooking.Intent.GetAvailableDoctors:             // We oasis't implemented the GetAvailableDoctorsDialog and so we only display a mock message.             var getAvailableDoctorsMessageText = "Dr. Kathy, Md Peter are bachelor today";             var getAvailableDoctorsMessage = MessageFactory.Text(getAvailableDoctorsMessageText, getAvailableDoctorsMessageText, InputHints.IgnoringInput);             wait stepContext.Context.SendActivityAsync(getAvailableDoctorsMessage, cancellationToken);             suspension;          default:             // Take hold of all for unhandled intents             var didntUnderstandMessageText = $"Sorry, I didn't become that. Please attempt asking in a different way (intent was {luisResult.TopIntent().intent})";             var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);             await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);             interruption;     }      return await stepContext.NextAsync(null, cancellationToken); }                                  

    In a nutshell, when a message action is received, the bot runs the MainDialog.

    The MainDialog prompts the user using the IntroStepAsync method, and then calls the ActStepAsync method.

    In the ActStepAsync method, the bot calls the LUIS app to get the luisResult object which will include the user's intent and entities. The user's intent and entities are used to determine the next footstep, either performing validation or invoking other dialogs.

    At the end, the bot calls the FinalStepAsync method to complete or cancel the process.

    Twilio adapter and controller

    By default, the Azure Bot service will connect to the web chat channel which is handled by the default AdapterWithErrorHandler adapter. The default adapter is injected into the default BotController form, and the controller exposes an endpoint /api/messages.

    To connect the bot to Twilio, you will create a new TwilioAdapterWithErrorHandlerclass extended from the TwilioAdapter class. Run the post-obit command to install the Microsoft.Bot.Architect.Adapters.Twilio NuGet package:

                      dotnet add package Microsoft.Bot.Builder.Adapters.Twilio --version 4.15.0                                  

    After the NuGet installation is completed, create the TwilioAdapterWithErrorHandler.cs file in the projection root directory, and add the post-obit code:

                      using Microsoft.Bot.Builder.Adapters.Twilio; using Microsoft.Bot.Builder.TraceExtensions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging;  namespace AppointmentBot {     public class TwilioAdapterWithErrorHandler : TwilioAdapter     {         public TwilioAdapterWithErrorHandler(IConfiguration configuration, ILogger<TwilioAdapter> logger)                 : base(configuration, null, logger)         {             OnTurnError = async (turnContext, exception) =>             {                 // Log whatever leaked exception from the application.                 logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Bulletin}");                  // Transport a message to the user                 await turnContext.SendActivityAsync("The bot encountered an error or issues.");                 await turnContext.SendActivityAsync("To continue to run this bot, delight fix the bot source code.");                  // Send a trace activity, which will be displayed in the Bot Framework Emulator                 await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");             };         }     } }                                  

    To handle the HTTP webhook requests from Twilio, you'll need to add together a TwilloController. Create a new file TwilioController.cs in the Controllers folder, and add the post-obit code:

                      using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Bot.Builder; using Microsoft.Bot.Architect.Adapters.Twilio; using Organisation.Threading; using System.Threading.Tasks;  namespace AppointmentBot.Controllers {     [Route("api/twilio")]     [ApiController]     public class TwilioController : ControllerBase     {         private readonly TwilioAdapter _adapter;         private readonly IBot _bot;          /// <summary>         /// Initializes a new example of the <meet cref="BotController"/> grade.         /// </summary>         /// <param name="adapter">adapter for the BotController.</param>         /// <param name="bot">bot for the BotController.</param>         public TwilioController(TwilioAdapter adapter, IBot bot)         {             _adapter = adapter;             _bot = bot;         }          /// <summary>         /// PostAsync method that returns an async Task.         /// </summary>         /// <returns>A <see cref="Job{TResult}"/> representing the result of the asynchronous performance.</returns>         [HttpPost]         [HttpGet]         public async Task PostAsync()         {             // Delegate the processing of the HTTP POST to the adapter.             // The adapter will invoke the bot.             look _adapter.ProcessAsync(Asking, Response, _bot, default(CancellationToken));         }     } }                                  

    The endpoint for TwilioController is /api/twilio. After adding the new endpoint, the bot tin handle letters via both spider web channel and Twilio SMS channel.

    Finally, you need to register the dialogs and LUIS recognizer in the Startup class. Insert the following lines at the cease of the ConfigureServices method in the Startup.cs file:

                      using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Bot.Builder; using Microsoft.Bot.Architect.Integration.AspNet.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Bot.Connector.Authentication; using Microsoft.Bot.Architect.Adapters.Twilio;  using AppointmentBot.Bots; using AppointmentBot.Dialogs;  namespace AppointmentBot {     public class Startup     {         // This method gets chosen by the runtime. Utilise this method to add services to the container.         public void ConfigureServices(IServiceCollection services)         {             services.AddHttpClient().AddControllers().AddNewtonsoftJson();              // Create the Bot Framework Authentication to be used with the Bot Adapter.             services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();              // Create the Bot Adapter with fault handling enabled.             services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();              // Create the storage nosotros'll be using for User and Conversation state. (Memory is not bad for testing purposes.)             services.AddSingleton<IStorage, MemoryStorage>();              // Create the User state. (Used in this bot's Dialog implementation.)             services.AddSingleton<UserState>();              // Create the Chat land. (Used by the Dialog organisation itself.)             services.AddSingleton<ConversationState>();              // Register LUIS recognizer             services.AddSingleton<AppointmentBookingRecognizer>();              // Annals the AppointmentBookingDialog             services.AddSingleton<AppointmentBookingDialog>();              // The MainDialog that will be run by the bot.             services.AddSingleton<MainDialog>();              // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.             services.AddTransient<IBot, DialogAndWelcomeBot<MainDialog>>();              // Create the Twilio Adapter             services.AddSingleton<TwilioAdapter, TwilioAdapterWithErrorHandler>();         }          // This method gets chosen by the runtime. Use this method to configure the HTTP asking pipeline.         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)         {             if (env.IsDevelopment())             {                 app.UseDeveloperExceptionPage();             }              app.UseDefaultFiles()                 .UseStaticFiles()                 .UseWebSockets()                 .UseRouting()                 .UseAuthorization()                 .UseEndpoints(endpoints =>                 {                     endpoints.MapControllers();                 });              // app.UseHttpsRedirection();         }     } }                                  

    Examination Locally

    You'll need to install the Bot Framework Emulator to exam the bot locally. To Install the Bot Framework Emulator:

    • Navigate to GitHub releases page of the Bot Framework Emulator project
    • Click on the setup file for your OS to download it.
    • After the download is completed, click the file to showtime the installation. Follow the installation sorcerer, and use the default options to complete the installation.

    Next, beginning the bot project using the .Net CLI:

    Now, showtime the Bot Emulator, click on the Open Bot button, and enter the bot's URL in your local environment, which by default, is http://localhost:3978/api/messages. Then click on the Connect button.

    The Bot Emulator showing the Open a bot modal. This modal contains multiple fields, but most importantly the Bot URL field.

    A chat window will exist opened. You tin can blazon the message and start testing.

    The user chats with the bot using the Bot Emulator. The user asks "Can I see doctor Peter?" and the bot responds with "To make your booking please enter an appointment date including time, Day, Month and Year.

    After y'all are happy with test results, the bot can be deployed to Azure.

    Deploy the bot to Azure

    To deploy the .Cyberspace bot to Azure, y'all demand to apply Azure CLI to create the following resources:

    • A resources group: You have already created a rg-bot resources group earlier.
    • A managed identity: Although this resource won't exist used for the Twilio bot, it is required by the Bot Framework deployment templates
    • An App Service plan and an App Service
    • A Bot Service

    To create a new App Service program, run the following command:

                      az appservice plan create -g rg-bot -n asp-bot --location [AZURE_LOCATION] --sku F1                                  

    Here's what the parameters practise:

    • -g or --resource-grouping: The resource group the resource should be placed in, in this instance into the "rg-bot" resource grouping you created earlier.
    • -n or --proper noun: The proper name of the App Service programme, which is asp-bot. "asp" is short for App Service plan.
    • -l or --location: The Azure location the resource should reside in. Replace [AZURE_LOCATION] with the location closest to y'all or your users, like you did when creating the resource grouping earlier.
    • --sku: The size (CPU/RAM/etc.) of the App Service plan by SKU, which in this case is F1 (gratis).

    To make sure the .Cyberspace project will be deployed correctly, y'all'll need to generate a deployment file. The deployment file tin be generated with the command below:

                      az bot prepare-deploy --lang Csharp --code-dir "." --proj-file-path "AppointmentBot.csproj"                                  

    Please note that --code-dir and --proj-file-path need to match together to resolve the path to the project file.

    Create the managed identity using the following control:

                      az identity create --resource-group "rg-bot" --name "identity-appointment-bot"                                  


    Afterwards the command finished, a new "identity-appointment-bot" managed identity has been added in Azure, which will be used to create the new App Service and Bot Service in the adjacent step.

    The App Service and Bot Service tin can be generated using the existing App Service program and the Azure Resources Manager (ARM) template which is function of the "CoreBot" .NET template.

    You lot be using the DeploymentTemplates/template-with-preexisting-rg.json ARM template, but it requires a lot of parameters, which is why you should use a parameter file. Create a new ParameterFiles folder in the projection root and create a new file RegisterAppParams.json with the following contents:

                      {   "$schema": "https://schema.direction.azure.com/schemas/2019-04-01/deploymentParameters.json#",   "contentVersion": "i.0.0.0",   "parameters": {     "appId": {       "value": "[CLIENT_ID_FROM_MANAGED_IDENTITY]"     },     "appType": {       "value": "UserAssignedMSI"     },     "tenantId": {       "value": "[TENANT_ID]"     },     "existingUserAssignedMSIName": {       "value": "identity-date-bot"     },     "existingUserAssignedMSIResourceGroupName": {       "value": "rg-bot"     },     "botId": {       "value": "bs-bot-[UNIQUE_SUFFIX]"     },     "newWebAppName": {       "value": "appointment-bot-[UNIQUE_SUFFIX]"     },     "existingAppServicePlan": {       "value": "asp-bot"     },     "appServicePlanLocation": {       "value": "[AZURE_LOCATION]"     }   } }                                  

    Some parameters have already been configured with the names of the previously created resources, only you lot still demand to update a few with your own specific settings:

    • appId: The value of clientId in the response of the create identity command. Yous tin also query the clientId similar this: az identity bear witness -g rg-bot -n identity-engagement-bot --query clientId
    • tenantId: The value of tenantId in the response of the create identity command. You can also query the tenantId like this: az account show --query tenantId
    • appServicePlanLocation: The Azure region you lot used when creating your App Service plan.
    • botId: The proper name for you Bot Service. This proper noun has to be globally unique. Replace [UNIQUE_SUFFIX] with anything that would make the proper name unique, like "firstname-lastname1234". If it doesn't have the name, change it up and try again.
    • newWebAppName: The proper name for your App Service. This name has to be globally unique because it will be used as a subdomain to azurewebsites.net. Replace [UNIQUE_SUFFIX] with anything that would make the name unique, like "firstname-lastname1234". If it doesn't accept the name, change information technology up and try again.

    Later on the parameter file is updated, run the following control to generate the App Service and Bot Service:

                      az deployment group create `   --resources-group "rg-bot" `   --template-file "./DeploymentTemplates/template-with-preexisting-rg.json" `   --parameters "@ParameterFiles/RegisterAppParams.json"                                  

    Azure will accept a minute to deploy this infrastructure. Subsequently the App Service is generated, run the following control below to get the App Service hostname:

                      az webapp show -k rg-bot -n appointment-bot-[UNIQUE_SUFFIX] --query 'hostNames[0]'                                  

    Supplant [UNIQUE_SUFFIX] with the suffix yous used in the parameters file. Have note of this hostname as you will need it afterward.

    Now that the App Service infrastructure has been provisioned, you can deploy your local .Net bot project. Run the following command which will create a Aught file and deploy the ZIP file to App Service:

                      az webapp up -g rg-bot -n appointment-bot-[UNIQUE_SUFFIX]                                  

    Information technology can take about 30 seconds for the deployment to complete. When information technology is washed, you will see the success response like beneath:

                      {   "agile": true,   "author": "N/A",   "author_email": "N/A",   "complete": true,   "deployer": "ZipDeploy",   "end_time": "2022-02-27T21:58:45.0863404Z",   "id": "b3c4cbb7a470479ebd7a2c6dd17bd70f",   "is_readonly": true,   "is_temp": false,   "last_success_end_time": "2022-02-07T21:58:45.0863404Z",   "log_url": "https://twilio-appointment-bot-app-service.scm.azurewebsites.net/api/deployments/latest/log",   "message": "Created via a button deployment",   "progress": "",   "provisioningState": "Succeeded",   "received_time": "2022-02-27T21:57:25.4159Z",   "site_name": "twilio-appointment-bot-app-service",   "start_time": "2022-02-07T21:57:25.5565272Z",   "status": 4,   "status_text": "",   "url": "https://twilio-engagement-bot-app-service.scm.azurewebsites.net/api/deployments/latest" }                                  

    Setting up Twilio for SMS communication

    You've tested the bot locally using the spider web chat, but the goal of this tutorial is to use SMS to communicate. To receive and send SMS messages, you'll need a Twilio Phone Number.

    • Go and buy a new phone number from Twilio. The cost of the phone number volition be applied to your free promotional credit if you're using a trial account.
      Brand sure to accept note of your new Twilio telephone number. Y'all'll need it later on!
    • If you are using a trial Twilio account, you tin can only transport text messages to Verified Caller IDs. Verify your telephone number or the phone number you want to SMS if information technology isn't on the list of Verified Caller IDs.
    • Lastly, you'll need to find your Twilio Account SID and Auth Token. Navigate to your Twilio account page and take notation of your Twilio Account SID and Auth Token located at the bottom left of the folio.
      Account Info box holding 3 read-only fields: Account SID field, Auth Token field, and Twilio phone number field.

    When your Twilio Phone Number receives a text message, Twilio should forward information technology to your .NET bot hosted on Azure App Service. To configure that, navigate to Telephone numbers > Manage > Active numbers, and click on your Twilio Telephone Number to access the Configure page.

    Twilio"s Active Numbers page which shows a list of phone numbers the user owns. The page currently shows one phone number.

    Under the Messaging section, set the dropdown under CONFIGURE WITH OTHER HANDLERS to "Webhook", and in the adjacent text field, enter "https://", then paste in the App Service hostname you took note of earlier, and and then enter "/api/twilio". The URL should wait like https://your-hostname.azurewebsites.cyberspace/api/twilio.

    The messaging configuration section for the Twilio Phone Number. The form has 3 fields for when a message comes in, a dropdown which is set to "Webhook", a text field which is set to the App Service URL with /api/twilio as path, and another dropdown set to "HTTP POST"

    Click the Save button on the bottom left. Take note of this webhook URL, you lot will demand information technology again soon.

    Lastly, you need to add some configuration to your App Service. Run the following command to configure the app settings:

                      az webapp config appsettings set -g rg-bot -n appointment-bot-[UNIQUE_SUFFIX] --settings `  LuisApiKey=[YOUR_LUIS_API_KEY] `  TwilioNumber=[YOUR_TWILIO_PHONE_NUMBER] `  TwilioAccountSid=[YOUR_TWILIO_ACCOUNT_SID] `  TwilioAuthToken=[YOUR_TWILIO_AUTH_TOKEN] `  TwilioValidationUrl=[YOUR_BOT_TWILIO_ENDPOINT]                                  

    Earlier running the control, replace the placeholders.

    • Replace [YOUR_LUIS_API_KEY] with the LUIS Primary Key you took note of earlier.
    • Replace [YOUR_TWILIO_PHONE_NUMBER] with your Twilio Phone Number yous bought earlier. Enter the phone number using the E.164 which looks like +11234567890.
    • Replace [YOUR_TWILIO_ACCOUNT_SID] with your Twilio Account SID which you lot took note of earlier.
    • Supplant [YOUR_BOT_TWILIO_ENDPOINT] with the webhook URL you took note of earlier. It should wait like https://your-hostname.azurewebsites.cyberspace/api/twilio.

    Y'all can restart the App Service to make sure the app settings are loaded by the bot. Run the post-obit command to restart the App Service:

                      az webapp restart -g rg-bot -n appointment-bot-[UNIQUE_SUFFIX]                                  

    End-to-End Test

    Finally, you have built and assembled all the moving parts. Let's test information technology!

    Ship a text message to your Twilio Phone Number and y'all should see a response from your bot. Here you lot tin see I sent the below SMS messages to my Twilio Phone Number, and it works!

    An SMS conversation between a user and the appointment booking bot where the user successfully books an appointment with doctor Peter.

    Next Steps

    At that place are some important bot features that aren't covered in this article. You lot may like to explore further when developing a production class bot.

    • Authentication: When a bot needs to access resources on behalf of a user, y'all must authenticate the user identity. The user authentication can exist handled by Identity providers such as Azure AD with OAuth two.0. Your bot will apply the token generated by Azure to admission those resource. The details on how to add Azure Advertising authentication to a bot tin be plant at Microsoft's documentation.
    • Bot Framework Composer: It is an open-source visual designer and authoring tool to create a bot with Azure Bot Service. You tin use it to build dialogs in the UI and visualize the flow to business users. It too allows you to train LUIS models within the tool, thus saving the need to switch between different environments. If your projection requires involving non-technical people to bot development, it is definitely a good tool to consider.

    Conclusion

    In this article, you walked through the steps to build an SMS booking arrangement using Twilio, Azure Bot Framework, and LUIS. You could extend this by adding more channels, expanding the LUIS model to support real-life scenarios, and incorporating other features like image recognition or multiple language support.

    Yan Sun is a total stack developer. He loves coding, always learning, writing and sharing. Yan tin can be reached at sunny [at] gmail.com.


    Related Posts

    How to send Emails in C# .NET with FluentEmail and SendGrid

    How to ship Emails in C# .Cyberspace with FluentEmail, Razor, and SendGrid

    May 11, 2022

    Learn how to generate emails in C# .Internet using Razor templates and transport them using FluentEmail and SendGrid.

    How to better configure C# and .NET apps for Twilio

    How to better configure C# and .NET applications for Twilio

    Apr eleven, 2022

    Acquire how to use multiple configuration sources, strongly-typed objects, and implement the options pattern in your .NET applications

    What's new in the Twilio helper library for ASP.NET (v5.73.0 - April 2022)

    What'due south new in the Twilio helper library for ASP.Cyberspace (v5.73.0 - April 2022)

    Apr 06, 2022

    Larn about what's new and one-time with the Twilio helper library for ASP.NET in version 5.73.0

    Send SMS without a Phone Number using C# and Alphanumeric Sender ID

    How to Send SMS without a Phone Number using C# .Internet and an Alphanumeric Sender ID

    April 05, 2022

    You don't always need a telephone number to send SMS! Acquire how to send text letters using Alphanumeric Sender IDs.

    SendGrid APIを使用し、C#と.NET 6でメールを送信する方法

    SendGrid APIを使用し、C#と.NET 6でメールを送信する方法

    Apr 01, 2022

    SendGrid .NET SDKと.Cyberspace 6コンソールアプリケーションを使用してメールを送信する方法をご紹介します。

    Organize Incoming Email Attachments with C# and ASP.NET Core using Twilio SendGrid Inbound Parse

    Organize Incoming Email Attachments with C# and ASP.NET Core using Twilio SendGrid Inbound Parse

    Mar 22, 2022

    Larn how to receive emails in ASP.NET Core and organize attachments using Twilio SendGrid Inbound Parse

    • Azure
    • C#
    • bot
    • AI
    • .NET
    • SMS

denisonmardelis.blogspot.com

Source: https://www.twilio.com/blog/doctor-appointment-bot-with-azure-bot-service-language-understanding-and-twilio-sms

0 Response to "I Didn t Understand Please Try Again Bot Framework"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel