conversationUpdate event not getting user input - node.js

I use following method to ask question by bot when user connects first time. But after user answering the question, instead of going to next step bot goes to new dialog.
This works fine with emulator, but not with directline api.
bot.on('conversationUpdate', function (message) {
if (message.membersAdded) {
message.membersAdded.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
bot.beginDialog(message.address, '/startConversation');
}
});
}
});
bot.dialog('/startConversation', [
function (session) {
builder.Prompts.text(session, 'I\'m the SPF VA Assistant. How may I address you?');
},
function (session, results) {
session.userData.name = results.response;
session.send('Hi, %s, Please choose one of the options below to lodge a Police report.', session.userData.name);
var cards = getReportsAsCards();
var reportOptions = new builder.Message(session)
.attachmentLayout(builder.AttachmentLayout.carousel)
.attachments(cards);
session.send(reportOptions).endDialog();
}
]);

After you send the question "Please choose one of the options below..." you are calling endDialog(). This is what causes it to go to the new dialog like you said.
If you want to prompt the user for input, use a Choice Prompt, and add another step in your waterfall dialog to handle the user's choice input.
For example, to get the user's name, first ask the question using a text prompt, then handle the user's response in step 2 of the waterfall dialog. The user's response text will be available in the results.response object in step 2.
bot.dialog('greetings', [
// Step 1
function (session) {
builder.Prompts.text(session, 'Hi! What is your name?');
},
// Step 2
function (session, results) {
session.endDialog('Hello %s!', results.response);
}
]);
To prompt the user with a series of choices use a waterfall dialog combined with a builder.Prompts.choice() call. For example:
var noiseComplaintName = "Noise Complaint";
var trashDumpingName = "Trash Dumping";
var weirdSmellName = "Weird Smell";
var trafficSignalName = "Traffic Signal";
var reportTypes = [
noiseComplaintName,
trashDumpingName,
weirdSmellName,
trafficSignalName
];
bot.dialog('pick-report-type', [
function(session) {
session.send('Choice prompt example:');
var promptOptions = {
listStyle: builder.ListStyle.button
};
builder.Prompts.choice(session, "What do you want to report?", reportTypes, promptOptions);
},
function (session, results) {
// handle response from choice prompt
var selectedReportType = results.response.entity;
switch (selectedReportType) {
case noiseComplaintName:
// handle response here
break;
case trashDumpingName:
// handle response here
break;
case weirdSmellName:
// handle response here
break;
case trafficSignalName:
// handle response here
break;
default
// handle response here
break;
}
}
]);
For a more detailed example that combines prompts with Rich Cards, check out the sample code: BotBuilder-Samples/Node/cards-RichCards on GitHub.

Related

Prompts.text - handle image/attachment response

I have a bot dialog which asks a user for an order id:
bot.dialog("orderId", [
(session, args, next) => {
return builder.Prompts.text(session, "Please provide order id");
},
(session, results, next) => {
const matched = results.response.match(/\d{3}-?\d{7}/g);
if (matched) {
// ...
session.endDialogWithResult(matched);
} else {
// ...
}
}
]);
This works as expected when user enters a valid order id and has validation
code which is omitted.
The problem i am facing is that users from time to time upload a screenshot
of the order id which triggers a default action which is just to re-prompt with a system prompt.
I know i can use retryPrompt property if the IPromptOptions interface, but this does not solve my issue.
I want to be able to start another dialog or end the conversation.
Any ideas how should i make this work ?
Edit:
In another case instead of saying "Yes" or any other positive phrase, user replies to Prompts.text with a "Thumbs Up" image from Facebook Messenger. which will also break Prompts.text flow.
I want to be able to treat the "Thumbs Up" image as a positive answer
to my question and control conversation flow according to that.
As build-in Prompts will create a individual dialog, and Prompts.text() will verify the user input strictly for string type only, when you upload an image file to bot, which will not be verified by Prompts.text() dialog, and that raises your issue.
You can consider to add an step for your user to choice one approch to verify the code (by inputing code string, or uploading code picture).
var bot = new builder.UniversalBot(connector, [
(session) => {
const choices = ['code', 'image'];
var msg = new builder.Message(session)
.text("Which will you prefer to provide your code?")
.suggestedActions(
builder.SuggestedActions.create(
session, [
builder.CardAction.imBack(session, "code", "By Code"),
builder.CardAction.imBack(session, "image", "By Image")
]
)
);
builder.Prompts.choice(session, msg, choices);
}, (session, args, next) => {
const type = args.response.entity;
session.send(`Your choice is ${type}`);
if (type == 'code') {
session.replaceDialog('verifyCode')
} else {
session.replaceDialog('verifyAttachmenet')
}
}
]);
bot.dialog('verifyCode', [(session) => {
builder.Prompts.text(session, 'Input your code');
}, (session, args, next) => {
session.send(args.response);
session.endDialog();
}])
bot.dialog('verifyAttachmenet', [(session) => {
builder.Prompts.attachment(session, 'update your image');
}, (session, args, next) => {
session.send(`You upload ${args.response.length} images`);
session.endDialogWithResult(args.response);
}])

