I have in total 4 dialogs. In one of the dialogs (named "product issue") I have to go a new dialog named "resolution", at the end of that dialog their itself at particular condition. An adaptive card is displayed from product issue have 4 option in it, on "Overload" ,"Computer Rebooting", "Low Backup" it goes to new dialog named "resolution", where it has two steps: in step one display an adaptive card again with 4 options named
"Restart Machine", "off device","Press,Need assistance...when I select any option three open from step 1 like "Restart Machine", "off device","Press..it should display the adaptive card and end their itself and need assistance it will go back to product issue and will display defined steps for it. But when I choose
"Restart Machine", "off device" or press ..dialog is not ending and it it going back to product issue.
Product issue:
namespace Microsoft.BotBuilderSamples
{
public class ProductIssue : ComponentDialog
{
private const string UserInfo = "value-userInfo";
protected readonly ILogger _logger;
protected readonly string[] _end =
{
Path.Combine(".", "Resources", "ValidationCard.json"),
};
protected readonly string[] _date =
{
Path.Combine(".", "Resources", "Datepicker.json"),
};
protected readonly string[] _time =
{
Path.Combine(".", "Resources", "Timepicker.json"),
};
protected readonly string[] _cards =
{
Path.Combine(".", "Resources", "State.json"),
};
protected readonly string[] _card =
{
Path.Combine(".", "Resources", "City.json"),
};
protected readonly string[] _purchase =
{
Path.Combine(".", "Resources", "purchase.json"),
};
protected readonly string[] _service =
{
Path.Combine(".", "Resources", "Service.json")
};
public ProductIssue(string dialogId) : base(dialogId)
{
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
if (dialogId == $"{nameof(MainDialog)}.fromMain")
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
optionAsync,
InoptionAsync,
AnyOthersAsync,
OtpAsync,
UserauthenticationAsync,
CityAsync,
purchaseAsync,
purchaseYesAsync,
reviewAsync,
lastAsync,
EndAsync
}));
else if (dialogId == $"{ nameof(Resolution)}.resolution")
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
OtpAsync,
UserauthenticationAsync,
CityAsync,
purchaseAsync,
purchaseYesAsync,
reviewAsync,
lastAsync,
EndAsync
}));
else
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
InoptionAsync,
AnyOthersAsync,
OtpAsync,
UserauthenticationAsync,
CityAsync,
purchaseAsync,
purchaseAsync,
purchaseYesAsync,
reviewAsync,
lastAsync,
EndAsync
}));
InitialDialogId = nameof(WaterfallDialog);
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new TextPrompt($"{nameof(MainDialog)}.fromMain", ValidateAsync));
}
private async Task<DialogTurnResult> optionAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var attachments = new List<Attachment>();
var reply = MessageFactory.Attachment(attachments);
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment4());
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment5());
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment6());
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment7());
var promptOptions = new PromptOptions { Prompt = MessageFactory.Text("Please select any option.") };
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
return await stepContext.PromptAsync(nameof(TextPrompt), promptOptions, cancellationToken);
}
private async Task<DialogTurnResult> InoptionAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string choice = stepContext.Result.ToString();
if (choice == "Inverter" || choice == "Fan" || choice == "Battery")
{
var attachments = new List<Attachment>();
var reply = MessageFactory.Attachment(attachments);
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment8());
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
}
else
{
var attachments = new List<Attachment>();
var reply = MessageFactory.Attachment(attachments);
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.GetHeroCard5().ToAttachment());
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("I will be here if you need me further."), cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
}
var promptOptions = new PromptOptions { Prompt = MessageFactory.Text("i will") };
return await stepContext.PromptAsync(nameof(TextPrompt), promptOptions, cancellationToken);
}
private async Task<DialogTurnResult> AnyOthersAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string choice = stepContext.Result.ToString();
if (choice == "Any Others")
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("We are here to help you."), cancellationToken);
}
else if(choice == "Overload" || choice == "Computer Rebooting" || choice == "Low Backup")
{
return await stepContext.BeginDialogAsync($"{nameof(Resolution)}.start", null, cancellationToken);
}
var anyothers = new PromptOptions { Prompt = MessageFactory.Text("Please enter your specific problem.") };
return await stepContext.PromptAsync(nameof(TextPrompt), anyothers, cancellationToken);
}
private async Task<DialogTurnResult> OtpAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values[UserInfo] = new UserInput();
await stepContext.Context.SendActivityAsync(MessageFactory.Text("To proceed further, we will be verifying your mobile number by sending an OTP."), cancellationToken);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("We have just sent an OTP to your number"), cancellationToken);
var num = new PromptOptions
{
Prompt = MessageFactory.Text("Kindly enter the OTP sent(6 digit number)"),
RetryPrompt = MessageFactory.Text("Incorrect OTP entered. Kindly re-enter the OTP sent (6 digit number).")
};
return await stepContext.PromptAsync($"{nameof(MainDialog)}.fromMain", num, cancellationToken);
}
private async Task<DialogTurnResult> UserauthenticationAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var Otp = (UserInput)stepContext.Values[UserInfo];
int value;
var len = (stepContext.Result.ToString()).Length;
bool success = int.TryParse(stepContext.Result.ToString(), out value);
if (success == true && len == 6)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thanks. Your OTP is confirmed."), cancellationToken);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("We are now validating your number against our database. This may take a minute."), cancellationToken);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("We will be registering you as a user in our system. Please provide a few details."), cancellationToken);
}
var attachments = new List<Attachment>();
var reply = MessageFactory.Attachment(attachments);
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment9());
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Hello"), cancellationToken);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Please select the state you are located in"), cancellationToken);
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_cards[r.Next(_cards.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
var num = new PromptOptions
{
Prompt = MessageFactory.Text(" Selected state is:")
};
return await stepContext.PromptAsync(nameof(TextPrompt), num, cancellationToken);
}
private async Task<DialogTurnResult> CityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_card[r.Next(_card.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
var num = new PromptOptions
{
Prompt = MessageFactory.Text(" Selected city is:")
};
return await stepContext.PromptAsync(nameof(TextPrompt), num, cancellationToken);
}
private async Task<DialogTurnResult> purchaseAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_purchase[r.Next(_purchase.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
var num = new PromptOptions
{
Prompt = MessageFactory.Text("")
};
return await stepContext.PromptAsync(nameof(TextPrompt), num, cancellationToken);
}
private async Task<DialogTurnResult> purchaseYesAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string choice = stepContext.Result.ToString();
if (choice.ToLower() == "yes")
{
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_date[r.Next(_date.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
}
else if (choice.ToLower() == "no")
{
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_service[r.Next(_service.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
}
var num = new PromptOptions
{
Prompt = MessageFactory.Text("")
};
return await stepContext.PromptAsync(nameof(TextPrompt), num, cancellationToken);
}
private async Task<DialogTurnResult> reviewAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string choice = stepContext.Result.ToString();
if (choice.ToLower() == "no")
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Our representative will be reviewing your comments shortly."), cancellationToken);
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_end[r.Next(_end.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
}
else
{
ServiceDate serviceDate = JsonConvert.DeserializeObject<ServiceDate>(choice);
Random r2 = new Random();
var validationcards = Cards.CreateAdaptiveCardAttachment2(_service[r2.Next(_service.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcards), cancellationToken);
}
var num = new PromptOptions
{
Prompt = MessageFactory.Text("")
};
return await stepContext.PromptAsync(nameof(TextPrompt), num, cancellationToken);
}
private async Task<DialogTurnResult> lastAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string choice = stepContext.Result.ToString();
if (choice.ToLower() == "yes")
{
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_time[r.Next(_time.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
}
else if (choice.ToLower() == "no")
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Our representative will be reviewing your comments shortly."), cancellationToken);
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_end[r.Next(_end.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
}
var num = new PromptOptions
{
Prompt = MessageFactory.Text("")
};
return await stepContext.PromptAsync(nameof(TextPrompt), num, cancellationToken);
}
private async Task<DialogTurnResult> EndAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string choice = stepContext.Result.ToString();
ServiceDate serviceDate = JsonConvert.DeserializeObject<ServiceDate>(choice);
if (serviceDate.id == "12")
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Starting to raise service request with the team. Please give it a minute."), cancellationToken);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Our representative will be reviewing your comments shortly."), cancellationToken);
}
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_end[r.Next(_end.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
}
private Task<bool> ValidateAsync(PromptValidatorContext<string> promptContext, CancellationToken cancellationToken)
{
int value;
int len = (promptContext.Context.Activity.Text).Length;
bool success = int.TryParse(promptContext.Context.Activity.Text, out value);
if (success == true && len == 6)
{
return Task.FromResult(true);
}
else
{
return Task.FromResult(false);
}
}
}
public class ServiceDate
{
public string id { get; set; }
public string value { get; set; }
public string date { get; set; }
}
}
**resolution dialog**
namespace Microsoft.BotBuilderSamples
{
public class Resolution : ComponentDialog
{
protected readonly string[] _solution =
{
Path.Combine(".", "Resources", "resolution.json"),
};
protected readonly string[] _action =
{
Path.Combine(".", "Resources", "ValidationCard.json"),
};
public Resolution(string dialogId) : base(dialogId)
{
AddDialog(new TextPrompt($"{nameof(Resolution)}.solution"));
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
AddDialog(new WaterfallDialog($"{nameof(Resolution)}.start", new WaterfallStep[]
{
solutionAsync,
actionAsync
}));
InitialDialogId = $"{nameof(Resolution)}.start";
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new TextPrompt($"{nameof(Resolution)}.end"));
}
private async Task<DialogTurnResult> solutionAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_solution[r.Next(_solution.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
var anyothers = new PromptOptions { Prompt = MessageFactory.Text("Please select the problem resloution.") };
return await stepContext.PromptAsync($"{nameof(Resolution)}.solution", anyothers, cancellationToken);
}
private async Task<DialogTurnResult> actionAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string choice = stepContext.Result.ToString();
if (choice == "Restart Machice" || choice == "off device" || choice == "Press")
{
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_action[r.Next(_action.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
return await stepContext.EndDialogAsync($"{nameof(Resolution)}.end", cancellationToken);
}
else
{
return await stepContext.BeginDialogAsync($"{ nameof(Resolution)}.resolution", null, cancellationToken);
} } }}
If you want to end a dialog and NOT return to ANY previous dialog, you need to use CancellAllDialogs. It looks like you are using only EndDialog, which only ends the currently active dialog.
In the first step of this Dialog, I want to ask the user what they want to do with a Choice prompt. After the return, where I would expect the dialog to go to the next Waterfallstep (HandleChoiceAsync), it instead returns to it's parentdialog.
I seem to remember this used to work before. Thanks in advance!
MainDialog
OrdersDialog
private async Task<DialogTurnResult> AskForProductsAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
productList = (List<Product>)stepContext.Options;
if(productList.Count > 0)
{
string producten = "";
foreach(Product p in productList)
{
producten += p.GetProductName() + ", ";
}
producten = producten.Remove(producten.Length - 2);
await stepContext.Context.SendActivityAsync("In je bestelling staan nu de volgende producten: " + producten + ".");
}
//choice prompt does not go to next waterfallstep, instead returns to parent dialog
return await stepContext.PromptAsync(nameof(ChoicePrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Wil je nog een product toevoegen of verwijderen?"),
RetryPrompt = MessageFactory.Text("Probeer het nog een keer"),
Choices = ChoiceFactory.ToChoices(new List<string> { "Toevoegen", "Verwijderen", "Klaar met bestellen" })
}, cancellationToken);
}
private async Task<DialogTurnResult> HandleChoiceAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
FoundChoice choice = (FoundChoice)stepContext.Result;
switch (choice.Index)
{
case 0:
toevoegen = true;
var messageText = "Welk product wil je toevoegen?";
var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
case 1:
toevoegen = false;
List<string> productsString = new List<string>();
foreach (Product p in productList)
{
productsString.Add(p.GetProductName());
}
return await stepContext.PromptAsync(nameof(ChoicePrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Welk product wil je verwijderen?"),
Choices = ChoiceFactory.ToChoices(productsString)
}, cancellationToken);
case 2:
return await stepContext.EndDialogAsync(productList);
default:
break;
}
return await stepContext.NextAsync();
}
I need to do a condition depending on the Id of the active dialog something like this.
var dc = await _dialogs.CreateContextAsync(turnContext);
if (dc.ActiveDialog != null && dc.ActiveDialog.id == "SkipLuisDialog")
{
var interruptedQnaMaker = await IsTurnInterruptedDispatchToQnAMakerAsync(turnContext, topDispatch, QnaConfiguration, cancellationToken);
}
When i check my active dialog on OnTurnAsync() like this:
if (dc.ActiveDialog != null)
{
await dc.Context.SendActivityAsync(dc.ActiveDialog.Id.ToString());
}
It always say "mainDialog" which is the ID of my main dialog. Even when im on the "FAQDialog" or "complaintDialog" the active dialog ID is always "mainDialog". Why is the active dialog Id not changing to "FAQDialog" when im in the FAQ dialog or to "complaintDialog" when im in the complaint dialog?
My main dialog and other dialogs are in another class.
This is how i call the mainDialog on OnTurnAsync():
if (!dc.Context.Responded)
{
switch (dialogResult.Status)
{
case DialogTurnStatus.Empty:
switch (topIntent) // topIntent // text
{
case GetStartedIntent:
await dc.BeginDialogAsync(MainDialogId);
break;
case NoneIntent:
default:
await dc.Context.SendActivityAsync("I didn't understand what you just said to me.");
break;
}
break;
case DialogTurnStatus.Waiting:
// The active dialog is waiting for a response from the user, so do nothing.
break;
case DialogTurnStatus.Complete:
await dc.EndDialogAsync();
break;
default:
await dc.CancelAllDialogsAsync();
break;
}
}
The Main Dialog:
namespace CoreBot.Dialogs
{
public class MainDialog : ComponentDialog
{
private const string InitialId = "mainDialog";
private const string TextPromptId = "textPrompt";
private const string ComplaintDialogId = "complaintDialog";
private const string FAQDialogId = "FAQDialog";
public MainDialog(string dialogId) : base(dialogId)
{
WaterfallStep[] waterfallSteps = new WaterfallStep[]
{
FirstStepAsync,
SecondStepAsync,
ThirdStepAsync,
};
AddDialog(new WaterfallDialog(InitialId, waterfallSteps));
AddDialog(new FAQDialog(FAQDialogId));
AddDialog(new FileComplaintDialog(ComplaintDialogId));
AddDialog(new TextPrompt(TextPromptId));
}
private static async Task<DialogTurnResult> FirstStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
{
return await stepContext.PromptAsync(
TextPromptId,
new PromptOptions
{
Prompt = new Activity
{
Type = ActivityTypes.Message,
Text = $"What can i do for you?",
SuggestedActions = new SuggestedActions()
{
Actions = new List<CardAction>()
{
new CardAction() { Title = "Frequently Asked Questions", Type = ActionTypes.ImBack, Value = "Frequently Asked Questions" },
new CardAction() { Title = "File a Complaint Ticket", Type = ActionTypes.ImBack, Value = "File a Complaint Ticket" },
},
},
},
});
}
private static async Task<DialogTurnResult> SecondStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
{
var response = stepContext.Result.ToString().ToLower();
string[] FAQArray = { "frequently asked questions", "question", "ask question" };
string[] ComplaintsArray = { "file a complaint ticket", "complaint", "file a complaint" };
if (FAQArray.Contains(response))
{
return await stepContext.BeginDialogAsync(FAQDialogId, cancellationToken);
}
if (ComplaintsArray.Contains(response))
{
await stepContext.EndDialogAsync();
return await stepContext.BeginDialogAsync(ComplaintDialogId, cancellationToken: cancellationToken);
}
return await stepContext.NextAsync();
}
private static async Task<DialogTurnResult> ThirdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
{
return await stepContext.ReplaceDialogAsync(InitialId);
}
}
}
The Main Dialog calls 2 dialogs. This is one of them.
namespace CoreBot.Dialogs
{
public class FAQDialog : ComponentDialog
{
private const string InitialId = "FAQDialog";
private const string ChoicePromptId = "choicePrompt";
private const string TextPromptId = "textPrompt";
private const string ConfirmPromptId = "confirmPrompt";
public FAQDialog(string dialogId) : base(dialogId)
{
WaterfallStep[] waterfallSteps = new WaterfallStep[]
{
FirstStepAsync,
SecondStepAsync,
ThirdStepAsync,
FourthStepAsync,
};
AddDialog(new WaterfallDialog(InitialId, waterfallSteps));
AddDialog(new ChoicePrompt(ChoicePromptId));
AddDialog(new ConfirmPrompt(ConfirmPromptId));
AddDialog(new TextPrompt(TextPromptId));
}
private static async Task<DialogTurnResult> FirstStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
{
var choices = new List<Choice>();
choices.Add(new Choice { Value = "This is Sample Question 1", Synonyms = new List<string> { "Question 1" } });
choices.Add(new Choice { Value = "This is Sample Question 2", Synonyms = new List<string> { "Question 2" } });
return await stepContext.PromptAsync(
ChoicePromptId,
new PromptOptions
{
Prompt = MessageFactory.Text($"Welcome to FAQ! Choose the number of the question or type your own question."),
Choices = choices,
});
}
private static async Task<DialogTurnResult> SecondStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
{
var choiceResult = (stepContext.Result as FoundChoice).Value.ToLower();
switch (choiceResult)
{
case "this is sample question 1":
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Answer to question 1."));
break;
case "this is sample question 2":
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Answer to question 2."));
break;
default:
break;
}
return await stepContext.NextAsync();
}
private static async Task<DialogTurnResult> ThirdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
{
return await stepContext.PromptAsync(
ConfirmPromptId,
new PromptOptions
{
Prompt = MessageFactory.Text($"Have another question?"),
});
}
private static async Task<DialogTurnResult> FourthStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
{
var confirmResult = (bool)stepContext.Result;
if (confirmResult)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Ask away!"));
return await stepContext.ReplaceDialogAsync(InitialId);
}
else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Great!"));
return await stepContext.EndDialogAsync();
}
}
}
}
This is because all of your dialogs are being called within MainDialog.
Dialog Stack Concept
Here's more or less what's going on:
In MyBot.cs (or whatever your main .cs file is), you have something like Dialogs = new DialogSet(_dialogStateAccessor) which creates an empty dialog stack:
___________
You then use something like await dc.BeginDialogAsync(MainDialogId);, which adds MainDialog to the top of your dialog stack:
______________
| MainDialog |
|______________|
If you call return await stepContext.BeginDialogAsync(FAQDialogId, cancellationToken); outside of your MainDialog, your stack will look something like this:
______________
| FAQDialogId |
|______________|
______________
| MainDialog |
|______________|
However, you call return await stepContext.BeginDialogAsync(FAQDialogId, cancellationToken); from within MainDialog, which has it's own stack, so FAQDialog is the active dialog within MainDialog, but your Dialogs stack still has MainDialog as the ActiveDialog. So, your stack is more or less like this:
_______________________
| ______________ |
| |__FAQDialog__| |
| |
| MainDialog |
|_______________________|
Doing What You Want
You have a couple of options:
1. Call all of your other dialogs from outside of MainDialog.
If you're not going to come back to MainDialog, you could do something like:
In MainDialog:
return await stepContext.EndDialogAsync("FAQDialog");
In MyBot.cs:
switch (dialogResult)
{
case "FAQDialog":
dc.BeginDialogAsync(FAQDialogId, cancellationToken);
break;
case "ComplaintDialog":
dc.BeginDialogAsync(ComplaintDialogId, cancellationToken);
break;
}
2. Dig for the the dialog's ID within the ActiveDialog's stack
if (dc.ActiveDialog != null && IsDialogInStack(dc.ActiveDialog, "SkipLuisDialog"))
{
var interruptedQnaMaker = await IsTurnInterruptedDispatchToQnAMakerAsync(turnContext, topDispatch, QnaConfiguration, cancellationToken);
}
[...]
private bool IsDialogInStack(DialogInstance activeDialog, string searchId)
{
if (activeDialog.Id == searchId)
{
return true;
};
foreach (KeyValuePair<string, object> item in activeDialog.State)
{
if (item.Key == "dialogs" && item.Value is DialogState)
{
DialogState dialogState = (DialogState)item.Value;
foreach (DialogInstance dialog in dialogState.DialogStack)
{
return IsDialogInStack(dialog, searchId);
}
}
}
return false;
}
Note: I'm not a C# expert and there might be a better way to code IsDialogInStack, but the code above works fine.
I was trying the multi-language chat bot in bot framework. https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/17.multilingual-bot.
I was successful in translating ordinary message with no attachment. But I'm having a problem with attachment like herocard or suggestedcard.
var reply = stepContext.Context.Activity.CreateReply();
var card = new HeroCard();
card.Buttons = new List<CardAction>()
{
new CardAction() { Title = "1. All lights are green.", Type = ActionTypes.ImBack, Value = "All lights are green." },
new CardAction() { Title = "2. DSL light is OFF/Red/Blinking Green.", Type = ActionTypes.ImBack, Value = "DSL light is OFF/Red/Blinking Green." },
new CardAction() { Title = "3. Internet light is OFF/Red/Amber or blinking red and green.", Type = ActionTypes.ImBack, Value = "Internet light is OFF/Red/Amber or blinking red and green." },
new CardAction() { Title = "4. Power light is OFF/Red/Amber or blinking.", Type = ActionTypes.ImBack, Value = "Power light is OFF/Red/Amber or blinking." },
};
reply.Attachments = new List<Attachment>() { card.ToAttachment() };
reply.AttachmentLayout = AttachmentLayoutTypes.List;
var options = new PromptOptions()
{
Prompt = reply,
};
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
Thanks!
You need to check if there are any attachments. I have some middleware that sets the speak on message and use the following code:
if (string.IsNullOrEmpty(message.Text))
{
if (message.Attachments[0].Content is HeroCard attachment)
{
message.Speak = TextToSpeechHelper.ConvertTextToSpeechText(attachment.Text);
}
}
else
{
message.Speak = TextToSpeechHelper.ConvertTextToSpeechText(message.Text);
}
You'd have to adjust it to set the text and translate.
Below is a full example of what my middleware is. Keep in mind this middleware sets the speak to the text and sets the input hint.
public class TextToSpeechMiddleware : IMiddleware
{
private readonly IEnumerable<string> ignoreList;
public TextToSpeechMiddleware(string speakIgnore)
{
ignoreList = GetSpeakIgnore(speakIgnore);
}
public Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default(CancellationToken))
{
turnContext.OnSendActivities(OnSendActivities);
turnContext.OnUpdateActivity(OnUpdateActivity);
return next(cancellationToken);
}
private static IEnumerable<string> GetSpeakIgnore(string value)
{
string[] ignoreList = value.Split(';');
return ignoreList.Select(i => i.Trim())
.Where(i => !string.IsNullOrEmpty(i));
}
private Task<ResourceResponse> OnUpdateActivity(ITurnContext turnContext, Activity activity, Func<Task<ResourceResponse>> next)
{
ConvertTextToSpeech(activity);
return next();
}
private Task<ResourceResponse[]> OnSendActivities(ITurnContext turnContext, List<Activity> activities, Func<Task<ResourceResponse[]>> next)
{
foreach (Activity currentActivity in activities.Where(a => a.Type == ActivityTypes.Message))
{
ConvertTextToSpeech(currentActivity);
}
return next();
}
private void ConvertTextToSpeech(Activity message)
{
Activity initialMessage = message;
try
{
if (message.Type == ActivityTypes.Message)
{
bool ignoredSpeak = false;
if (string.IsNullOrEmpty(message.Speak))
{
if (string.IsNullOrEmpty(message.Text))
{
if (message.Attachments[0].Content is HeroCard attachment)
{
message.Speak = TextToSpeechHelper.ConvertTextToSpeechText(attachment.Text);
}
}
else
{
message.Speak = TextToSpeechHelper.ConvertTextToSpeechText(message.Text);
}
message.Speak = message.Speak.Trim();
if (ignoreList.Where(i => message.Speak.ToLower().StartsWith(i.ToLower())).Count() != 0)
{
message.Speak = null;
ignoredSpeak = true;
}
}
else if (string.IsNullOrWhiteSpace(message.Speak))
{
message.Speak = " ";
}
if ((!string.IsNullOrEmpty(message.Speak) && (message.Speak.EndsWith("?") || message.Speak.StartsWith("Is this correct?")))
|| (!string.IsNullOrEmpty(message.Text) && message.Text.EndsWith("?"))
|| ignoredSpeak)
{
message.InputHint = InputHints.ExpectingInput;
}
// IOs won't work with expecting input
if (message.Recipient.Name.EndsWith(":ios"))
{
message.InputHint = InputHints.AcceptingInput;
}
}
// Logic needed to increase speech speed.
// if (!string.IsNullOrEmpty(message.Speak))
// {
// message.Speak = #"<speak version='1.0' " + "xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='en-GB'><prosody rate=\"1.5\">" + message.Speak + "</prosody></speak>";
// }
}
catch (Exception)
{
message = initialMessage;
}
}
}
I have a choice prompt and I wanted to make it so that even if a user types something else that synonyms with the choice the dialog can still move on. I tried doing this but its not working.
public class InitialQuestions : WaterfallDialog
{
public InitialQuestions(string dialogId, IEnumerable<WaterfallStep> steps = null)
: base(dialogId, steps)
{
AddStep(async (stepContext, cancellationToken) =>
{
var choices = new[] { "Agree" };
return await stepContext.PromptAsync(
"choicePrompt",
new PromptOptions
{
Prompt = MessageFactory.Text(string.Empty),
Choices = ChoiceFactory.ToChoices(choices),
RetryPrompt = MessageFactory.Text("Click Agree to proceed."),
});
});
AddStep(async (stepContext, cancellationToken) =>
{
var response = (stepContext.Result as FoundChoice).Value.ToLower();
var textResponse = (stepContext.Result as FoundChoice).ToString().ToLower();
if (response == "agree" || textResponse == "okay" || textResponse == "ok")
{
return await stepContext.NextAsync();
}
else
{
return await stepContext.ReplaceDialogAsync(InitialQuestions.Id);
}
});
}
public static string Id => "initialQuestions";
public static InitialQuestions Instance { get; } = new InitialQuestions(Id);
}
A choice prompt has to validate the user input by comparing it to a list of choices and the dialog will not proceed until valid input is supplied. You're trying to validate the input in the next step, but the next step won't be reached until the input is already validated and that's why textResponse will never be "okay" or "ok".
Luckily, choice prompts have a builtin way of providing synonyms for each choice. Instead of
Choices = ChoiceFactory.ToChoices(choices),
you could do something like
Choices = new List<Choice>
{
new Choice
{
Value = "Agree",
Synonyms = new List<string>
{
"Okay",
"OK",
},
},
},