Get duration between the bot sending the message and user replying - node.js

First of all thank you for your awesome work in building and maintaining this library.
I have a scenario in which I need to check if the person answered within 10 seconds. I have some code that looks similar to this where I measure the start time in the first waterfall step and I measure the end time in the next waterfall step, I ll find the difference between both in the second waterfall step.
bot.dialog('/duration', [(session, args)=>{
session.dialogData.startTime = new Date().getTime()
}, (session, results)=>{
session.dialogData.endTime = new Date().getTime()
}])
I feel that the code above is not accurate. I have seen a session.message.timestamp property. How would it be different than the code above
Is there a better way to measure time differences like these?
How do I account for network latency in such a scenario?
Thank you for your answers in advance

You can set the time you send the message and then re-evaluate with the message timestamp like:
var bot = new builder.UniversalBot(connector, [
function (session) {
session.userData.lastMessageSent = new Date();
builder.Prompts.text(session, 'Send something in 10 seconds or you die.');
},
function (session, result) {
if (session.userData.lastMessageSent) {
var lastMessageSent = new Date(session.userData.lastMessageSent);
var lastMessageReceived = new Date(session.message.timestamp);
var diff = lastMessageReceived - lastMessageSent / 1000;
if (diff >= 10) {
session.send('Game over.');
} else {
session.send('Good boy!');
}
}
}
]);

A better way to do that might be using the Application Insights connection when registering a bot.
This way the Bot Framework service measures your requests/responses and stores the timestamp automatically into Application Insights.
Once you copy the instrumentation key to the bot registration page, events under customEvents in Application Insights Analytics.
In case you just to have an actionable code, the answer above is a better solution.

Related

Firebase Cloud Function to send notification at very specific time [duplicate]

