In Bot Framework V4, it is explained that one can access the user state or conversation state by creating a dialog context in OnTurnAsync
var dc = await Dialogs.CreateContextAsync(turnContext);
or using an accessor in a dialog context class
var state = await HogehogeSettingAccessor.GetAsync(stepContext.Context);
however, how do I access them before sending a message to the dialog?
I'm currently developing a Directline API and would like to refer to the language setting before sending the first message (e.g., ignore the user input if the written language does not match to the setting).
private async Task OnMessageReceive(SocketMessage socketMessage)
{
if (IsLanguageMatch(socketMessage)){
await channel.SendMessageAsync(response);
}
}
How do I achieve this?
You can have the OnTurnAsync start the first/main Dialog only of your condition is met. When a user sends a message for the first time to the bot, there won't be any active Dialog. You can take advantage of that condition and add yours there, only starting the Dialog if both are met:
// Getting the bot accessor state you want to use
LanguageAccessor languageAccessor = await _accessors.LanguageAccessor.GetAsync(turnContext, () => new LanguageAccessor(), cancellationToken);
// Every step sends a response. If no dialog is active, no response is sent and turnContext.Responded is null
//where turnContext.Activity.Text is the message sent by the user
if (!turnContext.Responded && ((languageAccessor.property)turnContext.Activity.Text))
The OnTurnAsync should look something like this:
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
// Establish dialog state from the conversation state.
DialogContext dc = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
// Get the user's info.
LanguageAccessor languageAccessor = await _accessors.LanguageAccessor.GetAsync(turnContext, () => new LanguageAccessor(), cancellationToken);
await _accessors.UserInfoAccessor.SetAsync(turnContext, userInfo, cancellationToken);
// Continue any current dialog.
DialogTurnResult dialogTurnResult = await dc.ContinueDialogAsync();
// Every dialog step sends a response, so if no response was sent,
// then no dialog is currently active and the Else if is entered.
if (!turnContext.Responded && ((languageAccessor.property)turnContext.Activity.Text))
{
//This starts the MainDialog if there's no active dialog when the user sends a message
await dc.BeginDialogAsync(MainDialogId, null, cancellationToken);
}
//Else if the validation is not passed
else if (!turnContext.Responded && !
((languageAccessor.property)turnContext.Activity.Text))
{ await turnContext.SendActivityAsync("Thank you, see you next time"); }
}
}
The other option would be to send the accessor object to the Dialog where you want to use it and use the first Dialog's Waterfall step to either continue the dialog if the validation is met or end it if not.
The waterfall step should look something like this:
private async Task<DialogTurnResult> ValidationFirstStepAsync(
WaterfallStepContext stepContext,
CancellationToken cancellationToken = default(CancellationToken))
{
// Access the bot UserInfo accessor so it can be used to get state info.
LanguageAccessor languageAccessor = await
_accessors.LanguageAccessor.GetAsync(stepContext.Context, null,
cancellationToken);
if ((languageAccessor)stepContext.Context.Activity.Text)
{
await stepContext.Context.SendActivityAsync(
"Hi!");
return await stepContext.NextAsync();
}
else
{
await stepContext.Context.SendActivityAsync("Sorry, your language is not supported");
return await stepContext.EndDialogAsync(); }
}
}