How to Separate Dialogs in Triggered Actions from Prompts (Microsoft Botbuilder SDK) - node.js

Note
I am using Microsoft Botbuilder SDK in node.js using ES6 babel.
The Problem
I have a dialog, '/MainMenu', that prompts the user for a free-response reply in order to dive into another, more relevant dialog. I also, however, want the user to be able to trigger an action that is completely irrelevant to subject matter (i.e. a dialog asking the bot, "how are you?"), returning back to the original MainMenu dialog just as they left off. I understand that in the documentation for the SDK, onSelectAction can be used to put the triggered dialog on top of the stack rather than replacing the entire stack, but once the '/HowAreYou' dialog ends, the bot also thinks that response was for the initial MainMenu prompt, replying with "I didn't understand. Please try again," like so:
Code
// I am using the builder.Library routing standard, and have confirmed that
// this gets triggered as expected. this dialog exists in a different file
lib.dialog('/HowAreYou', [
(session, args, next) => {
session.send('I\'m doing well. Thanks for asking!');
builder.Prompts.text(session, 'How are you doing today?');
}, (session, results) => {
session.endDialog('Good to hear that!');
}
]).triggerAction({
matches: /^how are you?$/i,
onSelectAction: (session, args, next) => {
// Add the help dialog to the top of the dialog stack (override the
// default behavior of replacing the stack)
session.beginDialog(args.action, args);
}
});
bot.dialog('mainMenu', [
(session, args, next) => {
builder.Prompts.text(session, 'Hi there! What can I do for you today?');
},
(session, results) => {
session.endConversation('Goodbye!');
}
]).beginDialogAction('weatherAction', '/Weather', {
matches: /^weather$/i,
}).beginDialogAction('sportsAction', '/Sports', {
matches: /^sports$/i,
}).beginDialogAction('cookingAction', '/Cooking', {
matches: /^cooking$/i,
});
Desired Behavior
Although the current result is very close to the desired behavior, I ideally want the bot to reply with the same MainMenu prompt it began with, without saying it didn't understand after the HowAreYou dialog finishes.
The Question
Is this possible? If so, how? If not, what are alternatives?
Thank you for any help you can give.

There is an explain on GitHub at https://github.com/Microsoft/BotBuilder/issues/2421, with what we can realize that the Prompts are built-in dialog which will handle the input validation.
As you first step in the Prompts dialog in mainMenu, and then you trigger the HowAreYou dialog when the first Prompts dialog is waiting for the input text.
Then the HowAreYou end as session.endDialog('Good to hear that!'); without an result for the first Prompts dialog in mainMenu, which is failed in the validation.
the root cause should be equivalent to input an empty text for builder.Prompts.text(session, 'Hi there! What can I do for you today?');.
update
Found the promptAfterAction property of IPromptOptions for prompt dialog, and i think about this issue. I think this should be by design.
As the sentence I don't understand... is a default text for property retryPrompt. So when you end the HowAreYou dialog and back the to mainPage dialog stack. The prompt dialog will restart and send the retryPrompt to user, which raises your issue.
For the accessibility, you can try to use:
builder.Prompts.text(session, 'Hi there! What can I do for you today?', {
retryPrompt: 'Hi there! What can I do for you today?'
});

Related

Saving data in conversation exits

I have a google actions app and I am trying to save a variable between conversations. However if the cancel intent is invoked (the user says quit, exit, cancel etc. invoking actions_intent_CANCEL event) this data does not persist to the next conversation. I also have a custom save intent which too exits the conversation but it works fine.
This does not work (intent with event actions_intent_CANCEL)
app.intent('Exit', (conv) => {
if (conv.user.verification === 'VERIFIED') {
conv.user.storage.avar = 'somevalue';
}
conv.close('Goodbye!');
});
but this works (custom intent with no event)
app.intent('Save', (conv) => {
if (conv.user.verification === 'VERIFIED') {
conv.user.storage.avar = 'somevalue';
}
conv.close('Goodbye!');
});
In both cases the conversation exits and in the response there is the correct value for the variable.
"userStorage": "{\"data\":{\"avar\":\"somevalue\"}}"
However when I activate my app again, if exited via the Exit intent the data is missing or wrong. Does anyone know if this is bug or am I doing something wrong?
Help is appreciated!

ChatBot back to previous dialog

I've created a chatbot using Node.js and the flow of dialogs works fine til the endDialog. Im having issues implementing a back option so it can jump back only to previous dialog. Can anyone please guide me how to solve that?
.reloadAction(
"restartBenefits", "Ok. Let's start over.",
{
matches: /^start over$|^restart$/i
}
)
.cancelAction(
"cancelRequest", "Thank you for reaching out, Good bye!",
{
matches: /^nevermind$|^cancel$|^cancel.*request/i,
confirmPrompt: "This will cancel your request. Are you sure?"
}
);
Use a customAction
In this case you can listen for whatever you want to be a keyword for "back" and then simply route the user back to that dialog using replaceDialog
bot.customAction({
matches: /back|last/gi, //whatever prompts you want to match.
onSelectAction: (session, args, next) => {
session.replaceDialog(PreviousDialog); //variable with the last dialog in it, set that somewhere, such as the end of your previous dialog
}
})
I think that at the final step of the dialog waterfall you need to add the last lines in this sample step:
/**
* This is the final step in the main waterfall dialog.
* It wraps up the sample "book a flight" interaction with a simple confirmation.
*/
async finalStep(stepContext) {
// If the child dialog ("bookingDialog") was cancelled or the user failed to confirm, the Result here will be null.
if (stepContext.result) {
const result = stepContext.result;
// Now we have all the booking details.
// This is where calls to the booking AOU service or database would go.
// If the call to the booking service was successful tell the user.
const timeProperty = new TimexProperty(result.travelDate);
const travelDateMsg = timeProperty.toNaturalLanguage(new Date(Date.now()));
await stepContext.context.sendActivity(ActivityFactory.fromObject(this.lgTemplates.evaluate('BookingConfirmation', {
Destination: result.destination,
Origin: result.origin,
DateMessage: travelDateMsg
})));
}
// =====> HERE: Restart the main dialog with a different message the second time around
return await stepContext.replaceDialog(this.initialDialogId, { restartMsg: 'What else can I do for you?' });
}
Just change the this.initialDialogId accordingly.