Inside the Firebase Console, under the Cloud Messaging view, users are able to create test notifications. This functionality also allows you to schedule the time at which the notification will send to a device or set of devices.
Is it possible to create and send scheduled FCM notifications to specific devices by using firebase cloud functions and the Firebase Admin SDK? Is there an alternative way to solving this?
The current way that I send scheduled messages to users is like so:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const schedule = require('node-schedule');
admin.initializeApp();
exports.setScheduledNotification = functions.https.onRequest(async (req, res) => {
const key = req.query.notification_key;
const message = {
notification: {
title: 'Test Notification',
body: 'Test Notification body.'
}
};
var currentDate = new Date();
var laterDate = new Date(currentDate.getTime() + (1 * 60000));
var job = schedule.scheduleJob(key, laterDate, () => {
const snapshot = admin.messaging().sendToDevice(key, message);
});
return res.status(200).send(`Message has been scheduled.`);
});
First of all, I am unsure how node-schedule interacts with firebase cloud functions. The logs appear that the function terminates very quickly which I would think would be correct. The longer the operation runs the more costly it is in our firebase bills. The notification does still run on a scheduled time though. I'm confused on how that all is working behind the scenes.
Secondly, I am having issues canceling these scheduled notifications. The notifications will most likely be on a 2hr timed schedule from the point it gets created. Before the 2hrs is up, I'd like the have the ability to cancel/overwrite the notification with an updated scheduled time.
I tried this to cancel the notification and it failed to find the previously created notification. Here is the code for that:
exports.cancelScheduledNotification = functions.https.onRequest(async (req, res) => {
const key = req.query.notification_key;
var job = schedule.scheduledJobs[key];
job.cancel();
return res.status(200).send(`Message has been canceled.`);
});
Is it possible to tap into the scheduling functionality of firebase cloud messaging outside of the firebase console? Or am I stuck with hacking my way around this issue?
A Cloud Function can run for a maximum of 9 minutes. So unless you're using node-schedule for periods shorter than that, your current approach won't work. Even if it would work, or if you are scheduling for less than 9 minutes in advance, using this approach is very uneconomic as you'll be paying for the Cloud Functions for all this time while it's waiting.
A more common approach is to store information about what message you want to be delivered to whom at what time in a database, and then use regular scheduled functions to periodically check what messages to send. For more on this, see these previous questions:
Firebase scheduled notification in android
How to schedule push notifcations for react native expo?
Schedule jobs in Firebase
Ionic: Is it possible to delay incoming push FCM push notification from showing to my device until a specific time
Cloud Functions for Firebase trigger on time?
How to create cron jobs dynamically in firebase
A recent improvement on this is to use the Cloud Tasks API to programmatically schedule Cloud Functions to be called at a specific time with a specific payload, and then use that to send the message through FCM. Doug Stevenson wrote a great blog post about this here: How to schedule a Cloud Function to run in the future with Cloud Tasks (to build a Firestore document TTL). While the post is about deleting documents at a certain time, you can combine it with the previous approach to schedule FCM messages too.
Scheduling of tasks is now also described in the documentation on enqueueing functions with Cloud Tasks
A final option, and one I'd actually recommend nowadays, is to separate the delivery of the message from the display of the notification.
Display of data messages (unlike notification messages) is never handled by the system, and always left to your application. So you can deliver the FCM data message straight away that then contains the time to display the message, and then wake the device up to display the message (often called a local notification) at that time.
To make Frank's answer more tangible, I am including some sample code below for scheduled cloud functions, that can help you achieve the 'scheduled FCM notifications'.
You should store the information required to send your notification(s) in Firestore (e.g. the when-to-notify parameter and the FCM token(s) of the users you want to send the notification to) and run a cloud function every minute to evaluate if there is any notification that needs to be delivered.
The function checks what Firestore documents have a WhenToNofity parameter that is due, and send the notifications to the receiver tokens immediately. Once sent, the function sets the boolean 'notificationSent' to true, to avoid that the users receive the same notification again on the next iteration.
The code below achieves just that:
const admin = require('firebase-admin');
admin.initializeApp();
const database = admin.firestore();
exports.sendNotification = functions.pubsub.schedule('* * * * *').onRun(async (context) => {
//check whether notification should be sent
//send it if yes
const query = await database.collection("experiences")
.where("whenToNotify", '<=', admin.firestore.Timestamp.now())
.where("notificationSent", "==", false).get();
query.forEach(async snapshot => {
sendNotification(snapshot.data().tokens);
await database.doc('experiences/' + snapshot.id).update({
"notificationSent": true,
});
});
function sendNotification(tokens) {
let title = "INSERT YOUR TITLE";
let body = "INSERT YOUR BODY";
const message = {
notification: { title: title, body: body},
tokens: tokens,
android: {
notification: {
sound: "default"
}
},
apns: {
payload: {
aps: {
sound: "default"
}
}
}
};
admin.messaging().sendMulticast(message).then(response => {
return console.log("Successful Message Sent");
}).catch(error => {
console.log(error);
return console.log("Error Sending Message");
});
}
return console.log('End Of Function');
});
If you're unfamiliar with setting up cloud functions, you can check how to set them up here. Cloud functions require a billing account, but you get 1M cloud invocations per month for free, which is more than enough to cover the costs of this approach.
Once done, you can insert your function in the index.js file.

Lost ability to capture unique Conversation_ID for each new session

