I have a problem with my component dialogs in botframework V4.
I'm using a root dialog which contains a Waterfalldialog and an Setupdialog. The initial dialog is the Setupdialog. Like so:
public RootDialog(SetupDialog setupDialog)
: base(nameof(RootDialog))
{
AddDialog(setupDialog);
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
ProcessStep
}));
InitialDialogId = nameof(SetupDialog);
}
In the setup dialog I'm asking for some value. If the setup dialogs continues I check the value and if its like I wanted, I end the dialog.
public override Task<DialogTurnResult> ContinueDialogAsync(DialogContext outerDc, CancellationToken cancellationToken = default)
{
if (!int.TryParse(outerDc.Context.Activity.Text, out var recursions))
{
return outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
{
Prompt = MessageFactory.Text($"{outerDc.Context.Activity.Text} konnte nicht in eine Zahl konvertiert werden.")
});
}
return outerDc.EndDialogAsync(recursions);
}
If I end the dialog like this, shouldn't be ResumeDialog be called in the RootDialog?
Here the whole Dialogs:
public class RootDialog : ComponentDialog
{
private int recursions;
public RootDialog(SetupDialog setupDialog)
: base(nameof(RootDialog))
{
AddDialog(setupDialog);
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
ProcessStep
}));
InitialDialogId = nameof(SetupDialog);
}
public async Task<DialogTurnResult> ProcessStep(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Process step in root dialog")
});
if(Dialogs.Find(nameof(RecursiveDialog)) == null)
{
AddDialog(new RecursiveDialog(new DialogSet(), recursions));
}
if (recursions > 0)
{
return await stepContext.BeginDialogAsync(nameof(RecursiveDialog));
}
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Recursion lower or eqaul 0")
});
}
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext outerDc, object options = null, CancellationToken cancellationToken = default)
{
var dialogContext = CreateChildContext(outerDc);
await dialogContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Begin root dialog")
});
return await base.BeginDialogAsync(outerDc, options, cancellationToken);
}
public override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
{
if(innerDc.ActiveDialog != null)
{
return await innerDc.ContinueDialogAsync();
}
return await base.OnContinueDialogAsync(innerDc, cancellationToken);
}
public override async Task<DialogTurnResult> ResumeDialogAsync(DialogContext outerDc, DialogReason reason, object result = null, CancellationToken cancellationToken = default)
{
recursions = Convert.ToInt32(result);
await outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text($"Resume root dialog with recursion value {recursions}")
});
return await outerDc.BeginDialogAsync(nameof(WaterfallDialog));
}
}
SetupDialog:
public class SetupDialog : ComponentDialog
{
public SetupDialog()
: base(nameof(SetupDialog))
{
AddDialog(new TextPrompt(nameof(TextPrompt)));
}
public override Task<DialogTurnResult> ContinueDialogAsync(DialogContext outerDc, CancellationToken cancellationToken = default)
{
if (!int.TryParse(outerDc.Context.Activity.Text, out var recursions))
{
return outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
{
Prompt = MessageFactory.Text($"{outerDc.Context.Activity.Text} konnte nicht in eine Zahl konvertiert werden.")
});
}
return outerDc.EndDialogAsync(recursions);
}
public override Task<DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default)
{
return innerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
{
Prompt = MessageFactory.Text("Wie viel rekursionen sollen erstellt werden?")
});
}
}
So, I got the ResumeDialog method to be triggerd.
To trigger the ResumeDialog method, the dialog that ended and the dialog you want to resume to has to be on the same dialog stack!
My scenario were BotDialogContext[RootDialog] -> RootDialogContext[SetupDialog], but I need a context like this BotDialogContext[RootDialog, SetupDialog].
One of the problems is, that every ComponentDialog you start creates its own DialogContext. So if you begin a dialog within a dialog its pushed on the stack of the inner DialogContext and so on. But the description of the ResumeDialog method is
Called when a child dialog on the parent's dialog stack completed this turn, returning control to this dialog component.
To put an child dialog on the parents dialog stack you had to call the BeginDialog method on the outer dialog context. This context also needs to have the "child dialog" in its dialogset.
Here my example:
RootDialog.cs:
public class RootDialog : ComponentDialog
{
private int recursions;
public RootDialog(SetupDialog setupDialog)
: base(nameof(RootDialog))
{
AddDialog(setupDialog);
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
ProcessStep
}));
InitialDialogId = nameof(SetupDialog);
}
public async Task<DialogTurnResult> ProcessStep(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Process step in root dialog")
});
if(Dialogs.Find(nameof(RecursiveDialog)) == null)
{
AddDialog(new RecursiveDialog(new DialogSet(), recursions));
}
if (recursions > 0)
{
return await stepContext.BeginDialogAsync(nameof(RecursiveDialog));
}
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Recursion lower or eqaul 0")
});
}
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext outerDc, object options = null, CancellationToken cancellationToken = default)
{
if (true)
{
return await outerDc.BeginDialogAsync(nameof(SetupDialog));
}
var dialogContext = CreateChildContext(outerDc);
await dialogContext.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Begin root dialog")
});
return await base.BeginDialogAsync(outerDc, options, cancellationToken);
}
protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
{
if(innerDc.ActiveDialog != null)
{
return await innerDc.ContinueDialogAsync();
}
return await base.OnContinueDialogAsync(innerDc, cancellationToken);
}
public override async Task<DialogTurnResult> ResumeDialogAsync(DialogContext outerDc, DialogReason reason, object result = null, CancellationToken cancellationToken = default)
{
recursions = Convert.ToInt32(result);
await outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions
{
Prompt = MessageFactory.Text($"Resume root dialog with recursion value {recursions}")
});
return await outerDc.BeginDialogAsync(nameof(WaterfallDialog));
}
}
SetupDialog.cs:
public class SetupDialog : ComponentDialog
{
public SetupDialog()
: base(nameof(SetupDialog))
{
AddDialog(new TextPrompt(nameof(TextPrompt)));
}
public override Task<DialogTurnResult> ContinueDialogAsync(DialogContext outerDc, CancellationToken cancellationToken = default)
{
if (!int.TryParse(outerDc.Context.Activity.Text, out var recursions))
{
return outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
{
Prompt = MessageFactory.Text($"{outerDc.Context.Activity.Text} konnte nicht in eine Zahl konvertiert werden.")
});
}
return outerDc.EndDialogAsync(recursions);
}
public override Task<DialogTurnResult> BeginDialogAsync(DialogContext outerDc, object options = null, CancellationToken cancellationToken = default)
{
return base.BeginDialogAsync(outerDc, options, cancellationToken);
}
protected override Task<DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default)
{
return innerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
{
Prompt = MessageFactory.Text("Wie viel rekursionen sollen erstellt werden?")
});
}
}
DialogBot.cs:
public class DialogBot<T> : ActivityHandler
where T : Dialog
{
protected readonly DialogSet Dialogs;
protected readonly BotState ConversationState;
protected readonly BotState UserState;
protected readonly ILogger Logger;
public DialogBot(ConversationState conversationState, UserState userState, IEnumerable<Dialog> dialogs, ILogger<DialogBot<T>> logger)
{
ConversationState = conversationState;
UserState = userState;
Logger = logger;
Dialogs = new DialogSet(conversationState.CreateProperty<DialogState>(nameof(DialogState)));
foreach(var dialog in dialogs)
{
Dialogs.Add(dialog);
}
}
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
await base.OnTurnAsync(turnContext, cancellationToken);
// Save any state changes that might have occured during the turn.
await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
Logger.LogInformation("Running dialog with Message Activity.");
var dc = await Dialogs.CreateContextAsync(turnContext, cancellationToken).ConfigureAwait(false);
if (dc.ActiveDialog != null)
{
await dc.ContinueDialogAsync();
}
else
{
// Run the Dialog with the new message Activity.
await dc.BeginDialogAsync(typeof(T).Name, ConversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
}
}
}
Inside the IEnumerable are both (RootDialog & SetupDialog) to get both dialogs into to the BotDialogContext and DialogSet
Related
I have the following code:
public class MainDialog : ComponentDialog
{
protected readonly IConfiguration Configuration;
protected readonly ILogger Logger;
protected int counter = 0;
protected bool HaveToken = false;
protected static string Token = "";
public MainDialog(IConfiguration configuration, ILogger<MainDialog> logger)
: base(nameof(MainDialog))
{
//cache the config from appstettings.json for later usage in LUIS & QnAMaker.
Configuration = configuration;
Logger = logger;
AddDialog(new OAuthPrompt(
nameof(OAuthPrompt),
new OAuthPromptSettings
{
ConnectionName = Configuration["ConnectionName"],
Text = "Bitte melden sie sich an.",
Title = "Login",
Timeout = 3000,
}));
AddDialog(new TextPrompt(nameof(TextPrompt)));
//Adding Dialogs and giving the Dialogs the config info for the QnAMaker connection.
AddDialog(new HelpDialog(Configuration, logger));
AddDialog(new SpellingDialog(Configuration, logger));
AddDialog(new CreateTeamDialog(Configuration, logger));
AddDialog(new DIGASDialog(Configuration, logger));
AddDialog(new GreetingDialog(Configuration, logger));
AddDialog(new OnboardingDialog(Configuration, logger));
//AddDialog(new LoginDialog(Configuration, logger));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
PromptStepAsync,
LoginStepAsync,
ProcessStepAsync,
IntroStepAsync,
ActStepAsync,
FinalStepAsync
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> PromptStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.BeginDialogAsync(nameof(OAuthPrompt),null, cancellationToken);
}
private async Task<DialogTurnResult> LoginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (HaveToken)
{
return await stepContext.NextAsync(cancellationToken);
}
if(stepContext.Result.GetType() == typeof(CancellationToken) )
{
return await stepContext.EndDialogAsync();
}
try
{
var tokenResponse = (TokenResponse)stepContext.Result;
Token = tokenResponse.Token;
HaveToken = true;
await stepContext.Context.SendActivityAsync(MessageFactory.Text("You are now logged in."), cancellationToken);
//await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Your token is: {tokenResponse.Token}"), cancellationToken);
//await OAuthhelper.ListMeAsync(stepContext.Context, tokenResponse);
return await stepContext.NextAsync(cancellationToken);
}
catch (Exception x)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful please try again. " + x.Message), cancellationToken);
return await stepContext.EndDialogAsync();
}
}
private async Task<DialogTurnResult> ProcessStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (HaveToken)
{
return await stepContext.NextAsync(cancellationToken);
}
if (stepContext.Result != null)
{
// We do not need to store the token in the bot. When we need the token we can
// send another prompt. If the token is valid the user will not need to log back in.
// The token will be available in the Result property of the task.
var tokenResponse = stepContext.Result as TokenResponse;
// If we have the token use the user is authenticated so we may use it to make API calls.
if (tokenResponse?.Token != null)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Your token is: {tokenResponse.Token}"), cancellationToken);
}
}
else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("We couldn't log you in. Please try again later."), cancellationToken);
}
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}
private async Task<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.ContinueDialogAsync(cancellationToken);
}
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var intentDetails = new QueryDetails();
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
intentDetails = stepContext.Result != null
?
await LuisHelper.ExecuteLuisQuery(Configuration, Logger, stepContext.Context, cancellationToken)
:
new QueryDetails();
switch (intentDetails.Intent)
{
case "Hilfe":
return await stepContext.BeginDialogAsync(nameof(HelpDialog), intentDetails, cancellationToken);
case "Schreibweise_Ausdruck":
return await stepContext.BeginDialogAsync(nameof(SpellingDialog), intentDetails, cancellationToken);
case "Team_erstellen":
return await stepContext.BeginDialogAsync(nameof(CreateTeamDialog), intentDetails, cancellationToken);
case "DIGAS":
return await stepContext.BeginDialogAsync(nameof(DIGASDialog), intentDetails, cancellationToken);
case "Begrueßung":
return await stepContext.BeginDialogAsync(nameof(GreetingDialog), intentDetails, cancellationToken);
case "Onboarding":
return await stepContext.BeginDialogAsync(nameof(OnboardingDialog), intentDetails, cancellationToken);
default:
await stepContext.Context.SendActivityAsync("Das habe ich nicht verstanden.");
return await stepContext.BeginDialogAsync(nameof(HelpDialog), intentDetails, cancellationToken);
}
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
//await stepContext.Context.SendActivityAsync(MessageFactory.Text("Vielen Dank."), cancellationToken);
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}
public static string getToken()
{
return Token;
}
}
After the Promptstep, the next step does not run until I give input. I more or less copied the AuthBot sample from Microsoft and modified it a little.
In the Promptstep the user authenticates himself, but after the prompt vanishes the Bot waits for more input instead of jumping to the next step.
The bot, where I copied the code from:
https://github.com/microsoft/BotBuilder-Samples/blob/main/samples/csharp_dotnetcore/46.teams-auth/Dialogs/MainDialog.cs
I could not test the sample, because it doesn't run on it's own.
When I testet my bot in the Emulator with ngrok tunneling, the token was recieved but the bot just exits and waits for input.
Here is what happens:
Why is LoginStep not starting after the prompt?
I think you should add ChoicePrompt too:
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
I have 3 dialogs in my bot framework v4 C# project, having waterfall steps in each.
In my second dialog have to behave the same as the first dialog but it doesn't have to execute all the steps of the first dialog, which means I need to skip the first step.
So is there any method to invoke all the waterfall steps of the first dialog into the other dialog by skipping the first step of the first dialog.
A waterfall once formed is supposed to be executed in a step-by-step fashion. However, you can do the formation of your waterfall steps conditionally.
The condition here can be based on DialogId.
It's quite difficult to understand what exactly you're trying to do so, I've formed a sample solution for you. Hope you're trying to do the same.
Please refer the below code for the same :
MainDialog.cs
namespace EchoBot.Dialogs
{
public class MainDialog : ComponentDialog
{
private readonly BotStateService _botStateService;
public MainDialog(BotStateService botStateService) : base(nameof(MainDialog))
{
_botStateService = botStateService;
InitializeWaterfallDialog();
}
private void InitializeWaterfallDialog()
{
var waterfallSteps = new WaterfallStep[]
{
InitialStepAsync,
FinalStepAsync
};
AddDialog(new SecondDialog($"{nameof(MainDialog)}.second", _botStateService));
AddDialog(new FirstDialog($"{nameof(MainDialog)}.first", _botStateService));
AddDialog(new FirstDialog($"{nameof(SecondDialog)}.firstFromSecond", _botStateService));
AddDialog(new WaterfallDialog($"{nameof(MainDialog)}.mainFlow", waterfallSteps));
InitialDialogId = $"{nameof(MainDialog)}.mainFlow";
}
private async Task<DialogTurnResult> InitialStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (Regex.Match(stepContext.Context.Activity.Text.ToLower(), "hi").Success)
{
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.second", null, cancellationToken);
}
else
{
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.first", null, cancellationToken);
}
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.EndDialogAsync(null,cancellationToken);
}
} }
FirstDialog.cs
namespace EchoBot.Dialogs
{
public class FirstDialog : ComponentDialog
{
private readonly BotStateService _botStateService;
public FirstDialog(string dialogId, BotStateService botStateService) : base(dialogId)
{
_botStateService = botStateService ?? throw new ArgumentNullException(nameof(botStateService));
if (dialogId == $"{ nameof(MainDialog)}.first")
InitializeWaterfallDialog1();
else
InitializeWaterfallDialog2();
}
private void InitializeWaterfallDialog1()
{
var waterfallsteps = new WaterfallStep[]
{
GetAge,
GetCity,
FinalStepAsync
};
AddDialog(new WaterfallDialog($"{nameof(FirstDialog)}.mainFlow", waterfallsteps));
AddDialog(new NumberPrompt<int>($"{nameof(FirstDialog)}.age"));
AddDialog(new TextPrompt($"{nameof(FirstDialog)}.city"));
InitialDialogId = $"{nameof(FirstDialog)}.mainFlow";
}
private void InitializeWaterfallDialog2()
{
var waterfallsteps = new WaterfallStep[]
{
GetCity,
FinalStepAsync
};
AddDialog(new WaterfallDialog($"{nameof(FirstDialog)}.mainFlow", waterfallsteps));
AddDialog(new NumberPrompt<int>($"{nameof(FirstDialog)}.age"));
AddDialog(new TextPrompt($"{nameof(FirstDialog)}.city"));
InitialDialogId = $"{nameof(FirstDialog)}.mainFlow";
}
private async Task<DialogTurnResult> GetAge(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.PromptAsync($"{nameof(FirstDialog)}.age",
new PromptOptions
{
Prompt = MessageFactory.Text("Please enter your age."),
RetryPrompt = MessageFactory.Text("Please enter a valid age.")
}, cancellationToken);
}
private async Task<DialogTurnResult> GetCity(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.PromptAsync($"{nameof(FirstDialog)}.age",
new PromptOptions
{
Prompt = MessageFactory.Text("Please enter your city."),
RetryPrompt = MessageFactory.Text("Please enter a valid city.")
}, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.EndDialogAsync(null, cancellationToken);
}
}}
SecondDialog.cs
namespace EchoBot.Dialogs
{
public class SecondDialog : ComponentDialog
{
private readonly BotStateService _botStateService;
public SecondDialog(string dialogId, BotStateService botStateService) : base(dialogId)
{
_botStateService = botStateService ?? throw new ArgumentNullException(nameof(botStateService));
InitializeWaterfallDialog();
}
private void InitializeWaterfallDialog()
{
var waterfallSteps = new WaterfallStep[]
{
InitialStepAsync,
FinalStepAsync
};
AddDialog(new WaterfallDialog($"{nameof(SecondDialog)}.mainFlow", waterfallSteps));
AddDialog(new TextPrompt($"{nameof(SecondDialog)}.name"));
InitialDialogId = $"{nameof(SecondDialog)}.mainFlow";
}
private async Task<DialogTurnResult> InitialStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.PromptAsync($"{nameof(SecondDialog)}.name",
new PromptOptions
{
Prompt = MessageFactory.Text("Please enter your Name.")
}, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
//Suppose you want to call First Dialog from here
return await stepContext.BeginDialogAsync($"{nameof(SecondDialog)}.firstFromSecond", null, cancellationToken);
}
}
}
Added one line - AddDialog(new FirstDialog($"{nameof(SecondDialog)}.firstFromSecond", _botStateService)); in MainDialog.cs. It's working fine for me.
Image 1 : When you go into second dialog from Main Dialog, it asks name and then skips first step of First Dialog (ie. age) and asks the second step ie. city.
Image 2 : When you go directly into first dialog from Main Dialog, it asks age and city both ie. it didn't skip first step.
Hope this is helpful. Ask in comments if you have any query!
Functionality:
Fetch and display relevant answer from QnAMaker
This response must be followed by an adaptive card with checkboxes
Based on the values selected, invoke a web service to provide the user with required file links.
Display a feedback message 'Did this help?'
Direct the conversation flow based on user response.
Problem Statement: For the step 2 mentioned above, the button click is being handled at the bot.cs file and redirected to a new Dialog.
Everything works fine until displaying the feedback message(which is again invoked from a new Dialog). However, after this text prompt the
next step is not called and exits with an error: The given key 'dialogs' was not present in the dictionary.
Why does it show that error without going to the next step?
bot.cs
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
if (turnContext.Activity.Text != null)
{
if (!dc.Context.Responded)
{
// examine results from active dialog
switch (dialogResult.Status)
{
case DialogTurnStatus.Empty:
switch (topIntent)
{
case ...
}
break;
case ...
}
}
}
else if (string.IsNullOrEmpty(turnContext.Activity.Text))
{
await HandleSubmitActionAsync(turnContext, userProfile);
}
}
}
private async Task HandleSubmitActionAsync(ITurnContext turnContext, UserProfile userProfile)
{
if (value.Type == "GetCredentials")
{
userProfile.credentialsCard = true;
}
await dc.BeginDialogAsync(nameof(HandleButtonDialog));
}
HandleButtonDialog:
public HandleButtonDialog(BotServices _services, UserProfile _userProfile) : base(Name)
{
botServices = _services ?? throw new ArgumentNullException(nameof(_services));
userProfile = _userProfile;
var waterfallSteps = new WaterfallStep[]
{
GetFeedbackStepAsync,
FeedbackStepAsync,
FeedbackResponseStepAsync,
};
AddDialog(new WaterfallDialog(HBFeedbackDialog));
AddDialog(new TextPrompt("userFeed"));
}
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
{
if...
else if(userProfile.credentialsCard == true)
{
await dc.BeginDialogAsync(HandleCredentialsFeedbackDialog.Name);
}
}
HandleCredentialsFeedbackDialog:
public class HandleCredentialsFeedbackDialog : ComponentDialog
{
public HandleCredentialsFeedbackDialog(BotServices services, UserProfile _userProfile,string dialogId = null) : base(Name)
{
botServices = services ?? throw new ArgumentNullException(nameof(services));
userProfile = _userProfile;
// This array defines how the Waterfall will execute.
var waterfallSteps = new WaterfallStep[]
{
CredsValidate,
GetFeedbackStepAsync,
FeedbackStepAsync,
FeedbackResponseStepAsync,
};
AddDialog(new TextPrompt("userFeed"));
AddDialog(new WaterfallDialog(HBFeedbackDialog, waterfallSteps));
InitialDialogId = HBFeedbackDialog;
}
public async Task<DialogTurnResult> CredsValidate(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
VALIDATE THE CHECKBOX SELECTED VALUES
....
//Invoke Web Service
var qnaReuslt = await MakeBatchRequestCreds(stepContext.Context, finalSearchList.ToArray());
return await stepContext.PromptAsync("userFeed", new PromptOptions
{
Prompt = stepContext.Context.Activity.CreateReply("Did this help?")
});
}
}
Error Stack Trace:
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Microsoft.Bot.Builder.Dialogs.ComponentDialog.RepromptDialogAsync(ITurnContext turnContext, DialogInstance instance, CancellationToken cancellationToken) in d:\a\1\s\libraries\Microsoft.Bot.Builder.Dialogs\ComponentDialog.cs:line 112
at Microsoft.Bot.Builder.Dialogs.ComponentDialog.ResumeDialogAsync(DialogContext outerDc, DialogReason reason, Object result, CancellationToken cancellationToken) in d:\a\1\s\libraries\Microsoft.Bot.Builder.Dialogs\ComponentDialog.cs:line 106
at Microsoft.Bot.Builder.Dialogs.DialogContext.EndDialogAsync(Object result, CancellationToken cancellationToken) in d:\a\1\s\libraries\Microsoft.Bot.Builder.Dialogs\DialogContext.cs:line 196
at AESAiLean.Dialogs.HandleButtonDialog.BeginDialogAsync(DialogContext dc, Object options, CancellationToken cancellationToken) in C:\Users\...\Dialogs\HandleButtonDialog.cs:line 199
at Microsoft.Bot.Builder.Dialogs.DialogContext.BeginDialogAsync(String dialogId, Object options, CancellationToken cancellationToken) in d:\a\1\s\libraries\Microsoft.Bot.Builder.Dialogs\DialogContext.cs:line 113
at AESAiLean.AESAiLeanBot.HandleSubmitActionAsync(ITurnContext turnContext, UserProfile userProfile) in C:\Users\...\Bots\AESAiLeanBot.cs:line 361
I am using NLP Dispatch , through which i have a merged luis and QnA model, I am able to normally call and add my own logic inside top scoring intents block. Now i have a dialog class which i want to be called whenever a top scoring intent is being detected
namespace Microsoft.BotBuilderSamples
{
public class DispatchBot : ActivityHandler
{
private ILogger<DispatchBot> _logger;
private IBotServices _botServices;
public DispatchBot(IBotServices botServices, ILogger<DispatchBot> logger)
{
_logger = logger;
_botServices = botServices;
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// First, we use the dispatch model to determine which cognitive service (LUIS or QnA) to use.
var recognizerResult = await _botServices.Dispatch.RecognizeAsync(turnContext, cancellationToken);
// Top intent tell us which cognitive service to use.
var topIntent = recognizerResult.GetTopScoringIntent();
// Next, we call the dispatcher with the top intent.
await DispatchToTopIntentAsync(turnContext, topIntent.intent, recognizerResult, cancellationToken);
}
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
const string WelcomeText = "I am here to make your bot experience much more easier";
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(MessageFactory.Text($"Hi {member.Name}, I am your IT assistant at your service . {WelcomeText}"), cancellationToken);
}
}
}
private async Task DispatchToTopIntentAsync(ITurnContext<IMessageActivity> turnContext, string intent, RecognizerResult recognizerResult, CancellationToken cancellationToken)
{
switch (intent)
{
case "l_mts-bot-809f":
await ProcessHomeAutomationAsync(turnContext, recognizerResult.Properties["luisResult"] as LuisResult, cancellationToken);
break;
case "q_mts-bot":
await ProcessSampleQnAAsync(turnContext, cancellationToken);
break;
default:
_logger.LogInformation($"Dispatch unrecognized intent: {intent}.");
await turnContext.SendActivityAsync(MessageFactory.Text($"Dispatch unrecognized intent: {intent}."), cancellationToken);
break;
}
}
private Activity CreateResponse(IActivity activity, Attachment attachment)
{
var response = ((Activity)activity).CreateReply();
response.Attachments = new List<Attachment>() { attachment };
return response;
}
private async Task ProcessHomeAutomationAsync(ITurnContext<IMessageActivity> turnContext, LuisResult luisResult, CancellationToken cancellationToken)
{
_logger.LogInformation("ProcessHomeAutomationAsync");
// Retrieve LUIS result for Process Automation.
var result = luisResult.ConnectedServiceResult;
var topIntent = result.TopScoringIntent.Intent;
var entity = result.Entities;
if (topIntent == "welcome")
{
await turnContext.SendActivityAsync(MessageFactory.Text("Hi,This is your IT assistant"), cancellationToken);
}
if (topIntent == "None")
{
await turnContext.SendActivityAsync(MessageFactory.Text("Sorry I didnt get you!"), cancellationToken);
}
if (topIntent == "DateTenure")
{
// Here i want to call my dialog class
}
}
}
private async Task ProcessSampleQnAAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
_logger.LogInformation("ProcessSampleQnAAsync");
var results = await _botServices.SampleQnA.GetAnswersAsync(turnContext);
if (results.Any())
{
await turnContext.SendActivityAsync(MessageFactory.Text(results.First().Answer), cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text("Sorry, could not find an answer in the Q and A system."), cancellationToken);
}
}
}
}
I want as and when my top intent is detected , my custom dialog comes into action and the handling of conversation is then should be handled by my dialog class.
private async Task DispatchToTopIntentAsync(ITurnContext<IMessageActivity> turnContext, string intent, RecognizerResult recognizerResult, CancellationToken cancellationToken)
{
switch (intent)
{
case "l_mts-bot-809f":
//I MADE CHANGES HERE
await dc.BeginDialogAsync(nameof(DIALOG_CLASS_I_WANT_TO_START));
break;
case "q_mts-bot":
await ProcessSampleQnAAsync(turnContext, cancellationToken);
break;
default:
_logger.LogInformation($"Dispatch unrecognized intent: {intent}.");
await turnContext.SendActivityAsync(MessageFactory.Text($"Dispatch unrecognized intent: {intent}."), cancellationToken);
break;
}
}
Just remember to add the dialog to either your main dialog class like this:
public MainDialog(UserState userState)
: base(nameof(MainDialog))
{
_userState = userState;
AddDialog(new DIALOG_CLASS_I_WANT_TO_START());
InitialDialogId = nameof(aDifferentDialogNotShownHere);
}
or to your startup.cs as a transient:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//all the other configure stuff...
// Register dialogs
services.AddTransient<MainDialog>();
services.AddTransient<DIALOG_CLASS_I_WANT_TO_START>();
I am trying to migrate my codes from the old Bot Framework V4(last January) to the new Bot framework v4 design. What I like is to call a dialog when the user input. "Get started". I can do this easily on the previous design but now I can't. Also this is just text now. what if i want to use a LUIS intent? Thank you.
protected override async Task<DialogTurnResult> OnBeginDialogAsync(
DialogContext innerDc,
object options,
CancellationToken cancellationToken = default(CancellationToken))
{
var result = await InterruptAsync(innerDc, cancellationToken);
if (result != null)
{
return result;
}
return await base.OnBeginDialogAsync(innerDc, options, cancellationToken);
}
protected override async Task<DialogTurnResult> OnContinueDialogAsync(
DialogContext innerDc,
CancellationToken cancellationToken)
{
var result = await InterruptAsync(innerDc, cancellationToken);
if (result != null)
{
return result;
}
return await base.OnContinueDialogAsync(innerDc, cancellationToken);
}
private async Task<DialogTurnResult> InterruptAsync(
DialogContext innerDc,
CancellationToken cancellationToken)
{
if (innerDc.Context.Activity.Type == ActivityTypes.Message)
{
var text = innerDc.Context.Activity.Text.ToLowerInvariant();
switch (text)
{
case "help":
case "?":
await innerDc.Context.SendActivityAsync($"Show Help...",
cancellationToken: cancellationToken);
return new DialogTurnResult(DialogTurnStatus.Waiting);
case "get started":
return await innerDc.BeginDialogAsync(nameof(DialogA));
case "cancel":
case "quit":
await innerDc.Context.SendActivityAsync($"Cancelling",
cancellationToken: cancellationToken);
return await innerDc.CancelAllDialogsAsync();
}
}
return null;
}
DialogA
namespace practiceNewBot.Dialogs
{
public class DialogA : CancelAndHelpDialog
{
public DialogA() : base(nameof(DialogA))
{
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
FirstStepAsync,
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> FirstStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"start of dialog a"), cancellationToken);
return await stepContext.PromptAsync(
nameof(ChoicePrompt),
new PromptOptions
{
Prompt = MessageFactory.Text($"choose a dialog"),
Choices = new List<Choice>
{
new Choice
{
Value = "Dialog A Child",
Synonyms = new List<string>
{
"dialog a child",
},
},
new Choice
{
Value = "Dialog B Child",
Synonyms = new List<string>
{
"dialog b chilc",
},
},
new Choice
{
Value = "Cancel",
Synonyms = new List<string>
{
"cancel",
},
},
},
RetryPrompt = MessageFactory.Text("Please choose one of the options."),
},
cancellationToken: cancellationToken);
}
private async Task<DialogTurnResult> SecondStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var response = (stepContext.Result as FoundChoice).Value.ToLower();
switch (response)
{
case "dialog a child":
return await stepContext.BeginDialogAsync(nameof(DialogA), cancellationToken: cancellationToken);
case "dialog b child":
return await stepContext.BeginDialogAsync(nameof(DialogB), cancellationToken);
case "cancel":
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Ok, Cancelled."));
return await stepContext.CancelAllDialogsAsync(cancellationToken: cancellationToken);
default:
break;
}
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"end of dialog a"), cancellationToken);
return await stepContext.EndDialogAsync();
}
}
}