Trevor Daniel
Trevor Daniel

Reputation: 3974

MS Bot Framework Conversation with Decisions

I have successfully got a simple "What's the weather" bot using Luis up and running on both Skype and Twilio and it's excellent.

I have now been tasked with setting up what I can only think of describing as a "conversation" bot.

I have looked at as many examples as I can find on the interweb but I am unsure how to approach developing it, and I don't know whether I should be using the FormBuilder for my scenario.

Here is a flow chart of part of what I am trying to do...

enter image description here

I have got my form working down to the part where it branches at "Bill Available"...

I cannot work out how to "change direction" based on the answer.

Upvotes: 3

Views: 416

Answers (2)

Trevor Daniel
Trevor Daniel

Reputation: 3974

This, as suspected, turned out to be much easier that I thought..

Here is my solution...

This is my controller:

/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
    // check if activity is of type message
    if (activity != null && activity.GetActivityType() == ActivityTypes.Message)
    {
        await Conversation.SendAsync(activity, () => new Dialogs.BillSharkDialog());
    }
    else
    {
        HandleSystemMessage(activity);
    }
    return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
}

And here is the "Dialog" with just 2 steps...

[Serializable]
public class BillSharkDialog : IDialog<object>
{
    Model.Customer customer = new Model.Customer();

    public async Task StartAsync(IDialogContext context)
    {
        context.Wait(WelcomeMessageAsync);
    }
    public async Task WelcomeMessageAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
    {
        IMessageActivity message = await argument;
        await context.PostAsync("We're excited to start helping you save! Let's start by getting your name?");
        context.Wait(CaptureCustomerNameAsync);
    }
    public async Task CaptureCustomerNameAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
    {
        IMessageActivity message = await argument;
        customer.customerName = message.Text;
        await context.PostAsync($"Thanks {message.Text}. Now we need your email address?");
        context.Wait(CaptureCustomerEmailAsync);
    }
}

You can obviously change the route by checking the incoming message..

Here is an example:

public async Task DoesCustomerHaveBillAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
    {
        IMessageActivity message = await argument;
        switch (message.Text.ToLower())
        {
            case "yes":
                await context.PostAsync($"Great. Go ahead and take a picture of the first couple of pages and attach them to this conversation.\n\n\nWhen you have finished, please send the message 'finished'.");
                context.Wait(CustomerHasBillAsync);
                break;
            case "no":
                await context.PostAsync($"That's OK. Do you happen to have the login information for your provider account?");
                context.Wait(CustomerDoesntHaveBillAsync);
                break;
            default:
                await context.PostAsync($"Sorry, I didn't undestand. Please reply with 'yes' or 'no'.");
                context.Wait(DoesCustomerHaveBillAsync);
                break;
        }
    }

Upvotes: 1

Ezequiel Jadib
Ezequiel Jadib

Reputation: 14787

You could consider using the SetNext method of a FormBuilder field to decide what should be coming next based on another field value.

You can take a look to the ContosoFlowers sample. In the Order form; something similar is being done.

 public static IForm<Order> BuildOrderForm()
        {
            return new FormBuilder<Order>()
                .Field(nameof(RecipientFirstName))
                .Field(nameof(RecipientLastName))
                .Field(nameof(RecipientPhoneNumber))
                .Field(nameof(Note))
                .Field(new FieldReflector<Order>(nameof(UseSavedSenderInfo))
                    .SetActive(state => state.AskToUseSavedSenderInfo)
                    .SetNext((value, state) =>
                    {
                        var selection = (UseSaveInfoResponse)value;

                        if (selection == UseSaveInfoResponse.Edit)
                        {
                            state.SenderEmail = null;
                            state.SenderPhoneNumber = null;
                            return new NextStep(new[] { nameof(SenderEmail) });
                        }
                        else
                        {
                            return new NextStep();
                        }
                    }))
                .Field(new FieldReflector<Order>(nameof(SenderEmail))
                    .SetActive(state => !state.UseSavedSenderInfo.HasValue || state.UseSavedSenderInfo.Value == UseSaveInfoResponse.Edit)
                    .SetNext(
                        (value, state) => (state.UseSavedSenderInfo == UseSaveInfoResponse.Edit)
                        ? new NextStep(new[] { nameof(SenderPhoneNumber) })
                        : new NextStep()))
                .Field(nameof(SenderPhoneNumber), state => !state.UseSavedSenderInfo.HasValue || state.UseSavedSenderInfo.Value == UseSaveInfoResponse.Edit)
                .Field(nameof(SaveSenderInfo), state => !state.UseSavedSenderInfo.HasValue || state.UseSavedSenderInfo.Value == UseSaveInfoResponse.Edit)
                .Build();
        }
    }
}

Upvotes: 0

Related Questions