/// <summary> /// Prompt for Restaurant to book. /// </summary> /// <param name="sc">Waterfall Step Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>Dialog Turn Result.</returns> private async Task <DialogTurnResult> AskForRestaurant(WaterfallStepContext sc, CancellationToken cancellationToken) { var state = await Accessor.GetAsync(sc.Context); var reservation = state.Booking; if (reservation.Location != null) { return(await sc.NextAsync(sc.Values, cancellationToken)); } // Reset the dialog if the user hasn't confirmed the reservation. if (!reservation.Confirmed) { state.Booking = CreateNewReservationInfo(); return(await sc.ReplaceDialogAsync(Id, cancellationToken : cancellationToken)); } // Prompt for restaurant var restaurants = SeedReservationSampleData.GetListOfRestaurants(reservation.Category, "London", _urlResolver); state.Restaurants = restaurants; var restaurantOptionsForSpeak = new StringBuilder(); for (var i = 0; i < restaurants.Count; i++) { restaurantOptionsForSpeak.Append(restaurants[i].Name); restaurantOptionsForSpeak.Append(i == restaurants.Count - 2 ? $" {BotStrings.Or} " : ", "); } var restaurantResponse = RestaurantBookingSharedResponses.BookRestaurantRestaurantSelectionPrompt; var tokens = new StringDictionary { { "RestaurantCount", (restaurants.Count - 1).ToString() }, { "ServerUrl", _urlResolver.ServerUrl }, { "RestaurantList", restaurantOptionsForSpeak.ToString() } }; var cardData = new List <TitleImageTextButtonCardData>(); restaurants.ForEach(r => cardData.Add( new TitleImageTextButtonCardData { ImageUrl = r.PictureUrl, ImageSize = AdaptiveImageSize.Stretch, ImageAlign = AdaptiveHorizontalAlignment.Stretch, ButtonTitle = r.Name, ButtonSubtitle = r.Location, SelectedItemData = r.Name })); var reply = sc.Context.Activity.CreateAdaptiveCardGroupReply( restaurantResponse, @"Dialogs\RestaurantBooking\Resources\Cards\TitleImageTextButton.json", AttachmentLayoutTypes.Carousel, cardData, ResponseBuilder, tokens); return(await sc.PromptAsync(Actions.RestaurantPrompt, new PromptOptions { Prompt = reply }, cancellationToken)); }
/// <summary> /// Prompt for the Food type if not already provided on the initial utterance. /// </summary> /// <param name="sc">Waterfall Step Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>Dialog Turn Result.</returns> private async Task <DialogTurnResult> AskForFoodTypeAsync(WaterfallStepContext sc, CancellationToken cancellationToken) { var state = await ConversationStateAccessor.GetAsync(sc.Context, cancellationToken : cancellationToken); var reservation = state.Booking; // If we already have a Cuisine provided we skip to next step if (reservation.Category != null) { return(await sc.NextAsync(sc.Values, cancellationToken)); } // Fixed test data provided at this time var foodTypes = SeedReservationSampleData .GetListOfDefaultFoodTypes() .Select( r => new FoodTypeInfo { TypeName = r.Category, ImageUrl = BotImageForFoodType(r.Category) }).ToList(); var tokens = new Dictionary <string, object> { { "FoodTypeList", foodTypes.ToSpeechString(TemplateManager.GetString(BotStrings.Or), f => f.TypeName) } }; state.Cuisine = foodTypes; var cards = new List <Card>(); var options = new PromptOptions() { Choices = new List <Choice>(), }; foreach (var foodType in foodTypes) { cards.Add(new Card( GetDivergedCardName(sc.Context, "CuisineChoiceCard"), new CuisineChoiceCardData { ImageUrl = foodType.ImageUrl, ImageSize = AdaptiveImageSize.Stretch, ImageAlign = AdaptiveHorizontalAlignment.Stretch, Cuisine = foodType.TypeName, })); options.Choices.Add(new Choice(foodType.TypeName)); } var replyMessage = TemplateManager.GenerateActivity( RestaurantBookingSharedResponses.BookRestaurantFoodSelectionPrompt, cards, tokens); // Prompt for restaurant choice return(await sc.PromptAsync(Actions.AskForFoodType, new PromptOptions { Prompt = replyMessage, Choices = options.Choices }, cancellationToken)); }
/// <summary> /// Prompt for the Food type if not already provided on the initial utterance. /// </summary> /// <param name="sc">Waterfall Step Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>Dialog Turn Result.</returns> private async Task <DialogTurnResult> AskForFoodType(WaterfallStepContext sc, CancellationToken cancellationToken) { var state = await Accessor.GetAsync(sc.Context); var reservation = state.Booking; // If we already have a Cuisine provided we skip to next step if (reservation.Category != null) { return(await sc.NextAsync(sc.Values, cancellationToken)); } // Fixed test data provided at this time var foodTypes = SeedReservationSampleData .GetListOfDefaultFoodTypes() .Select( r => new FoodTypeInfo { TypeName = r.Category, ImageUrl = BotImageForFoodType(r.Category) }).ToList(); var tokens = new StringDictionary { { "FoodTypeList", foodTypes.ToSpeechString(BotStrings.Or, f => f.TypeName) } }; state.Cuisine = foodTypes; // Create a card for each restaurant var cardsData = new List <TitleImageTextButtonCardData>(); foodTypes.ForEach(ft => cardsData.Add( new TitleImageTextButtonCardData { ImageUrl = ft.ImageUrl, ImageSize = AdaptiveImageSize.Stretch, ImageAlign = AdaptiveHorizontalAlignment.Stretch, ButtonTitle = ft.TypeName, SelectedItemData = ft.TypeName })); var reply = sc.Context.Activity.CreateAdaptiveCardGroupReply( RestaurantBookingSharedResponses.BookRestaurantFoodSelectionPrompt, @"Dialogs\RestaurantBooking\Resources\Cards\TitleImageTextButton.json", AttachmentLayoutTypes.Carousel, cardsData, ResponseBuilder, tokens); // Prompt for restaurant choice return(await sc.PromptAsync(Actions.AskForFoodType, new PromptOptions { Prompt = reply }, cancellationToken)); }
private async Task <bool> ValidateRestaurantSelection(PromptValidatorContext <FoundChoice> promptContext, CancellationToken cancellationToken) { var state = await ConversationStateAccessor.GetAsync(promptContext.Context, cancellationToken : cancellationToken); // This is a workaround to a known issue with Adaptive Card button responses and prompts whereby the "Text" of the adaptive card button response // is put into a Value object not the Text as expected causing prompt validation to fail. // If the prompt was about to fail and the Value property is set with Text set to NULL we do special handling. if (!promptContext.Recognized.Succeeded && (promptContext.Context.Activity.Value != null) && string.IsNullOrEmpty(promptContext.Context.Activity.Text)) { dynamic value = promptContext.Context.Activity.Value; string promptResponse = value["selectedItem"]; // The property will be named after your choice set's ID if (!string.IsNullOrEmpty(promptResponse)) { // Override what the prompt has done promptContext.Recognized.Succeeded = true; var foundChoice = new FoundChoice { Value = promptResponse }; promptContext.Recognized.Value = foundChoice; } } if (promptContext.Recognized.Succeeded) { var restaurants = SeedReservationSampleData.GetListOfRestaurants(state.Booking.Category, "London", _urlResolver); var restaurant = restaurants.First(r => r.Name == promptContext.Recognized.Value.Value); if (restaurant != null) { state.Booking.BookingPlace = restaurant; var reply = TemplateManager.GenerateActivity(RestaurantBookingSharedResponses.BookRestaurantBookingPlaceSelectionEcho, new Dictionary <string, object> { { "BookingPlaceName", restaurant.Name } }); await promptContext.Context.SendActivityAsync(reply, cancellationToken); return(true); } } return(false); }
/// <summary> /// Prompt for Restaurant to book. /// </summary> /// <param name="sc">Waterfall Step Context.</param> /// <param name="cancellationToken">Cancellation Token.</param> /// <returns>Dialog Turn Result.</returns> private async Task <DialogTurnResult> AskForRestaurantAsync(WaterfallStepContext sc, CancellationToken cancellationToken) { var state = await ConversationStateAccessor.GetAsync(sc.Context, cancellationToken : cancellationToken); var reservation = state.Booking; if (state.IsAction) { return(await sc.NextAsync(cancellationToken : cancellationToken)); } // Reset the dialog if the user hasn't confirmed the reservation. if (!reservation.Confirmed) { state.Booking = CreateNewReservationInfo(); return(await sc.EndDialogAsync(cancellationToken : cancellationToken)); } // Prompt for restaurant var restaurants = SeedReservationSampleData.GetListOfRestaurants(reservation.Category, "London", _urlResolver); state.Restaurants = restaurants; var restaurantOptionsForSpeak = new StringBuilder(); for (var i = 0; i < restaurants.Count; i++) { restaurantOptionsForSpeak.Append(restaurants[i].Name); restaurantOptionsForSpeak.Append(i == restaurants.Count - 2 ? $" {TemplateManager.GetString(BotStrings.Or)} " : ", "); } var tokens = new Dictionary <string, object> { { "RestaurantCount", restaurants.Count.ToString() }, { "ServerUrl", _urlResolver.ServerUrl }, { "RestaurantList", restaurantOptionsForSpeak.ToString() } }; var cards = new List <Card>(); var options = new PromptOptions() { Choices = new List <Choice>(), }; foreach (var restaurant in restaurants) { cards.Add(new Card( GetDivergedCardName(sc.Context, "RestaurantChoiceCard"), new RestaurantChoiceCardData { ImageUrl = restaurant.PictureUrl, ImageSize = AdaptiveImageSize.Stretch, ImageAlign = AdaptiveHorizontalAlignment.Stretch, Name = restaurant.Name, Title = restaurant.Name, Location = restaurant.Location, SelectedItemData = restaurant.Name })); options.Choices.Add(new Choice(restaurant.Name)); } var replyMessage = TemplateManager.GenerateActivity(RestaurantBookingSharedResponses.BookRestaurantRestaurantSelectionPrompt, cards, tokens); return(await sc.PromptAsync(Actions.RestaurantPrompt, new PromptOptions { Prompt = replyMessage, Choices = options.Choices }, cancellationToken)); }