Bot Dialog - microsoft-campus-community/workshop-shopping-list-bot GitHub Wiki

Prerequisite: This page assumes that you forked the repository and have the dialogs folder open in your IDE. Suppose you do not have it open take a look at the basics first.

General Introduction into Dialogs

Since the chatbot is a participant in your messaging chat, you interact with it similar to a real person using dialogues. A dialog consists of a message-response pattern, i.e., an exchange of messages. Usually, a dialog is not done after one message but consists of multiple steps. Every dialog has a unique identifier. You can find an example dialog in the following figure.

[/images/Bot/BotDialog.png ](/microsoft-campus-community/workshop-shopping-list-bot/wiki/height=350px|Picture-explaining-dialog-and-dialog-steps-with-the-interaction-of-adding-item-to-shopping-list.)

Internally, the bot maintains a stack of dialogs. In the beginning, the stack contains the main dialog. When receiving a message, the main dialog continues with the dialog at the top of the stack or starts a new dialog if there is none to continue. The new dialog to start depends on the message received. For example, a chat participant wants to add a new item.

To learn how dialogs are implemented, you are going to look at addItemDialog.ts.

Add Item Dialog

This dialog aims to ask the user to provide all information to add a new item to the shopping list. The necessary information is a name for the item and an optional unit describing the item's quantity, e.g., 1 kg.

The main dialog will push an AddItemDialog when the chat participant wants to add a new item to the shopping list. If the chat participants provide any information about the item, a partial item (e.g., only an item's name provided) is passed to the AddItemDialog. Otherwise, the AddItemDialog will have to query the missing information so that the result of the dialog is an item with a name (bananas) and an optional unit consisting of a numerical value and a measure (kg, liters, etc). Hence, the AddItemDialog will ask the chat two questions (1) "Which item would you like to add?" (2) "Do you want to add a unit, e.g., 5 kg?". These questions translate to the itemNameStep, the queryUnitStep, and the finalStep to gather and return the constructed item. The WaterfallDialog binds all these three steps together and manages for us which step is next. Each step is passed the context (stepContext) from the WaterfallDialog. Steps can use the context to pass objects through multiple steps and pass the result from one step to the next. As a result, a dialog like the AddItemDialog can be built upon existing dialogs, like the WaterfallDialog.

The details and implementation of these steps are explained below.

Open the source code for this dialog. You can find it under shopping-list-bot/src/dialogs/addItemDialog.ts.

  1. itemNameStep:
    • In the beginning, the partial item passed as input from the main dialog is retrieved. const item = stepContext.options as Item;{.ruby}
    • If the partial item has no name, then the chat participants are queried for an item name. How do you query the participants? You create a text message with the question you would like to ask and invoke the TextPrompt with the query. A TextPrompt sends a message in the chat and awaits a textual response. The TextPrompt will pass the response to the next step (queryUnitStep) with the stepContext.
    if (!item.itemName) {
        const messageText = 'Which item would you like to add?';
        const message = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput);
        return await stepContext.prompt(TEXT_PROMPT, { prompt: message });
    }
    
    • If the partial item already has a name, there is no need to ask the chat participants for it, and the waterfall continues with the next step. As input for the next step, the existing item's name is passed.
    } else {
        return await stepContext.next(item.itemName);
    }
    
  2. queryUnitStep:
    • The basic principle is the same as for the itemNameStep. Get the partial item, check if the unit is already present, and query the chat participants for the unit if it is not present.
    • In addition to the item, the queryUnitStep receives the result from the previous step, which ended with the TextPrompt, through the context. The itemNameStep guarantees that the context contains the item's name. The item's name is stored in the partial item.
    const itemNameResult = stepContext.result as string;
    const item = stepContext.options as Item;
    item.itemName = itemNameResult;
    
    • Instead of asking for the unit itself, the AddItemDialog starts a new UnitDialog to do so. This is done for better decoupling and modularization to increase maintainability and readability.
    if (!item.unit) {
        // pushes the unit dialog to query for a unit and continues here when the unit dialog is completed.
        return await stepContext.beginDialog(UNIT_DIALOG);
    }
    
  3. finalStep:
    • First, the item's unit result from the previous step is stored in the partial item which completes all information necessary to add the item.
    const unit = stepContext.result as Unit;
    const item = stepContext.options as Item;
    item.unit = unit;
    
    • Now that all information is available, it is returned as the result to the dialog below in the stack. This ends the AddItemDialog.
    const result: IAddItemDialogResult = {
        dialogId: this.id,
        itemToAdd: item
     };
     return await stepContext.endDialog(result);
    

NEXT: Bot Calling Shopping List API