private async Task <DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { // If the child dialog ("BookingDialog") was cancelled, the user failed to confirm or if the intent wasn't BookFlight // the Result here will be null. if (stepContext.Result is BookingDetails 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); var travelDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now); var messageText = $"I have you booked to {result.Destination} from {result.Origin} on {travelDateMsg}"; var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput); await stepContext.Context.SendActivityAsync(message, cancellationToken); } // Restart the main dialog with a different message the second time around var promptMessage = "What else can I do for you?"; return(await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, 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); var travelDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now); var msg = $"I have you booked to {result.Destination} from {result.Origin} on {travelDateMsg}"; await stepContext.Context.SendActivityAsync(MessageFactory.Text(msg), cancellationToken); } else { await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank you."), cancellationToken); } return(await stepContext.EndDialogAsync(cancellationToken : cancellationToken)); }
protected async Task DigestLuisResult(DialogContext dc, ReservationLuis luisResult) { try { var state = await ConversationStateAccessor.GetAsync(dc.Context); // Extract entities and store in state here. if (luisResult != null) { var entities = luisResult.Entities; // Extract the cuisines out (already normalized to canonical form) and put in State thus slot-filling for the dialog. if (entities.cuisine != null) { foreach (var cuisine in entities.cuisine) { var type = cuisine.First <string>(); state.Booking.Category = type; } } if (entities.datetime != null) { var results = DateTimeRecognizer.RecognizeDateTime(dc.Context.Activity.Text, CultureInfo.CurrentUICulture.ToString()); if (results.Count > 0) { // We only care about presence of one DateTime var result = results.First(); // The resolution could include two example values: one for AM and one for PM. var distinctTimexExpressions = new HashSet <string>(); var values = (List <Dictionary <string, string> >)result.Resolution["values"]; foreach (var value in values) { // Each result includes a TIMEX expression that captures the inherent date but not time ambiguity. // We are interested in the distinct set of TIMEX expressions. if (value.TryGetValue("timex", out var timex)) { distinctTimexExpressions.Add(timex); } } // Now we have the timex properties let's see if we have a definite date and time // If so we slot-fill this and move on, if we don't we'll ignore for now meaning the user will be prompted var timexProperty = new TimexProperty(distinctTimexExpressions.First()); if (timexProperty.Types.Contains(Constants.TimexTypes.Date) && timexProperty.Types.Contains(Constants.TimexTypes.Definite)) { // We have definite date (no ambiguity) state.Booking.ReservationDate = new DateTime(timexProperty.Year.Value, timexProperty.Month.Value, timexProperty.DayOfMonth.Value); // Timex doesn't capture time ambiguity (e.g. 4 rather than 4pm) if (timexProperty.Types.Contains(Constants.TimexTypes.Time)) { // If we have multiple TimeX if (distinctTimexExpressions.Count == 1) { // We have definite time (no ambiguity) state.Booking.ReservationTime = DateTime.Parse($"{timexProperty.Hour.Value}:{timexProperty.Minute.Value}:{timexProperty.Second.Value}"); } else { // We don't have a distinct time so add the TimeEx expressions to enable disambiguation later and prepare the natural language versions foreach (var timex in distinctTimexExpressions) { var property = new TimexProperty(timex); state.AmbiguousTimexExpressions.Add(timex, property.ToNaturalLanguage(DateTime.Now)); } } } } else if (timexProperty.Types.Contains(Constants.TimexTypes.Time)) { // We might have a time but no date (e.g. book a table for 4pm) // If we have multiple timex (and time) this means we have a AM and PM component (e.g. ambiguous - book a table at 9) if (distinctTimexExpressions.Count == 1) { state.Booking.ReservationTime = DateTime.Parse($"{timexProperty.Hour.Value}:{timexProperty.Minute.Value}:{timexProperty.Second.Value}"); } } else { // We don't have a distinct time so add the TimeEx expressions to enable disambiguation later and prepare the natural language versions foreach (var timex in distinctTimexExpressions) { var property = new TimexProperty(timex); state.AmbiguousTimexExpressions.Add(timex, property.ToNaturalLanguage(DateTime.Now)); } } } } if (entities.geographyV2 != null) { state.Booking.Location = entities.geographyV2.First().Location; } // Establishing attendee count can be problematic as the number entity can be picked up for poorly qualified // times, e.g. book a restaurant tomorrow at 2 for 4 people so we rely on a composite entity if (entities.attendees != null) { var attendeesComposite = entities.attendees.First(); if (attendeesComposite != null) { int.TryParse(attendeesComposite.number.First().ToString(), out var attendeeCount); if (attendeeCount > 0) { state.Booking.AttendeeCount = attendeeCount; } } } } } catch { // put log here } }
public BookTable() : base("BookTable") { var promptOptions = new ChoicePromptOptions { Choices = new List <Choice> { new Choice { Value = "Seattle" }, new Choice { Value = "Bellevue" }, new Choice { Value = "Renton" }, } }; //Dialogs.Add("textPrompt", new TextPrompt()); Dialogs.Add("choicePrompt", new ChoicePrompt(Culture.English) { Style = Microsoft.Bot.Builder.Prompts.ListStyle.Auto }); Dialogs.Add("numberPrompt", new NumberPrompt <int>(Culture.English)); Dialogs.Add("timexPrompt", new TimexPrompt(Culture.English, TimexValidator)); Dialogs.Add("confirmationPrompt", new ConfirmPrompt(Culture.English)); Dialogs.Add("BookTable", new WaterfallStep[] { async(dc, args, next) => { dc.ActiveDialog.State = new Dictionary <string, object>(); // await dc.Prompt("textPrompt", "Sure. I can help with that. What City?"); await dc.Prompt("choicePrompt", "Which of our locations would you like?", promptOptions); }, async(dc, args, next) => { var choiceResult = (FoundChoice)args["Value"]; dc.ActiveDialog.State["bookingLocation"] = choiceResult.Value; await dc.Prompt("timexPrompt", $"Ok. I have {dc.ActiveDialog.State["bookingLocation"]} When would you like to arrive? (We open at 4PM.)", new PromptOptions { RetryPromptString = "Sorry, we only accept reservations for the next two weeks, 4PM-8PM" }); }, async(dc, args, next) => { var timexResult = (TimexResult)args; var timexResolution = timexResult.Resolutions.First(); var timexProperty = new TimexProperty(timexResolution.ToString()); var bookingDateTime = $"{timexProperty.ToNaturalLanguage(DateTime.Now)}"; dc.ActiveDialog.State["bookingDateTime"] = bookingDateTime; await dc.Prompt("numberPrompt", $"Ok. I have {dc.ActiveDialog.State["bookingDateTime"]}. How many in your party?"); }, async(dc, args, next) => { dc.ActiveDialog.State["bookingGuestCount"] = args["Value"]; var dialogState = dc.ActiveDialog.State; await dc.Prompt("confirmationPrompt", $"Thanks, Should I go ahead and book a table for {dialogState["bookingGuestCount"].ToString()} guests at our {dialogState["bookingLocation"].ToString()} location for {dialogState["bookingDateTime"].ToString()} ?"); }, async(dc, args, next) => { var dialogState = dc.ActiveDialog.State; // TODO: Verify user said yes to confirmation prompt // TODO: book the table! await dc.Context.SendActivity($"Thanks, I have {dialogState["bookingGuestCount"].ToString()} guests booked for our {dialogState["bookingLocation"].ToString()} location for {dialogState["bookingDateTime"].ToString()}."); } } ); }
public BookTable() : base("BookTable") { var promptOptions = new ChoicePromptOptions { Choices = new List <Choice> { new Choice { Value = "Seattle" }, new Choice { Value = "Bellevue" }, new Choice { Value = "Renton" }, } }; //Dialogs.Add("textPrompt", new TextPrompt()); Dialogs.Add("choicePrompt", new ChoicePrompt(Culture.English) { Style = Microsoft.Bot.Builder.Prompts.ListStyle.Auto }); Dialogs.Add("numberPrompt", new NumberPrompt <int>(Culture.English)); Dialogs.Add("timexPrompt", new TimexPrompt(Culture.English, TimexValidator)); Dialogs.Add("confirmationPrompt", new ConfirmPrompt(Culture.English)); Dialogs.Add("BookTable", new WaterfallStep[] { async(dc, args, next) => { dc.ActiveDialog.State = new Dictionary <string, object>(); IDictionary <string, object> state = dc.ActiveDialog.State; // add any LUIS entities to active dialog state. if (args.ContainsKey("luisResult")) { cafeLUISModel lResult = (cafeLUISModel)args["luisResult"]; updateContextWithLUIS(lResult, ref state); } // prompt if we do not already have cafelocation if (state.ContainsKey("cafeLocation")) { state["bookingLocation"] = state["cafeLocation"]; await next(); } else { await dc.Prompt("choicePrompt", "Which of our locations would you like?", promptOptions); } }, async(dc, args, next) => { var state = dc.ActiveDialog.State; if (!state.ContainsKey("cafeLocation")) { var choiceResult = (FoundChoice)args["Value"]; state["bookingLocation"] = choiceResult.Value; } bool promptForDateTime = true; if (state.ContainsKey("datetime")) { // validate timex var inputdatetime = new string[] { (string)state["datetime"] }; var results = evaluateTimeX((string[])inputdatetime); if (results.Count != 0) { var timexResolution = results.First().TimexValue; var timexProperty = new TimexProperty(timexResolution.ToString()); var bookingDateTime = $"{timexProperty.ToNaturalLanguage(DateTime.Now)}"; state["bookingDateTime"] = bookingDateTime; promptForDateTime = false; } } // prompt if we do not already have date and time if (promptForDateTime) { await dc.Prompt("timexPrompt", "When would you like to arrive? (We open at 4PM.)", new PromptOptions { RetryPromptString = "We only accept reservations for the next 2 weeks and in the evenings between 4PM - 8PM" }); } else { await next(); } }, async(dc, args, next) => { var state = dc.ActiveDialog.State; if (!state.ContainsKey("datetime")) { var timexResult = (TimexResult)args; var timexResolution = timexResult.Resolutions.First(); var timexProperty = new TimexProperty(timexResolution.ToString()); var bookingDateTime = $"{timexProperty.ToNaturalLanguage(DateTime.Now)}"; state["bookingDateTime"] = bookingDateTime; } // prompt if we already do not have party size if (state.ContainsKey("partySize")) { state["bookingGuestCount"] = state["partySize"]; await next(); } else { await dc.Prompt("numberPrompt", "How many in your party?"); } }, async(dc, args, next) => { var state = dc.ActiveDialog.State; if (!state.ContainsKey("partySize")) { state["bookingGuestCount"] = args["Value"]; } await dc.Prompt("confirmationPrompt", $"Thanks, Should I go ahead and book a table for {state["bookingGuestCount"].ToString()} guests at our {state["bookingLocation"].ToString()} location for {state["bookingDateTime"].ToString()} ?"); }, async(dc, args, next) => { var dialogState = dc.ActiveDialog.State; // TODO: Verify user said yes to confirmation prompt // TODO: book the table! await dc.Context.SendActivity($"Thanks, I have {dialogState["bookingGuestCount"].ToString()} guests booked for our {dialogState["bookingLocation"].ToString()} location for {dialogState["bookingDateTime"].ToString()}."); } } ); }
private async Task <DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { // If the child dialog ("BookingDialog") was cancelled, the user failed to confirm or if the intent wasn't BookFlight // the Result here will be null. if (stepContext.Result is BookingDetails 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); var travelDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now); var messageText = $"I have you booked to {result.Destination} from {result.Origin} on {travelDateMsg}"; var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput); var model = new FlightitineraryModel { Places = new Cards.Place[] { new Cards.Place { Name = result.Origin, Code = result.Origin, Id = 13554, Type = "Airport" }, new Cards.Place { Name = result.Destination, Code = result.Destination, Id = 11235, Type = "Airport" } }, Segments = new Segment[] { new Segment { Id = 1, ArrivalDateTime = DateTime.Parse(result.TravelDate), DepartureDateTime = DateTime.Parse(result.TravelDate), OriginStation = 13554, DestinationStation = 11235 }, new Segment { Id = 2, ArrivalDateTime = DateTime.Parse(result.TravelDate), DepartureDateTime = DateTime.Parse(result.TravelDate), OriginStation = 11235, DestinationStation = 13554 } }, Query = new Query { DestinationPlace = "11235", OriginPlace = "13554", InboundDate = result.TravelDate, OutboundDate = result.TravelDate, LocationSchema = "Default", CabinClass = "Economy", GroupPricing = false, Country = "GB", Currency = "GBP", Locale = "en-gb", Adults = 3, Children = 0, Infants = 0, }, BookingOptions = new Bookingoption[] { new Bookingoption { BookingItems = new Bookingitem[] { new Bookingitem { Price = new Random().Next(200, 5000), SegmentIds = new int[] { 1, 2 } } } } } }; var confirmationCard = CreateBookingConfimrationCardAttachment(model); message.Attachments.Add(confirmationCard); await stepContext.Context.SendActivityAsync(message, cancellationToken); } // Restart the main dialog with a different message the second time around var promptMessage = "What else can I do for you?"; return(await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken)); }