I have an enum that I am displaying to user for selection. Once user selects any option user should be asked to provide value for the selected option. Once value is provided another confirmation should come asking if user wants to provide more selections. If yes then same enum dialog/prompt should be displayed as earlier. If no then next operation should continue.
I have got the enum dialog and User can make selection as well but now I dont know what should be the approach to request for value and then where to save it and then prompt for confirmation to continue with displaying again the enum dialog.
public enum OrderSearchOptions
{
[Describe(Description = "Item Number")]
[Prompt("Please provide {&}?")]
ItemNumber,
[Describe(Description = "Location")]
Location,
[Describe(Description = "Country")]
Issuer,
[Describe(Description = "Include Breakable")]
IncludeBreakable,
Status,
[Describe(Description = "Packaging Requirement")]
}
[Serializable]
public class OrderAdvanceStepSearchQuery
{
[Prompt("Please choose search attribute to start the search operation {||}")]
public OrderSearchOptions? SearchOptions { get; set; }
}
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync($"Welcome to the Order helper!");
var OrderFormDialog = FormDialog.FromForm(BuildOrderAdvanceStepSearchForm, FormOptions.PromptInStart);
context.Call(OrderFormDialog, ResumeAfterOrdersFormDialog);
}
private IForm<OrderAdvanceStepSearchQuery> BuildOrderAdvanceStepSearchForm()
{
return new FormBuilder<OrderAdvanceStepSearchQuery>()
.Build();
}
private async Task ResumeAfterRequiredDWPsFormDialog(IDialogContext context, IAwaitable<OrderAdvanceStepSearchQuery> result)
{
try
{
var searchQuery = await result;
await context.PostAsync($"Ok. Searching for Orders...");
var count = 100;
if (count > 1)
{
await context.PostAsync($"I found total of 100 Orders");
await context.PostAsync($"To get Order details, you will need to provide more info...");
}
else
{
await context.PostAsync($"I found the Order you were looking for...");
}
}
catch (FormCanceledException ex)
{
string reply;
if (ex.InnerException == null)
{
reply = "You have canceled the operation. Quitting from the Required DWP Search";
}
else
{
reply = $"Oops! Something went wrong :( Technical Details: {ex.InnerException.Message}";
}
await context.PostAsync(reply);
}
finally
{
context.Done<object>(null);
}
}
Following code using IDialog approach.
public enum OrderSearchOptions
{
[Describe(Description = "Item Number")]
[Prompt("Please provide {&}?")]
ItemNumber,
[Describe(Description = "Location")]
Location,
[Describe(Description = "Country")]
Issuer,
[Describe(Description = "Include Breakable")]
IncludeBreakable,
Status,
[Describe(Description = "Packaging Requirement")]
}
public enum Confirmation
{
Yes,
No
}
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync($"Welcome to the Order helper!");
PromptDialog.Choice(
context: context,
resume: ResumeAfterOrderSearchAttributeSelection,
options: Enum.GetValues(typeof(OrderSearchOptions)).Cast<OrderSearchOptions>().ToArray(),
prompt: "Please select any Order search attribute to start the search operation:",
retry: "I didn't understand. Please try again.");
}
private async Task ResumeAfterOrderSearchAttributeSelection(IDialogContext context, IAwaitable<OrderSearchOptions> result)
{
var message = await result;
await context.PostAsync("Please provide value for: " + message.ToString());
context.Wait(OrderSearchAttributeValueReceived);
}
private async Task OrderSearchAttributeValueReceived(IDialogContext context, IAwaitable<object> result)
{
var message = await result;
await context.PostAsync("Please provide value as: " + ((Activity)message).Text);
PromptDialog.Choice(
context: context,
resume: ResumeAfterMoreOrderSearchAttributeConfirmation,
options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
prompt: "Do you want to search with more attributes:",
retry: "I didn't understand. Please try again.");
}
private async Task ResumeAfterMoreOrderSearchAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
{
var message = await result;
if (message == Confirmation.Yes)
{
PromptDialog.Choice(
context: context,
resume: ResumeAfterOrderSearchAttributeSelection,
options: Enum.GetValues(typeof(OrderSearchOptions)).Cast<OrderSearchOptions>().ToArray(),
prompt: "Please select any Order search attribute to start the search operation:",
retry: "I didn't understand. Please try again.");
}
else
{
await context.PostAsync($"Ok. Searching for Order s...");
var count = 100;
if (count > 1)
{
await context.PostAsync($"I found total of 100 Order s");
await context.PostAsync($"To get Order details, you will need to provide more info...");
}
else
{
await context.PostAsync($"I found the Order you were looking for...");
await context.PostAsync($"Now I can provide you information related to your Order .");
}
}
}
Edit: Explanation about Step approach in FormFlow
I understand the Skip approach in the FormFlow but there user was asked to provide information sequentially so i wanted a mechanism that after imp fields we take user confirmation if he/she wants to provide more info. I am good with that approach and I understand it as well. But now I am trying to see the possibility to implement a different approach. Steps:
- Bot sends options(enum/class) to User.
- User selects an option which is sent to Bot.
- Bot receives that options and asks user to provide its value.
- Once value is received (where to save this?) Bot ask user if he/she would like to make another selection.
- If answer is "Yes" then step 1 is repeated, If answer is "No" then move to next step.
- Send all the options:value combinations to DB to complete the search operation.
I am trying to see a possibility to do this via FormFlow and IDialog option as well.
A better IDialog approach but has 2 issue.
- I am unable to get the proper names of the Enum options like OrderNumber should have been displayed as Order Number.
- Whatever options user provide needs to be added to a class so that it can be passed to DB/service. I am not getting any idea on how to do that.
Note: I am trying to see the possibilities with FormFlow and IDialog that user decides for which attributes he/she needs to provide information.
using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Bot.Builder.FormFlow;
namespace Bot.Dialogs
{
[Serializable]
public class OrderDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync($"Welcome to the Order helper!");
await DisplayOrderSearchAttributeSelection(context);
}
private async Task DisplayOrderSearchAttributeSelection(IDialogContext context)
{
PromptDialog.Choice(
context: context,
resume: ResumeAfterOrderSearchAttributeSelection,
options: Enum.GetValues(typeof(OrderSearchOptions)).Cast<OrderSearchOptions>().ToArray(),
prompt: "Please select any Order search attribute to start the search operation:",
retry: "I didn't understand. Please try again.");
}
private async Task ResumeAfterOrderSearchAttributeSelection(IDialogContext context, IAwaitable<OrderSearchOptions> result)
{
var message = await result;
if (message.ToString().ToLower() == "status")
{
await DisplayOrderStatusCreatedAttributeSelection(context);
}
else
{
await context.PostAsync("Please provide value for: " + message.ToString());
context.Wait(OrderSearchAttributeValueReceived);
}
}
private async Task OrderSearchAttributeValueReceived(IDialogContext context, IAwaitable<object> result)
{
var message = await result as Activity;
//set received value in the corresponding property.
await MoreOrderSearchAttributeConfirmation(context);
}
private async Task MoreOrderSearchAttributeConfirmation(IDialogContext context)
{
PromptDialog.Choice(
context: context,
resume: ResumeAfterMoreOrderSearchAttributeConfirmation,
options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
prompt: "Do you want to search with more attributes:",
retry: "I didn't understand. Please try again.");
}
private async Task ResumeAfterMoreOrderSearchAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
{
var message = await result;
if (message == Confirmation.Yes)
{
await DisplayOrderSearchAttributeSelection(context);
}
else
{
//Take all the values provided by user and send to DB/Service for further processing.
await context.PostAsync($"Ok. Searching for Order s...");
var count = 100;
if (count > 1)
{
await context.PostAsync($"I found total of 100 Order s");
await context.PostAsync($"To get Order details, you will need to provide more info...");
//some more logic needs to be written here
}
else
{
await context.PostAsync($"I found the Order you were looking for...");
await context.PostAsync($"Now I can provide you information related to Consumer Package, Multi-Pack, Shelf Tray & Unit Load for this Order .");
}
}
}
private async Task DisplayOrderStatusCreatedAttributeSelection(IDialogContext context)
{
PromptDialog.Choice(
context: context,
resume: ResumeAfterOrderStatusCreatedAttributeConfirmation,
options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
prompt: "Do you want to search for Status Created?",
retry: "I didn't understand. Please try again.");
}
private async Task ResumeAfterOrderStatusCreatedAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
{
var message = await result;
//set received value in the corresponding property.
PromptDialog.Choice(
context: context,
resume: ResumeAfterOrderStatusPreliminaryAttributeConfirmation,
options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
prompt: "Do you want to search for Status Preliminary?",
retry: "I didn't understand. Please try again.");
}
private async Task ResumeAfterOrderStatusPreliminaryAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
{
var message = await result;
//set received value in the corresponding property.
PromptDialog.Choice(
context: context,
resume: ResumeAfterOrderStatusCompletedAttributeConfirmation,
options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
prompt: "Do you want to search for Status Completed?",
retry: "I didn't understand. Please try again.");
}
private async Task ResumeAfterOrderStatusCompletedAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
{
var message = await result;
//set received value in the corresponding property.
PromptDialog.Choice(
context: context,
resume: ResumeAfterOrderStatusSearchAttributesDone,
options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
prompt: "Do you want to search for Status Expired?",
retry: "I didn't understand. Please try again.");
}
private async Task ResumeAfterOrderStatusSearchAttributesDone(IDialogContext context, IAwaitable<Confirmation> result)
{
var searchQuery = await result;
await MoreOrderSearchAttributeConfirmation(context);
}
}
}
public enum OrderSearchOptions
{
[Describe(Description = "Order Number")]
OrderNumber,
[Describe(Description = "Location")]
Location,
Issuer,
[Describe(Description = "Include Breakable")]
IncludeBreakable,
Status,
[Describe(Description = "Packaging Requirement")]
PackagingRequirement
}
public enum OrderStatus
{
[Describe(Description = "Status Created")]
Created,
Preliminary,
Completed,
Expired
}
Edit: Added images for reference