Azure bot Nodejs Function calls

I am trying to call a dialog in the bot which has to go to specific function, Here is the scenario...
I have a dialog bot as shown below
bot.dialog('/Welcome', [
function(session){
builder.Prompts.text(session, "Welcome to the Bot");
},
function (session, args) {
// Has some code
},
function (session, args){
//has some code
}......
When ever i do replace dialog ie.
bot.replaceDialog('/Welcome')
it should not go to the first function ie. Welcome to the Bot It should skip this and go to next function.
Is there any way to accomplish this in azure bot?
This is quite simple if you look at their article
https://learn.microsoft.com/en-us/azure/bot-service/nodejs/bot-builder-nodejs-dialog-replace
Their example is like below
// This dialog prompts the user for a phone number.
// It will re-prompt the user if the input does not match a pattern for phone number.
bot.dialog('phonePrompt', [
function (session, args) {
if (args && args.reprompt) {
builder.Prompts.text(session, "Enter the number using a format of either: '(555) 123-4567' or '555-123-4567' or '5551234567'")
} else {
builder.Prompts.text(session, "What's your phone number?");
}
},
function (session, results) {
var matched = results.response.match(/\d+/g);
var number = matched ? matched.join('') : '';
if (number.length == 10 || number.length == 11) {
session.userData.phoneNumber = number; // Save the number.
session.endDialogWithResult({ response: number });
} else {
// Repeat the dialog
session.replaceDialog('phonePrompt', { reprompt: true });
}
}
]);
So yours would be something like below
bot.dialog('/Welcome', [
function(session, args, next){
if (!args || args.prompt)
builder.Prompts.text(session, "Welcome to the Bot");
else
next();
},
function (session, args) {
// Has some code
},
function (session, args){
//has some code
}......
and you will call it like below
bot.replaceDialog('/Welcome', {prompt: false}))

How to stay in dialog triggered by LUIS nodejs

I'm playing a bit with chatbots using Bot Framework from MS.
I created one supposed to book a flight, and it has LUIS integrated.
My question is: once I'm in the flight booking dialog triggered by LUIS, I want to check if all the informations regarding the flight are given by the user (departure city, arrival city, date, airline...). So if I'm missing an info, for example the city of departure, the bot will ask 'can you give me the departure city?' qnd if I write 'from London', LUIS detects that as a new flight reservation and triggers another dialog. but I want it to stay in the dialog obviously!
Here is the dialog code so far, just in case if departure city is missing:
// Main dialog with LUIS
bot.dialog('FlightBookingDialog', [
function (session, args, next) {
// Resolve and store any entity passed from LUIS.
var intent = args.intent;
session.dialogData.airline = builder.EntityRecognizer.findEntity(intent.entities, 'Airline');
session.dialogData.class = builder.EntityRecognizer.findEntity(intent.entities, 'Class');
session.dialogData.date_time = builder.EntityRecognizer.findEntity(intent.entities, 'builtin.datetimeV2');
session.dialogData.departure = builder.EntityRecognizer.findEntity(intent.entities, 'Departure');
session.dialogData.destination = builder.EntityRecognizer.findEntity(intent.entities, 'Destination');
session.dialogData.number_tickets = builder.EntityRecognizer.findEntity(intent.entities, 'number');
session.send("I see you want to travel, great !");
if(!session.dialogData.departure) {
builder.Prompts.text(session, "Can you specify me a departure city please ?");
} else {
next();
}
},
function (session, args, results, next) {
if (results.response) {
builder.LuisRecognizer.recognize(session.message.text, luisModelUrl,
function(err, intents, entities) {
if(entities) {
var departure = builder.EntityRecognizer.findEntity(intents.entities, 'Departure');
if (departure) {
session.dialogData.departure = departure;
}
}
}
);
};
session.send("Good !");
},
]).triggerAction({
matches: 'FlightBooking'
});
If I understand correctly, I think you can update the code to have multiple matches. From this documentation page the code may be updated to look like this:
var intents = new builder.IntentDialog({ recognizers: [recognizer] })
.matches('Greeting', (session) => {
session.send('You reached Greeting intent, you said \'%s\'.', session.message.text);
})
.matches('Note.Create', [(session, args, next) => {
// Resolve and store any Note.Title entity passed from LUIS.
var intent = args.intent;
var title = builder.EntityRecognizer.findEntity(intent.entities, 'Note.Title');
var note = session.dialogData.note = {
title: title ? title.entity : null,
};
// Prompt for title
if (!note.title) {
builder.Prompts.text(session, 'What would you like to call your note?');
} else {
next();
}
},
(session, results, next) => {
var note = session.dialogData.note;
if (results.response) {
note.title = results.response;
}
// Prompt for the text of the note
if (!note.text) {
builder.Prompts.text(session, 'What would you like to say in your note?');
} else {
next();
}
},
(session, results) => {
var note = session.dialogData.note;
if (results.response) {
note.text = results.response;
}
// If the object for storing notes in session.userData doesn't exist yet, initialize it
if (!session.userData.notes) {
session.userData.notes = {};
console.log("initializing session.userData.notes in CreateNote dialog");
}
// Save notes in the notes object
session.userData.notes[note.title] = note;
// Send confirmation to user
session.endDialog('Creating note named "%s" with text "%s"',
note.title, note.text);
}])
Note that there is no endDialog method until the end when the bot has all the information it needs to process the request.
Hope that helps or at least gets you on the right path!
I found the solution here:
https://github.com/Microsoft/BotFramework-Samples/tree/master/docs-samples/Node/basics-naturalLanguage
They are exactly talking about my problem, I post it here so people in the same situation can see it.
You have to go to the IntentDialog part, Ctrl+F it.

Azure Botframework: How to re-prompt the Prompt.text if user responds with invalid value

I have a single dialog which has multiple Prompts(Prompts.text, Prompts.number, Prompts.Choice, Prompts.confirm). Though Prompts.choice and Prompts.confirm seems to have inbuilt validations but how to validate Prompts.text?
I have gone through this thread How to handle wrong input from the user? but it was rectified by converting text into choice.
Also I do not want to restart the whole dialog as it ask the questions form beginning then as shown in create custom prompts to validate input
Here is shorter version for my dialog:
bot.dialog('/getDetails', [
function (session, args, next) {
let options = {
retryPrompt: 'The response id invalid'
}
builder.Prompts.text(session, 'What is your full name?', options);
//passing options as argument works for Prompts.choice, which seems an inbuilt validation
},
function (session, results, next){
var name = session.dialogData.name;
//How to to reprompt if user does not enters its full name?
if (results.response) {
name.fullname = results.response;
}
builder.Prompts.text(session, 'Can you please provide your country name?');
},
function (session, results) {
var name = session.dialogData.name;
//How to reprompt only last Prompts.text if user enter an invlid value?
if (results.response) {
name.text = results.response;
}
}
}]).triggerAction({
matches: 'GetDetails',
})
Here is how i solved it through DialogAction.validatedPrompt
bot.dialog('/getDetail', [
function (session) {
session.beginDialog('/validateAge', { prompt: "What's your age?" });
//if false response, then prmopts "I did not understand {age}""
},
function (session, results) {
if (results.response) {
session.send("Thank you for adding your age");
}
}
]).triggerAction({
matches: /^lets validate$/i
})
bot.dialog('/validateAge', builder.DialogAction.validatedPrompt(builder.PromptType.text, function (response) {
if(response> 0 && response < 70){
return response;
}
}));

What are "custom prompts" in the Microsoft Bot Framework?

Since version 3.8, the Bot Framework now includes a handful of messages like this one:
DialogAction.validatedPrompt() has been deprecated as of version 3.8. Consider using custom prompts instead.
I don't see any mention of this in the documentation. What are "custom prompts," and where can I learn more about how they improve on the deprecated functionality?
You can find an example on the Git Hub here. The code provided in there can be found in the example is below:
// Create a recognizer for your LUIS model
var recognizer = new builder.LuisRecognizer('<model>');
// Create a custom prompt
var prompt = new builder.Prompt({ defaultRetryPrompt: "I'm sorry. I didn't recognize your search." })
.onRecognize(function (context, callback) {
// Call prompts recognizer
recognizer.recognize(context, function (err, result) {
// If the intent returned isn't the 'None' intent return it
// as the prompts response.
if (result && result.intent !== 'None') {
callback(null, result.score, result);
} else {
callback(null, 0.0);
}
});
});
// Add your prompt as a dialog to your bot
bot.dialog('myLuisPrompt', prompt);
// Add function for calling your prompt from anywhere
builder.Prompts.myLuisPrompt = function (session, prompt, options) {
var args = options || {};
args.prompt = prompt || options.prompt;
session.beginDialog('myLuisPrompt', args);
}
// Then call it like a builtin prompt:
bot.dialog('foo', [
function (session) {
builder.Prompts.myLuisPrompt(session, "Please say something I recognize");
},
function (session, results) {
switch (results.response.intent) {
case 'Bar':
break;
}
}
]);
`

Resources