ToDo Bot | Part 6 | Coding the Create Task Dialog | Microsoft Bot Framework

This is the bot series on ToDo Bot where we will be creating a Chatbot to create, view, and delete tasks. In this part, we will be writing the logic to ask the user new tasks and saving those tasks in a List.

Pre-requisites

  1. ToDo Bot | Part 1 | Setup the Development Environment | Microsoft Bot Framework
  2. ToDo Bot | Part 2 | Welcome Card | Adaptive Cards | Microsoft Bot Framework
  3. ToDo Bot | Part 3 | Planning and Designing the Bot | Microsoft Bot Framework
  4. ToDo Bot | Part 4 | Coding the Main Dialog | Microsoft Bot Framework
  5. ToDo Bot | Part 5 | Creating Sub Dialogs | Microsoft Bot Framework

Video


Coding the Create Task Dialog

We have already created the sub dialogs in our last part. Now, declare the steps in the Waterfall Dialog steps in the constructor of CreateTaskDialog.cs.

public CreateTaskDialog() : base(nameof(CreateTaskDialog))
        {
            var waterfallSteps = new WaterfallStep[]
            {
                TasksStepAsync,
                ActStepAsync,
                MoreTasksStepAsync,
                SummaryStepAsync
            };

            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
            AddDialog(new TextPrompt(nameof(TextPrompt)));
            AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
            AddDialog(new CreateMoreTaskDialog());

            InitialDialogId = nameof(WaterfallDialog);
        }

Create the Methods for the steps declared in the constructor. Make sure all your methods are async.

private async Task<DialogTurnResult> TasksStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        private async Task<DialogTurnResult> MoreTasksStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

Method Definition:
  1. TasksStepAsync – This will be Text Prompt asking the user to provide the task.
  2. ActStepAsync – Capture the user response from TasksStepAsync and ask the user if he/she wants to add more tasks. This will be a Confirm Prompt that shows the user two buttons (Yes and No). The value returned by these buttons is of type boolean.
  3. MoreTasksStepAsync – Capture the boolean response. If Yes, begin another dialog CreateMoreTaskDialog.cs (Create a new class file with a constructor similar to CreateTaskDialog.) If No, move to the next step.
  4. SummaryStepAsync – Send all the tasks provided back to the user.

Ask the user to provide the task in TasksStepAsync.

private async Task<DialogTurnResult> TasksStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text("Please give the task to add.")
            }, cancellationToken);
        }

Capture the above user response and ask the user if he/she wants to add more tasks in ActStepAsync.

private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var userDetails = (User)stepContext.Options;
            stepContext.Values["Task"] = (string)stepContext.Result;
            userDetails.TasksList.Add((string)stepContext.Values["Task"]);

            return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text("Would you like to Add more tasks?")
            }, cancellationToken);
        }

In the first statement we are using the options provided in the form User Object. In the last part, when we called the CreateTaskDialog from the MainDialog, we had passed the User object. This is the same object used to store user data.

if ("Create Task".Equals(operation))
            {
                return await stepContext.BeginDialogAsync(nameof(CreateTaskDialog), new User(), cancellationToken);
            } 

Create a new List property inside the User.cs. This will be used to store the tasks provided by the user.

public List<string> TasksList = new List<string>();

The confirm prompt will show the user 2 buttons with text Yes and No. This returns the value of the datatype boolean. Based on the user selection, MoreTasksStepAsync method performs following actions.

private async Task<DialogTurnResult> MoreTasksStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var userDetails = (User)stepContext.Options;
            if ((bool)stepContext.Result)
            {
                return await stepContext.BeginDialogAsync(nameof(CreateMoreTaskDialog), userDetails, cancellationToken);
            }
            else
            {
                return await stepContext.NextAsync(userDetails, cancellationToken);
            }
        }

If user clicked on Yes, we call a new dialog CreateMoreTaskDialog.cs else we move to the next step. Below is the new dialog created.

using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using System.Threading;
using System.Threading.Tasks;

namespace ToDoBot.Dialogs.Operations
{
    public class CreateMoreTaskDialog : ComponentDialog
    {
        public CreateMoreTaskDialog() : base(nameof(CreateMoreTaskDialog))
        {
            var waterfallSteps = new WaterfallStep[]
            {
                TasksStepAsync,
                ConfirmStepAsync,
                SummaryStepAsync,
            };

            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
            AddDialog(new TextPrompt(nameof(TextPrompt)));
            AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));

            InitialDialogId = nameof(WaterfallDialog);
        }

        private async Task<DialogTurnResult> TasksStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text("Please give the task to add.")
            }, cancellationToken);
        }

        private async Task<DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var userDetails = (User)stepContext.Options;
            stepContext.Values["Task"] = (string)stepContext.Result;
            userDetails.TasksList.Add((string)stepContext.Values["Task"]);

            return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text("Would you like to Add more tasks?")
            }, cancellationToken);
        }

        private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var userDetails = (User)stepContext.Options;
            if ((bool)stepContext.Result)
            {
                return await stepContext.ReplaceDialogAsync(InitialDialogId, userDetails, cancellationToken);
            }
            else
            {
                await stepContext.Context.SendActivityAsync(MessageFactory.Text("Ok."));
                return await stepContext.EndDialogAsync(userDetails, cancellationToken);
            }
        }
    }
}

This dialog will take the user input repeatedly unless user clicks on No. At the end, the control goes back to the CreateTaskDialog with User Object option.

In the SummaryStepAsync, we show the user all the tasks provided by him/her and move the control back to the MainDialog which in turn repeats it from the beginning.

private async Task<DialogTurnResult> SummaryStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var userDetails = (User)stepContext.Result;
            await stepContext.Context.SendActivityAsync(MessageFactory.Text("Here are the tasks you provided - "), cancellationToken);
            for (int i = 0; i < userDetails.TasksList.Count; i++)
            {
                await stepContext.Context.SendActivityAsync(MessageFactory.Text(userDetails.TasksList[i]), cancellationToken);
            }

            return await stepContext.EndDialogAsync(userDetails, cancellationToken);
        }

It is completely optional to pass the userDetails as options while ending the dialog. If you want to make use of the userDetails inside MainDialog, then you can pass otherwise pass null.

Run the project and test the output.

There is a drawback in this approach. The tasks provided gets lost when the control goes to the MainDialog. To fix that, we will be using the Database to store the tasks.

Thank you All!!! Hope you find this useful.


Leave a Reply

Up ↑

%d