Using Bot Builder 4.11.1 .Net and seemed to have lost the ability to capture any unique identifier for each new session. I need a unique identifier to keep state with the AI engine I am using to respond to input. Any suggestions? So, to expand, if I have a slack bot, for example, each time a user logs into Slack and starts a conversation with MyBot, I need a new unique identifier.
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
var welcomeText = "Hello and welcome!";
Random rnd1 = new Random();
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(MessageFactory.Text(welcomeText, welcomeText), cancellationToken);
}
}
}
}
Unless I'm missing something, you should be able to get the information you need from TurnContext. In my code I'm running this in onMessage only (as in my case I only will message the user if they have sent the bot a message), but I don't see why you couldn't use this in onMembersAdded as well. I don't know how channels like Slack work, but in Microsoft Teams the user is just "added" when they first talk to the bot, and you don't end up in onMembersAdded unless they remove and read the bot in Teams. So if you may want to grab the conversation reference in the future, you may want to have it in onMessage or in both places. Also if you need the activity ID for some reason, as this will obviously update with each activity (though I haven't had any need for this information). Here is how you get the conversation reference. I am storing this in my conversation state and have assumed you are familiar with that but let me know if you need further help there. I also store in an Azure table to be accessed outside of the bot (e.g. I have an Azure Function that uses this to send proactive followups).
const { TurnContext } = require('botbuilder');
const conversationData = await this.dialogState.get(context, {});
conversationData.conversationReference = TurnContext.getConversationReference(context.activity);
await this.conversationState.saveChanges(context);
And that's it! Here is a sample conversation reference. Note that if you are storing this in Azure Tables or similar, you'll likely need to stringify it and re-parse when you pull it out.
{
"activityId":"ACTIVITY_ID",
"user": {
"id":"USER_ID",
"name":"USER_NAME",
"aadObjectId":"AAD_OBJECT_ID",
"role":"user"
},
"bot": {
"id":"BOT_ID",
"name":"BOT_NAME"
},
"conversation": {
"conversationType":"personal",
"tenantId":"YOUR_TENANT_ID",
"id":"YOUR_CONVERSATION_ID"
},
"channelId":"msteams",
"locale":"en-US",
"serviceUrl":"https://smba.trafficmanager.net/amer/"
}
What you are looking for (I think) is conversationReference.conversation.id. Different channels are going to have different attributes in the conversation reference, but this ID should always be there.

Analysing Microsoft Chat Bot Load Test result (Application Insights)

Here is the detail on one of the transaction (For most of the transactions the observation is the same). The screenshots below are of an "End-to-End transaction details" found under Application Insights's Performance Option.
The total duration of the transaction is around 29.4 seconds out of ~26 seconds is spent between the Step 1 & 2 marked in the below Timeline & Telemetery screenshots.
Timeline
Telemetery
Could someone help me understand the gap between Step 1 & 2 and how can I reduce this to increase the performance.
As you can also see in the Telemetery screenshot that the custom trace messages are also printed (highlighted in Red) mentioned in the MessagesController.cs
[ResponseType(typeof(void))]
public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
var telemetry = new Microsoft.ApplicationInsights.TelemetryClient();
telemetry.TrackTrace("MessagesController POST",
Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Warning,
new Dictionary<string,string> { {"Activity Id", activity.Id} });
// check if activity is of type message
if (activity != null && activity.GetActivityType() == ActivityTypes.Message)
{
telemetry.TrackTrace("MessagesController Type:Message",
Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Warning,
new Dictionary<string,string> { {"Activity Id", activity.Id} });
//var reply = activity.CreateReply(String.Empty);
//reply.Type = ActivityTypes.Typing;
//await context.SendResponse(reply);
await Conversation.SendAsync(activity, () => new EchoDialog());
Is it that after executing this line - await Conversation.SendAsync(activity, () => new EchoDialog()); it is putting the message in some queue and then it is picked up by the EchoDialog after sometime?
Thanks in advance!
This was due to the load script that I had created. The load script was creating same conversation id for multiple users and hence it was taking time to retrieve the dialog record.

How to add Get Started button in the typing bar using bot builder sdk for node.js

I am using bot builder sdk for node.js to create a chatbot. Also connected it to facebook channel. I am using the following code to greet the user:
var bot = new builder.UniversalBot(connector, [
(session, result, next) => {
let text = '';
switch(session.message.address.channelId) {
case 'facebook':
text = 'Hi ' + session.message.user.name + ' !';
break;
default:
text = 'Hi !';
}
session.sendTyping();
session.say(text);
next();
},
(session, say) => {
}
]);
The above code works fine, but I want to add "Get Started" button in the typing bar to invoke the above code. Note that this button appears only once. Please find image of the typing bar below:
Is there a way to achieve this using bot builder sdk for node.js ?
Thanks
Although one can certainly add a button to start any activity with the bot, but that will limit the bots potential to only one customizable channel, i.e. WebChat.
I think there are better 2 alternative ways to get the desired functionality which will work across many channels.
First
I would suggest to add a conversation update event. Code goes in the botbuilder's middleware. Here is a sample code from the docs.
bot.on('conversationUpdate', function (message) {
if (message.membersAdded && message.membersAdded.length > 0) {
// Say hello
var txt = "Send me a Hi";
var reply = new builder.Message()
.address(message.address)
.text(txt);
bot.send(reply);
});
What this will do is make the bot send a message Send me a Hi to the user, if it determines this is a first time visitor. This will give the visitor enough cue to send the bot Hi by typing it. Although he can enter whatever he wants, but this will result in the invocation of the 1st dialog configured which in this case is the will be the dialog which you have posted in question.
Second
You can mark some dialog to be invoked automatically if your bot has never encountered this visitor. Here is the sample code...
var bot = new builder.UniversalBot(connector);
bot.dialog('firstRun', function (session) {
session.userData.firstRun = true;
session.send("Hello...").endDialog();
}).triggerAction({
onFindAction: function (context, callback) {
// Only trigger if we've never seen user before
if (!context.userData.firstRun) {
// Return a score of 1.1 to ensure the first run dialog wins
callback(null, 1.1);
} else {
callback(null, 0.0);
}
}
});
Here we have split the bot creation and dialog registration in 2 steps. And while registering the firstRun dialog, we have provided it the triggerAction that if the visitor is new, then trigger this dialog.
Both of these approaches do not use adding some extra buttons and it is up to the bot either to educate him on sending some message which in turn will start the 1st dialog or directly start some dialog.
For more info on conversationEvent you can refer to this page
I tried the above options, but they didn't seem to be working for facebook messenger. But I found a solution to add the Get Started button into the typing bar of the messenger. For that we need to use the Facebook Graph API and not the bot builder sdk.
https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<PAGE_ACCESS_TOKEN>
{
"get_started":{
"payload":"Get Started"
}
}
The above API call will add the button for you to get the conversation started.
Thanks all for the help!!

SqlFilter on Azure ServiceBus Topic subscription not filtering

I’ve got a WinRT app that I’m using the Windows Azure Toolkit for Windows 8 with. I’ve got a setup where I’d like clients subscribed to ignore messages posted to a ServiceBus Topic if they’re the originator or if the message is older than when their subscription started.
In the Properties of my BrokeredMessage, I’ve added 2 items to cover these scenarios:
message.Properties["Timestamp"] = DateTime.UtcNow.ToFileTime();
message.Properties["OriginatorId"] = clientId.ToString();
clientId is a Guid.
The subscriber side looks like this:
// ti is a class that contains a Topic, Subscription and a bool as a cancel flag.
string FilterName = "NotMineNewOnly";
// Find or create the topic.
if (await Topic.ExistsAsync(DocumentId.ToString(), TokenProvider))
{
ti.Topic = await Topic.GetAsync(DocumentId.ToString(), TokenProvider);
}
else
{
ti.Topic = await Topic.CreateAsync(DocumentId.ToString(), TokenProvider);
}
// Find or create this client's subscription to the board.
if (await ti.Topic.Subscriptions.ExistsAsync(ClientSettings.Id.ToString()))
{
ti.Subscription = await ti.Topic.Subscriptions.GetAsync(ClientSettings.Id.ToString());
}
else
{
ti.Subscription = await ti.Topic.Subscriptions.AddAsync(ClientSettings.Id.ToString());
}
// Find or create the subscription filter.
if (!await ti.Subscription.Rules.ExistsAsync(FilterName))
{
// Want to ignore messages generated by this client and ignore any that are older than Timestamp.
await ti.Subscription.Rules.AddAsync(FilterName, sqlFilterExpression: string.Format("(OriginatorId != '{0}') AND (Timestamp > {1})", ClientSettings.Id, DateTime.UtcNow.ToFileTime()));
}
ti.CancelFlag = false;
Topics[boardId] = ti;
while (!ti.CancelFlag)
{
BrokeredMessage message = await ti.Subscription.ReceiveAndDeleteAsync(TimeSpan.FromSeconds(30));
if (!ti.CancelFlag && message != null)
{
// Everything gets here! :(
}
I get back everything – so I’m not sure what I’m doing wrong. What’s the easiest way to troubleshoot problems with subscription filters?
When you create a Subscription then by default you get a "MatchAll" filter. In the code above you are just adding your filter so it is applied in addition to the "MatchAll" filter and thus all messages are recieved. Just delete the $Default filter once the Subscription is created and that should resolve the issue.
Best way to troubleshoot is using the Service Bus Explorer from Paolo Salvatori
http://code.msdn.microsoft.com/windowsazure/Service-Bus-Explorer-f2abca5a
He has done a good few blog posts on it e.g. http://windowsazurecat.com/2011/07/exploring-topics-and-queues-by-building-a-service-bus-explorer-toolpart-1/
Windows Azure SDK 1.7 does have built in capability but the Service Bus Explorer Standalone version is still better, see comparison here.
http://soa-thoughts.blogspot.com.au/2012/06/visual-studio-service-bus-explorer.html
HTH your debugging...

Resources