How to Prevent Both beginDialogAction and triggerAction from Executing in Microsoft Bot Builder SDK

Note: I am using Microsoft Bot Builder's SDK in Node.js.
The Desired Task
I am attempting to use beginDialogAction to initiate another sub-dialog so that by the end of the sub-dialog dialog, the stack would go right back to the dialog they left off. At the same time, however, I want to give the user the option of activating this same sub-dialog as a triggerAction in any other conversation.
The Problem
Implementing as follows puts TWO COPIES of the sub-dialog in the stack, by both the triggerAction and beginDialogAction. This is unwanted behavior because once one of those dialogs complete, a duplicate of that dialog runs again.
Here is the code:
// user dialog
bot.dialog('/', [
function (session, args, next) {
session.send('Starting root dialog');
},
function (session) {
session.send('Ending root dialog.');
]).beginDialogAction('addUser', 'UserAdd', {
matches: /^user add$/i
});
// branch dialog
bot.dialog('UserAdd', [
function (session, args, next) {
session.send('Adding user.');
}
]).triggerAction({
matches: /^user add$/i
})
The Question
What is the correct way to enable both a beginDialogAction and triggerAction of a dialog, but only run that dialog ONCE if it is triggered by a beginDialogAction, so that the root dialog can continue where it left off? If this is not traditional way of thinking to use this framework, I welcome other perspectives.
This is unwanted behavior because once one of those dialogs complete, a duplicate of that dialog runs again.
I couldn't reproduce this issue. The problem in your code is that you didn't end the UserAdd dialog after it is triggered whether it is called by beginDialogAction or triggerAction, this makes the dialog stack always stays inside of this UserAdd dialog and never end.
Based on your description, I don't think there would be any conflicts between beginDialogAction and triggerAction, tried to modify your code as below and it works fine on my side:
bot.dialog('/', [
function (session, args, next) {
session.send('Starting root dialog');
builder.Prompts.text(session, "How many people are in your party?");
},
function (session, results) {
builder.Prompts.text(session, "Who will be there?");
},
function (session, results) {
session.send('Ending root dialog.');
session.endDialog();
}
])
.beginDialogAction('addUser', 'UserAdd', {
matches: /^user add$/i
})
// // branch dialog
bot.dialog('UserAdd', [
function (session, args, next) {
session.send('Adding user.');
session.endDialog();
}
]).triggerAction({
matches: /^user add$/i
});
If there's any further concern or more problem, please feel free to leave a comment.

Retry prompt customization

I am using MS bot builder node.js SDK. Before one of the recent updates, when the prompt is retried, it was sending the same message text to the user as the retry prompt.
However, now it is sending the default text message in the system, which is "I didn't understand.Please try again". However, I want retry prompts always be the same as the original message and if possible want to apply this globally, meaning I don't want to customize retry prompt for every prompt I am sending to the user.
I had been looking around, but couldn't find a way yet.
Thanks!
You can modify the prompts to automatically set the prompt as the retry prompt. The Prompts interface shows how the args are passed in to the base Prompt classes, so we can modify this prompt behavior by accessing the method in Prompts.
Here's an example of how to do it with Prompts.confirm
const promptPrefix = 'BotBuilder:prompt-';
bot.dialog('/', [
(session) => {
builder.Prompts.confirm(session, 'Say yes or no');
},
(session, args) => {
session.endConversation('You said: ' + session.message.text);
}
]);
builder.Prompts.confirm = (session, prompt, options) => {
var args = options || {};
args.prompt = prompt || args.prompt;
// If options.retryPrompt was passed in use this, otherwise use prompt
args.retryPrompt = args.retryPrompt || args.prompt;
session.beginDialog(promptPrefix + 'confirm', args);
};
The modified Prompts.confirm in action:
One option is to send the retry prompt as an option to the prompt. For example:
builder.Prompts.number(session, "What's the number?", {
retryPrompt: "What's the number?"
});
But you will have to configure that on every prompt.

How to call luis dialog from general dialog and luis dialog

I am using node js, I want to call luis dialog("morningUpdate") from
general dialog("work")
dialog.matches('morningUpdate',[
function (session, args, next) {
}
]);
bot.dialog('/work', [
function (session, args) {
//how to call here "morningUpdate" dialog
}
]);
how we can achieve this.
To call a new dialog, you can just use session.beginDialog('nameOfDialog');. Take a look at the basic and advanced Multi Dialogs samples.
If you need a LUIS sample, take a look at this one.
here is actual answer
link for code
dialog.matches('morningUpdate', 'morningUpdate');
bot.dialog('morningUpdate', [
function (session, args, next) {
}
]);

Resources