Bot Dialog - microsoft-campus-community/workshop-shopping-list-bot GitHub Wiki
dialogs
folder open in your IDE. Suppose you do not have it open take a look at the basics first.
Prerequisite: This page assumes that you forked the repository and have the 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
.
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. ATextPrompt
sends a message in the chat and awaits a textual response. TheTextPrompt
will pass the response to the next step (queryUnitStep
) with thestepContext
.
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); }
- In the beginning, the partial item passed as input from the main dialog is retrieved.
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 theTextPrompt
, through the context. TheitemNameStep
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 newUnitDialog
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); }
- The basic principle is the same as for the
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);