var bot = new builder.UniversalBot(connector, [
function (session, args, next) {
if (!session.userData.name) {
session.beginDialog('profile');
} else {
next();
}
},
function (session, results) {
session.send('Hello %s!', session.userData.name);
}]);bot.dialog('profile', [
function (session) {
builder.Prompts.text(session, 'Hi! What is your name?');
},
function (session, results) {
session.userData.name = results.response;
session.endDialog();
}]);
The above code is taken from the Microsoft bot framework documentation. Here, I cant understand the purpose of the session.endDialog(). After running this code, it asks for username and gets the input and replies back with "hello user input". What happens is, it keeps looping again and again.
I want to start the qnamaker dialog after getting the user name and it should continue with the qnamaker and should not get back to the first function where user name is asked.
session.endDialog() will end the current dialog (in your example code, it would end the profile dialog), and returns control to the dialog that called it, so (in your example) control would return to the default dialog that launches after the wake word is sent.
The example code that you took from the Microsoft site is just meant to show a basic example, so of course it just has those few dialogs. For the QnA bot you want to build, instead of simply echoing the name back to the user, you would want to start your QnA functionality/dialog in that function, which could include starting another dialog, depending on how you want to architect it.
Related
I'm trying both to show a welcome message when my bot starts up and also load a specific dialog. We are using version 3 in the company where I'm working (I know, it's old and not supported).
As far as the welcome message, https://learn.microsoft.com/en-us/azure/bot-service/nodejs/bot-builder-nodejs-handle-conversation-events?view=azure-bot-service-3.0 says to use on conversationUpdate, which works fine, but this seems to be contradicted by https://blog.botframework.com/2018/07/12/how-to-properly-send-a-greeting-message-and-common-issues-from-customers/, which suggests one should not use conversationUpdate, except when using DirectLine, but instead send an event. Is this the final word on the matter? Is there a better way?
I'd also like to load a dialog automatically after the welcome message. How do I do this? Can I access the session during the 'on conversationUpdate' event above and load the dialog directly there? Is there a better way?
Thanks for any help!
It is contradictory, but conversationUpdate is likely your best bet in most situations. However, because channels handle this differently, you should be aware that the result can vary. For direct line, it is a better option to utilize sending events.
An example, in case of need:
bot.on('conversationUpdate', function(message) {
if (message.membersAdded) {
message.membersAdded.forEach(function(identity) {
if (identity.id === message.address.bot.id) {
var reply = new builder.Message()
.address(message.address)
.text("Welcome");
bot.send(reply);
}
});
}
});
For immediately calling a specific dialog, do this:
bot.on('conversationUpdate', function (message) {
if (message.membersAdded) {
message.membersAdded.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
bot.beginDialog(message.address, '/main');
}
});
}
});
bot.dialog('/main', [
function (session, args, next) {
session.send("Glad you could join.");
session.beginDialog('/next');
}
]);
Simply combine the two for sending the welcome message and starting up a dialog.
Hope of help!
I've just been given a project where I'll need to learn and deal with Microsoft Bot Framework, version 3.2 (it's an old project that I'm modifying).
I'm working through the examples and trying to understand how dialog flow works, and how I might best modularize it.
As I understand it, when you create your bot like this
// CODE SAMPLE 1
const bot = new builder.UniversalBot(connector, [
function (session) {
session.send("Welcome to the dinner reservation.");
session.beginDialog('askForDateTime');
},
/*
functions omitted for brevity
*/
]).set('storage', inMemoryStorage); // Register in-memory storage
the array of functions that comprise your "default dialog" must be given when the bot is created -- that is, you can't add the default dialog at a later point, or change it. Is that correct?
Then, later, if you want to modularize your dialog structure, you can have something like this (referring to the code above)
// CODE SAMPLE 2 (in same file as code above)
bot.dialog('askForDateTime', [
function (session) {
builder.Prompts.time(session, "Please provide a reservation date and time (e.g.: June 6th at 5pm)");
},
function (session, results) {
session.endDialogWithResult(results);
}
]);
So, is bot.dialog registering this dialog with the bot for later use? That is, there's a look-up of some sort at run-time -- during the conversation -- based on this string that connects session.beginDialog('askForDateTime'); in the first code sample with the functions registered with bot.dialog('askForDateTime') in the second code sample?
When I looked at the SDK reference, I see that beginDialog accepts an IAddress
function beginDialog(address: IAddress, dialogId: string, dialogArgs?: any, done?: (err: Error) => void)
where it says
Address routing information for an event. Addresses are bidirectional
meaning they can be used to address both incoming and outgoing events.
They're also connector specific meaning that connectors are free to
add their own fields to the address.
So this 'registration' via string is basically an event registration system, kind of like addEventListener, but in this case it's not registering an action per se, but a dialog?
Two last questions:
Can one call session.beginDialog from within a bot.dialog? That is, have a nested tree of dialogs? As it is, the only example is of nesting from the default dialog, but I didn't know if it could go deeper.
Finally, how can one modularize your dialogs into separate node modules, that is, move your sub-dialogs into separate files? I thought of something like this:
// askForDateTime.js
module.exports = bot =>
bot.dialog('askForDateTime', [
function (session) {
builder.Prompts.time(session, "Please provide a reservation date and time (e.g.: June 6th at 5pm)");
},
function (session, results) {
session.endDialogWithResult(results);
}
]);
but don't see how to use it in my main app
// app.js
const askDateTimeDialog = require('./askForDateTime')(bot) // how to use this? I need to pass in a bot that's not yet created. Do I even need to import it?
const bot = new builder.UniversalBot(connector, [
function (session) {
session.send("Welcome to the dinner reservation.");
session.beginDialog('askForDateTime'); // <--- how to beginDialog with imported dialog? Or is the 'registration' behind the scenes sufficient since I'm just using the same string?
},
/*
functions omitted for brevity
*/
]).set('storage', inMemoryStorage); // Register in-memory storage
Thanks for any help! I realize this probably all is easier with version 4, but I need to use an earlier version.
First and foremost, please be aware the Botbuilder V3 is in sunset with support ending at the end of 2019. You can read more about this topic here and options for migrating from v3 to v4 here.
Regarding default dialogs, the other method is to only pass the connector into the adapter and then start a dialog when the conversationUpdate occurs. There are some challenges that can bubble up when using conversationUpdate, so I would look over this blog post before continuing.
var bot = new builder.UniversalBot(connector)
bot.on('conversationUpdate', function (message) {
if (message.membersAdded) {
message.membersAdded.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
bot.beginDialog(message.address, '/main');
}
});
}
});
// ----greetings.js -----
bot.dialog('/main', [
function (session, args, next) {
session.send("Glad you could join.");
session.beginDialog('/nextDialog');
}
]);
Regarding beginDialog() registration/addressing, there is no pre-registeration that occurs. When a dialog is called, it's essentially called like any other function is. The address, more or less, is used for managing dialog state (i.e. where a bot is in a conversation) - whether a dialog is being added to stack, is in use, or being popped off the stack.
Regarding calling one dialog within another, yes it is doable, as you can see in this sample:
lib.dialog('/', [
[...other functions...]
function (session, args) {
session.dialogData.recipientPhoneNumber = args.response;
session.beginDialog('validators:notes', {
prompt: session.gettext('ask_note'),
retryPrompt: session.gettext('invalid_note')
});
},
[...other functions...]
]);
Lastly, regarding modularizing, yes, this is also doable. Look over either of these two samples. core-MultiDialogs is the less complicated but demo-ContosoFlowers is also a good example to reference.
Hope of help!
I want to end a dialog for a particular user proactively. I see APIs to send message and start a dialog proactively using the address of the conversation. Is there any API to end a current/particular dialog for that conversation (preferably using the address of the conversation) proactively. I am using the bot builder Node.js SDK.
This is required to provide a manual intervention for the chat admin. I don't want to just end the whole conversation (there is an API to end the conversation as well), but assist the user on something.
All the APIs mentioned can be found here
You can try to leverage loadSession(address: IAddress, callback: (err: Error, session: Session) => void): void;, then end the conversion in the callback function. Please refer to following code snippet:
let savedAddress;
server.get('/api/CustomWebApi', function (req, res, next) {
bot.loadSession(savedAddress, (err, session) => {
if (!err) {
session.send('ternimal this conversion')
session.endConversation();
}
})
res.send('triggered');
next();
});
bot.dialog('/', [
function (session) {
savedAddress = session.message.address;
message = 'You can terminate the conversation by accessing: ';
message += 'http://localhost:' + server.address().port + '/api/CustomWebApi';
session.send(message);
}
])
For Node.js bots, you can always end a dialog using the session variable, i.e
session.endDialog();
See bot builder SDK session references. You can also 'chain' these methods, i.e do something like...
session.send('proactiveMessage').endDialog();
Hope that helps !
I've built a bot that asks user to upload an attachment. But I also want to give the ability to the user to just type any text instead of uploading an attachment, but whenever I do that it says
I didn't receive a file. Please try again.
In the command line, I can see it says no intent handler found for null. How do I handle these nulls/incorrect inputs?
Sample code:
intents.matchesAny([/lost and found/i], [
function (session) {
builder.Prompts.attachment(session,"Please upload a picture of the item.");
},
function (session) {
session.endConversation('Thank you');
}
]);
Per your issue message, no intent handler found for null, which seems that you are using builder.IntentDialog, and the issue means that your bot didn't match any intents provided in your bot.
Also I notice that your are using intents.matchesAny, according to the comment:
Invokes a handler when any of the given intents are detected in the users utterance. So I think you forget to set such intent lost and found in your LUIS server.
If you want to trigger any miss catched user utterance, you can try to use:
intents.onDefault([
function (session) {
builder.Prompts.attachment(session,"Please upload a picture of the item.");
},
function (session) {
session.endConversation('Thank you');
}
]);
I have a simple bot that fetches news articles based on a user prompt. The entire flow works fine locally using emulator but after being deployed to a server the bot fails when it hits a builder.Prompts.text block. Below is my code and you will see a "Asking article count" prompt which is where it stops in flow.
Bot shows accepted when testing on the BOT Framework page
Bot is receiving messages via WebChat and Slack
Bot also shows 0 issues for each channel after interacting
var bot = new builder.UniversalBot(connector);
var intents = new builder.IntentDialog();
bot.dialog('/', intents);
var HHCC = require('./hhcc.js');
intents.matches(/^news/i, [
function(session) {
console.log("Intent Given!");
session.beginDialog('/news');
},
function(session, results) {
session.send('Enjoy reading!');
}
]);
bot.dialog('/news', [
function(session) {
console.log("Asking article count");
builder.Prompts.text(session, 'How many articles would you like to see?');
},
function(session, results) {
session.sendTyping();
session.conversationData.count = results.response;
HHCC.getNews(session.conversationData.count, session, function(newsArticles) {
newsArticles.forEach(function(newsCard) {
session.send(newsCard);
});
session.conversationData.news = newsArticles;
console.log(newsArticles);
session.endDialog();
});
}
]);
server.post('/api/messages', connector.listen());
Ive checked all logs and can't seem to find any clues as its failing pretty silently.
Have you attempted using builder.Prompts.number() instead of .text()? It only accepts numbers and (I'm guessing you're doing this) you won't have to parse the results.response into a number. Without provided error messages or logs it's difficult to help.
One thing you might have to look out for (if using builder.Prompts.number) is if a user provides a decimal, as the prompt will accept this input, requiring the bot to round to the nearest integer.
Also, if you've saved the results.response into your session object, you will not need to pass in session.conversationData.count as another parameter to HHCC.getNews(). You can instead access it from session in your function.