public AdapterWithErrorHandler(ICredentialProvider credentialProvider, ILogger <BotFrameworkHttpAdapter> logger, ConversationState conversationState = null) : base(credentialProvider) { // combine path for cross platform support string[] paths = { ".", "Resources", "AdapterWithErrorHandler.lg" }; string fullPath = Path.Combine(paths); _lgEngine = new TemplateEngine().AddFile(fullPath); OnTurnError = async(turnContext, exception) => { // Log any leaked exception from the application. logger.LogError($"Exception caught : {exception.Message}"); // Send a catch-all apology to the user. await turnContext.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("SomethingWentWrong", exception))); if (conversationState != null) { try { // Delete the conversationState for the current conversation to prevent the // bot from getting stuck in a error-loop caused by being in a bad state. // ConversationState should be thought of as similar to "cookie-state" in a Web pages. await conversationState.DeleteAsync(turnContext); } catch (Exception e) { logger.LogError($"Exception caught on attempting to Delete ConversationState : {e.Message}"); } } }; }
protected override async Task OnMembersAddedAsync(IList <ChannelAccount> membersAdded, ITurnContext <IConversationUpdateActivity> turnContext, CancellationToken cancellationToken) { // Actions to include in the welcome card. These are passed to LG and are then included in the generated Welcome card. var actions = new { actions = new List <Object>() { new { title = "Get an overview", url = "https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0" }, new { title = "Ask a question", url = "https://stackoverflow.com/questions/tagged/botframework" }, new { title = "Learn how to deploy", url = "https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0" } } }; foreach (var member in membersAdded) { // Greet anyone that was not the target (recipient) of this message. // To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details. if (member.Id != turnContext.Activity.Recipient.Id) { await turnContext.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("WelcomeCard", actions))); } } }
private Activity InternalGenerateActivity(string templateName, object data, string locale) { var iLocale = locale == null ? "" : locale; if (TemplateEnginesPerLocale.ContainsKey(iLocale)) { return(ActivityBuilder.GenerateFromLG(TemplateEnginesPerLocale[locale].EvaluateTemplate(templateName, data))); } var locales = new string[] { string.Empty }; if (!LangFallBackPolicy.TryGetValue(iLocale, out locales)) { if (!LangFallBackPolicy.TryGetValue(string.Empty, out locales)) { throw new Exception($"No supported language found for {iLocale}"); } } foreach (var fallBackLocale in locales) { if (TemplateEnginesPerLocale.ContainsKey(fallBackLocale)) { return(ActivityBuilder.GenerateFromLG(TemplateEnginesPerLocale[fallBackLocale].EvaluateTemplate(templateName, data))); } } return(new Activity()); }
private async Task <DialogTurnResult> NameConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { stepContext.Values["name"] = (string)stepContext.Result; // We can send messages to the user at any point in the WaterfallStep. await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("AckName", new { Result = stepContext.Result })), cancellationToken); // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog. return(await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("AgeConfirmPrompt")) }, cancellationToken)); }
private static async Task <DialogTurnResult> TransportStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog. // Running a prompt here means the next WaterfallStep will be run when the users response is received. return(await stepContext.PromptAsync(nameof(ChoicePrompt), new PromptOptions { Prompt = ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("ModeOfTransportPrompt")), Choices = ChoiceFactory.ToChoices(new List <string> { "Car", "Bus", "Bicycle" }), }, cancellationToken)); }
private async Task <DialogTurnResult> ChoiceCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { _logger.LogInformation("MainDialog.ChoiceCardStepAsync"); // Create options for the prompt var options = new PromptOptions() { Prompt = ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("CardChoice")), Choices = new List <Choice>(), }; // Add the choices for the prompt. options.Choices.Add(new Choice() { Value = "Adaptive card" }); options.Choices.Add(new Choice() { Value = "Animation card" }); options.Choices.Add(new Choice() { Value = "Audio card" }); options.Choices.Add(new Choice() { Value = "Hero card" }); options.Choices.Add(new Choice() { Value = "Receipt card" }); options.Choices.Add(new Choice() { Value = "Signin card" }); options.Choices.Add(new Choice() { Value = "Thumbnail card" }); options.Choices.Add(new Choice() { Value = "Video card" }); options.Choices.Add(new Choice() { Value = "All cards" }); return(await stepContext.PromptAsync(nameof(ChoicePrompt), options, cancellationToken)); }
private async Task <DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { stepContext.Values["age"] = (int)stepContext.Result; var msg = _lgGenerator.GenerateActivity("AgeReadBack", new { userAge = stepContext.Values["age"] }, stepContext); // We can send messages to the user at any point in the WaterfallStep. await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(msg), cancellationToken); // WaterfallStep always finishes with the end of the Waterfall or with another dialog, here it is a Prompt Dialog. return(await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = _lgGenerator.GenerateActivity("ConfirmPrompt", stepContext) }, cancellationToken)); }
private async Task <DialogTurnResult> AgeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { if ((bool)stepContext.Result) { // User said "yes" so we will be prompting for the age. // WaterfallStep always finishes with the end of the Waterfall or with another dialog, here it is a Prompt Dialog. var promptOptions = new PromptOptions { Prompt = ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("AskForAge")), RetryPrompt = ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("AskForAge.reprompt")), }; return(await stepContext.PromptAsync(nameof(NumberPrompt <int>), promptOptions, cancellationToken)); } else { // User said "no" so we will skip the next step. Give -1 as the age. return(await stepContext.NextAsync(-1, cancellationToken)); } }
private async Task <DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { // If the child dialog ("BookingDialog") was cancelled or the user failed to confirm, the Result here will be null. if (stepContext.Result != null) { var result = (BookingDetails)stepContext.Result; // Now we have all the booking details call the booking service. // If the call to the booking service was successful tell the user. var timeProperty = new TimexProperty(result.TravelDate); result.travelDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now); await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("BookingConfirmation", result)), cancellationToken); } else { await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank you."), cancellationToken); } return(await stepContext.EndDialogAsync(cancellationToken : cancellationToken)); }
/// <summary> /// Create an activity through Language Generation using the thread culture or provided override. /// </summary> /// <param name="templateName">Langauge Generation template.</param> /// <param name="data">Data for Language Generation to use during response generation.</param> /// <param name="localeOverride">Optional override for locale.</param> /// <returns>Activity.</returns> public Activity GenerateActivityForLocale(string templateName, object data = null, string localeOverride = null) { if (templateName == null) { throw new ArgumentNullException(nameof(templateName)); } // By default we use the locale for the current culture, if a locale is provided then we ignore this. var locale = localeOverride ?? CultureInfo.CurrentUICulture.Name; // Do we have a template engine for this locale? if (TemplateEnginesPerLocale.ContainsKey(locale)) { return(ActivityBuilder.GenerateFromLG(TemplateEnginesPerLocale[locale].EvaluateTemplate(templateName, data))); } else { // We don't have a set of matching responses for this locale so we apply fallback policy to find options. languageFallbackPolicy.TryGetValue(locale, out string[] locales); { // If no fallback options were found then we fallback to the default and log. if (!languageFallbackPolicy.TryGetValue(localeDefault, out locales)) { throw new Exception($"No LG responses found for {locale} or when attempting to fallback to '{localeDefault}'"); } } // Work through the fallback hierarchy to find a response foreach (var fallBackLocale in locales) { if (TemplateEnginesPerLocale.ContainsKey(fallBackLocale)) { return(ActivityBuilder.GenerateFromLG(TemplateEnginesPerLocale[fallBackLocale].EvaluateTemplate(templateName, data))); } } } throw new Exception($"No LG responses found for {locale} or when attempting to fallback to '{localeDefault}'"); }
public AdapterWithErrorHandler(ICredentialProvider credentialProvider, ILogger <BotFrameworkHttpAdapter> logger, IStorage storage, UserState userState, ConversationState conversationState, IConfiguration configuration) : base(credentialProvider) { this.UseStorage(storage); this.UseState(userState, conversationState); this.Use(new RegisterClassMiddleware <IActivityGenerator>(new ActivityBuilder())); this.UseDebugger(configuration.GetValue <int>("debugport", 4712)); string[] paths = { ".", "AdapterWithErrorHandler.lg" }; string fullPath = Path.Combine(paths); _lgEngine = new TemplateEngine().AddFile(fullPath); OnTurnError = async(turnContext, exception) => { // Log any leaked exception from the application. logger.LogError($"Exception caught : {exception.Message}"); // Send a catch-all apology to the user. await turnContext.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("SomethingWentWrong", exception))); if (conversationState != null) { try { // Delete the conversationState for the current conversation to prevent the // bot from getting stuck in a error-loop caused by being in a bad state. // ConversationState should be thought of as similar to "cookie-state" in a Web pages. await conversationState.DeleteAsync(turnContext); } catch (Exception e) { logger.LogError($"Exception caught on attempting to Delete ConversationState : {e.Message}"); } } }; }
private async Task <DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { if ((bool)stepContext.Result) { // Get the current profile object from user state. var userProfile = await _userProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken); userProfile.Transport = (string)stepContext.Values["transport"]; userProfile.Name = (string)stepContext.Values["name"]; userProfile.Age = (int)stepContext.Values["age"]; var msg = _lgGenerator.GenerateActivity("SummaryReadout", userProfile, stepContext); await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(msg), cancellationToken); } else { await stepContext.Context.SendActivityAsync(_lgGenerator.GenerateActivity("NoProfileReadBack", stepContext), cancellationToken); } // WaterfallStep always finishes with the end of the Waterfall or with another dialog, here it is the end. return(await stepContext.EndDialogAsync(cancellationToken : cancellationToken)); }
private async Task <DialogTurnResult> InitialStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { var bookingDetails = (BookingDetails)stepContext.Options; var timex = bookingDetails.TravelDate; var promptMsg = _lgEngine.EvaluateTemplate("PromptForTravelDate"); var repromptMsg = _lgEngine.EvaluateTemplate("InvalidDateReprompt"); if (timex == null) { // We were not given any date at all so prompt the user. return(await stepContext.PromptAsync(nameof(DateTimePrompt), new PromptOptions { Prompt = ActivityBuilder.GenerateFromLG(promptMsg), RetryPrompt = ActivityBuilder.GenerateFromLG(repromptMsg) }, cancellationToken)); } else { // We have a Date we just need to check it is unambiguous. var timexProperty = new TimexProperty(timex); if (!timexProperty.Types.Contains(Constants.TimexTypes.Definite)) { // This is essentially a "reprompt" of the data we were given up front. return(await stepContext.PromptAsync(nameof(DateTimePrompt), new PromptOptions { Prompt = ActivityBuilder.GenerateFromLG(repromptMsg) }, cancellationToken)); } else { return(await stepContext.NextAsync(new DateTimeResolution { Timex = timex }, cancellationToken)); } } }
private async Task <DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(Configuration["LuisAppId"]) || string.IsNullOrEmpty(Configuration["LuisAPIKey"]) || string.IsNullOrEmpty(Configuration["LuisAPIHostName"])) { await stepContext.Context.SendActivityAsync( MessageFactory.Text("NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' to the appsettings.json file."), cancellationToken); return(await stepContext.NextAsync(null, cancellationToken)); } else { return(await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("IntroPrompt")) }, cancellationToken)); } }
private async Task <DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { var bookingDetails = (BookingDetails)stepContext.Options; bookingDetails.TravelDate = (string)stepContext.Result; return(await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("PromptForMissingInformation", bookingDetails)) }, cancellationToken)); }
private async Task <DialogTurnResult> OriginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { var bookingDetails = (BookingDetails)stepContext.Options; bookingDetails.Destination = (string)stepContext.Result; if (bookingDetails.Origin == null) { return(await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("PromptForMissingInformation", bookingDetails)) }, cancellationToken)); } else { return(await stepContext.NextAsync(bookingDetails.Origin, cancellationToken)); } }
private static async Task <DialogTurnResult> NameStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { stepContext.Values["transport"] = ((FoundChoice)stepContext.Result).Value; return(await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("AskForName")) }, cancellationToken)); }
private async Task <DialogTurnResult> ShowCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { _logger.LogInformation("MainDialog.ShowCardStepAsync"); // Reply to the activity we received with an activity. var reply = stepContext.Context.Activity.CreateReply(); // Cards are sent as Attachments in the Bot Framework. // So we need to create a list of attachments on the activity. reply.Attachments = new List <Attachment>(); // Get the text from the activity to use to show the correct card var text = stepContext.Context.Activity.Text.ToLowerInvariant().Split(' ')[0]; // Decide which type of card(s) we are going to show the user if (text.StartsWith("hero")) { // Display a HeroCard. await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("HeroCard"))); } else if (text.StartsWith("thumb")) { // Display a ThumbnailCard. await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("ThumbnailCard"))); } else if (text.StartsWith("sign")) { // Display a SignInCard. await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("SigninCard"))); } else if (text.StartsWith("animation")) { // Display an AnimationCard. await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("AnimationCard"))); } else if (text.StartsWith("video")) { // Display a VideoCard await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("VideoCard"))); } else if (text.StartsWith("audio")) { // Display an AudioCard await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("AudioCard"))); } else if (text.StartsWith("receipt")) { // Display a ReceiptCard. reply.Attachments.Add(Cards.GetReceiptCard().ToAttachment()); // Send the card(s) to the user as an attachment to the activity await stepContext.Context.SendActivityAsync(reply, cancellationToken); } else if (text.StartsWith("adaptive")) { await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("AdaptiveCard"))); } else { // Display a carousel of all the rich card types. await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("AllCards"))); } // Give the user instructions about what to do next await stepContext.Context.SendActivityAsync(ActivityBuilder.GenerateFromLG(_lgEngine.EvaluateTemplate("CardStartOverResponse")), cancellationToken); return(await stepContext.EndDialogAsync()); }