End dialog Proactively - node.js

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 !

Related

How can I send a message from bot framework sdk after a period of inactivity? In nodejs

I am using the nodejs SDK for Bot Framework to develop a chatbot. I want to send a message to the user if they do not write in 5 minutes.
I do not find an example in bot-framework documentation and, in stackoverflow there are not solutions for a started bot (I do not need it to start the conversation). Where do I need to create the code? I have an index.js and a dialog file. How can I set the timer and restart it when the user send a message?
I'm using directline.
Thanks
There are two different ways you can approach this, one for directline only using events and one for all channels using setTimeout. The directline solution requires some code on your webchat client, but the latter requires you to save the conversation reference and start a new bot adapter. Both approaches could work.
Directline Only
You need to set up your webchat client to set up the timer and send an event to your bot if no activities are sent before the timer expires. You need to create a custom store to do this. Here is an example I used in the past:
const store = window.WebChat.createStore({}, function(dispatch) { return function(next) { return function(action) {
if (action.type === 'WEB_CHAT/SEND_MESSAGE') {
// Message sent by the user
clearTimeout(interval);
} else if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY' && action.payload.activity.name !== "inactive") {
// Message sent by the bot
clearInterval(interval);
interval = setTimeout(function() {
// Notify bot the user has been inactive
dispatch.dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'inactive',
value: ''
}
});
}, 300000)
}
return next(action);
}}});
This will send an event to your bot with the name 'inactive'. Now you need to set up your bot to handle it. So in your this.onEvent handler you need to do something like this:
if (context.activity.name && context.activity.name === 'inactive') {
await context.sendActivity({
text: 'Are you still there? Is there anything else I can help you with?',
name: 'inactive'
});
}
All channels
As I'm typing this up, I'm realizing you should be able to emit the event from your bot itself and forego starting a new bot adapter instance. But I haven't tried that before, so I'm providing my existing solution. But you may wish to experiment with emitting an inactive event if the timeout is reached instead of the actions below.
That said, here is a solution you can use within your this.onMessage handler.
// Inactivity messages
// Reset the inactivity timer
clearTimeout(this.inactivityTimer);
this.inactivityTimer = setTimeout(async function(conversationReference) {
console.log('User is inactive');
try {
const adapter = new BotFrameworkAdapter({
appId: process.env.microsoftAppID,
appPassword: process.env.microsoftAppPassword
});
await adapter.continueConversation(conversationReference, async turnContext => {
await turnContext.sendActivity('Are you still there?');
});
} catch (error) {
//console.log('Bad Request. Please ensure your message contains the conversation reference and message text.');
console.log(error);
}
}, 300000, conversationData.conversationReference);
Note that you have to get and save the conversationReference if you go this route, so that you can call continueConversation if the timer expires. I typically do this in my this.onMessage handler as well just to make sure I always have a valid conversation reference. You can get it with the below code (I'm assuming you already have your conversation state and state accessor defined).
const conversationData = await this.dialogState.get(context, {});
conversationData.conversationReference = TurnContext.getConversationReference(context.activity);
Now as I mentioned in the first solution, I believe you should be able to send an inactivity event in your try block instead of initiating the bot adapter. If you try that and it works, please let me know so I can update this solution!

How to send welcome message AND load a specific dialog automatically in Microsoft Bot Framework v.3 (Node.js)?

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!

Botbuilder - How to ignore user response without exiting prompt dialog

I have a multipatform bot (node.js through Azure Botframework) that uses a series of prompts to play a game with the user.
In group mode, such as on Kik or Slack, it waits for responses addressed to the bot.
However, I haven't found a way to simply ignore a message that doesn't address the bot. The solution I found ages ago was to simply reply with a new blank prompt:
builder.Prompts.text(session, "");
And this worked fine. However recently Slack must have changed something, because now this causes an error and the bot restarts.
How do I make the bot ignore certain responses without ending the dialog?
If suggesting a duplicate, please ensure it actually addresses this issue. Many other questions allow for the dialog to end, however this would interrupt the game.
You can setup middleware, as mentioned by Gary, that intercepts incoming messages and only processes it if the bot is #mentioned:
bot.use({
botbuilder: function (session, next) {
var message = session.message;
var botMri = message.address.bot.id.toLowerCase();
var botAtMentions = message.entities && message.entities.filter(
(entity) => (entity.type === "mention") && (entity.mentioned.id.toLowerCase() === botMri));
if (botAtMentions && botAtMentions.length) {
next();
}
},
send: function (event, next) {
next();
}
})

Bot Framework Node.js ad hoc message TO A SPECIFIC USER

I have been staring at this for hours and can't find a solution and that is even though by all suggestions it SHOULD be quite easy - https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-proactive-messages.
I have created a simple code which will "register" the user and save their data in my cosmosDatabse on Azure. That works perfectly.
//ON "register" SAVE USER DATA AND SAY REGISTERED MESSAGE
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
//REGISTERED MESSAGE
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
But how can I then access that specific user and send him a message if, say, a condition is met? (in fact on HTTP request)
I am fairly certain we have to write the conversation ID or user ID somewhere. The question is where?
function startProactiveDialog(address) {
bot.beginDialog(address, "A notification!");
}
This is how simple I think it should be. But where do you specify the user then?
You've saved the address of the user inside of your database by saving it to session.userData.savedAddress. When the event triggers, perform a query to your database that checks for the users that meet two criteria.
They're registered to listen for the event
Their address has been saved inside of the database.
In your case, you can save a property to the session.userData object, a property that lists which events they're listening for. If you just need to send a message to the user, then you can simply use bot.loadSession(savedAddress) to ping the user.
Edit:
So instead of looking specifically by user ID, you should send a query to your CosmosDB that looks for entries that have a "listen-to" Boolean-type flag corresponding to the event.
You're not worrying about the user ID at first, you're just retrieving all entries with a query that would (broadly speaking) look like this:
SELECT * FROM BotState WHERE data LIKE 'listenForEvent=1.
So to setup your session.userData so that the above theoretical query would work, you would need to modify that snippet of code in your question to something like the following:
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
session.userData.listenForEvent = 1 // Our property we're going to look for.
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
Actually, the savedAddress should be an instance of IAddress, and also, the function loadSession(address: IAddress, callback: (err: Error, session: Session) => void): void; and address(adr: IAddress): Message; under Message class all require IAddress as the parameter.
So first of all, you should save the entire address json object in cosmosDB for later using.
As botbuilder for Node.js is built on Restify or Express, you can build an addition route for your user to trigger and send proactive messages. The work flow could be following:
Guide user to register & Save the user's address object with the account mapping in your DB
Create a Route in Restify or Expressjs for trigger the proactive message:
server.get('/api/CustomWebApi', (req, res, next) => {
//find the user's address in your DB as `savedAddress`
var msg = new builder.Message().address(savedAddress);
msg.text('Hello, this is a notification');
bot.send(msg);
res.send('triggered');
next();
}
);
or if you want to leverage loadSession
server.get('/api/CustomWebApi', function (req, res, next) {
bot.loadSession(savedAddress, (err, session) => {
if (!err) {
session.send('Hello, this is a notification')
session.endConversation();
}
})
res.send('triggered');
next();
});
I created a users.json file, to which I save all the users. It works the way I need it to. I guess database would be better, but I don't really have a clue where to begin with that. Database is a whole new chapter I have not encountered yet, so it doesn't make sense to work on it when the project needs are resolved.

How to end a dialog and start an another dialog?

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.

Resources