Using Formflow Dialog in Bot Framework

Using FormFlow dialog in Bot Framework

Bot Builder V3 FormFlow dialogs can now be used in the context of a V4 bot by using the recently released Bot.Builder.Community.Dialogs.FormFlow library.

Your HelpForm can be added to a V4 DialogSet in the same way as other V4 ComponentDialogs:

_dialogs.Add(FormDialog.FromForm(HelpForm.BuildForm));

Here is a more complete example:

Accessors:

public class TestEchoBotAccessors
{
public TestEchoBotAccessors(ConversationState conversationState)
{
ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
}

public ConversationState ConversationState { get; }
public IStatePropertyAccessor<DialogState> ConversationDialogState { get; set; }
}

Startup.cs ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
services.AddBot<TestEchoBotBot>(options =>
{
IStorage dataStore = new MemoryStorage();
options.State.Add(new ConversationState(dataStore));
options.Middleware.Add(new AutoSaveStateMiddleware(options.State.ToArray()));

var secretKey = Configuration.GetSection("botFileSecret")?.Value;
var botFilePath = Configuration.GetSection("botFilePath")?.Value;

// Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection.
var botConfig = BotConfiguration.Load(botFilePath ?? @".\TestEchoBot.bot", secretKey);
services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded. ({botConfig})"));

// Retrieve current endpoint.
var environment = _isProduction ? "production" : "development";
var service = botConfig.Services.Where(s => s.Type == "endpoint" && s.Name == environment).FirstOrDefault();
if (!(service is EndpointService endpointService))
{
throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
}

options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
});

services.AddSingleton(sp =>
{
var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
var conversationState = options.State.OfType<ConversationState>().FirstOrDefault();
var accessors = new TestEchoBotAccessors(conversationState)
{
ConversationDialogState = conversationState.CreateProperty<DialogState>("DialogState")
};
return accessors;
});
}

Bot code:

public class TestEchoBotBot : IBot
{
private readonly TestEchoBotAccessors _accessors;
private DialogSet _dialogs;

public TestEchoBotBot(TestEchoBotAccessors accessors, ILoggerFactory loggerFactory)
{
if (loggerFactory == null)
{
throw new System.ArgumentNullException(nameof(loggerFactory));
}

_dialogs = new DialogSet(accessors.ConversationDialogState);
_dialogs.Add(FormDialog.FromForm(HelpForm.BuildForm));
_accessors = accessors ?? throw new System.ArgumentNullException(nameof(accessors));
}

public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
var dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
if (turnContext.Activity.Text?.ToUpper() == "HELP")
{
await dialogContext.BeginDialogAsync(typeof(HelpForm).Name, null, cancellationToken);
}
else
{
var dialogResult = await dialogContext.ContinueDialogAsync(cancellationToken);
if ((dialogResult.Status == DialogTurnStatus.Cancelled || dialogResult.Status == DialogTurnStatus.Empty))
{
var responseMessage = $"You sent '{turnContext.Activity.Text}'\n";
await turnContext.SendActivityAsync(responseMessage);
}
}
}
}
}

using formflow with botbuilder v4

While FormFlow would be well-suited for this task, you are correct that there is no FormFlow in V4. However, V4 does have waterfall dialogs that may well be just as good for what you're trying to do. A waterfall dialog consists of waterfall steps that are like mini-dialogs that prompt the user for information. Have a look at the documentation to see how to use waterfall dialogs: https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-dialog-manage-conversation-flow

Bot Framework Formflow Dialog with list?

I guess I'm late, but I've got an answer!
FormFlow Dialog doesn't allow you to pass List as field to collect in conversation with user :(

Allowed types:

  • Integral – sbyte, byte, short, ushort, int, uint, long, ulong
  • Floating point - float, double
  • String
  • DateTime
  • Enum
  • List of enum

Source

P.S. Recently I faced the same problem: my questions were stored in external database and I wanted bot to ask them, but there is no clear way to do it :( Anyway, requesting expert help for this question!

How to catch exceptions thrown from FormFlow dialog?

Try this-

Message Controller

public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}

And add a new class Root Dialog:

Root Dialog

public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);

return Task.CompletedTask;
}

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var message = await result;
FormDialog<CustomerDetails> customerForm = new FormDialog<CustomerDetails>(new CustomerDetails(), CustomerDetails.BuildForm, FormOptions.PromptInStart);
context.Call(customerForm, FormSubmitted);
}

public async Task FormSubmitted(IDialogContext context, IAwaitable<CustomerDetails> result)
{
try
{
var form = await result;
await context.PostAsync("Thanks for your response.");
}
catch (FormCanceledException<SoftwareRequest> e)
{
string reply;
if (e.InnerException == null)
{
reply = $"Thanks for filling out the form.";

}
else
{
reply = $"Sorry, I've had a short circuit. Please try again.";
}
context.Done(true);
await context.PostAsync(reply);
}
}

NOTE

Please change CustomerDetails to the name of your form class.

FormFlow ChatBot using microsoft bot framework

Assume the first options i provide are support,contact details and other information, when user selects I have to display a set of options and when he selects contact details I would display another set of options, another set for other information. How would I do this?

If you’d like to display tooloption and other field(s) conditionally, you can try to use the SetActive method to specify that the field should only be enabled if user selected the specific option. The following code snippet is for your reference.

return new FormBuilder<SupportBox>()
.Message("Welcome to the Support Bot!")
.Field(nameof(supportoption))
.Field(new FieldReflector<SupportBox>(nameof(tooloption))
.SetActive(state=>state.supportoption== SupportOptions.Specific)
)
.Field(new FieldReflector<SupportBox>(nameof(contactinformation))
.SetActive(state => state.supportoption == SupportOptions.ContactInformation)
)
.OnCompletion(processOrder)
.Build();

Test result:

enter image description here

How to pass a result when calling CancelAllDialogsAsync in bot framework?

Our solution is to store the utterance in the user state.
The MainDialog checks the userstate if an utterance is stored. If so it will answer it. No utterance is stored in the user state the main dialog it will ask the user 'What can I do for you?'.



Related Topics



Leave a reply



Submit