Facebook Messenger Bot wait to respond after many request and resume - node.js

When I write the alphabet on my facebook bot, my bot wait to respond.
I write for example : I write "a"... bot respond "a", I write "b"... bot respond "b", etc...
But, for example on the letter "l", the bot waits to respond, and resume after about some minutes :
after about some minutes, the bot responds :
I use the official code, from https://developers.facebook.com/docs/messenger-platform/guides/quick-start/
app.js
app.get('/webhook', function(req, res) {
if (req.query['hub.mode'] === 'subscribe' &&
req.query['hub.verify_token'] === CONFIGURATION.webhook_token) {
console.log("Validating webhook");
res.status(200).send(req.query['hub.challenge']);
} else {
console.error("Failed validation. Make sure the validation tokens match.");
res.sendStatus(403);
}
});
app.post('/webhook', function (req, res) {
let data = req.body;
// Make sure this is a page subscription
if (data.object === 'page') {
// Iterate over each entry - there may be multiple if batched
data.entry.forEach(function(entry) {
let pageID = entry.id;
let timeOfEvent = entry.time;
// Iterate over each messaging event
entry.messaging.forEach(function(event) {
if (event.message) {
receivedMessage(event);
} else {
console.log("Webhook received unknown event: ", event);
}
});
});
// Assume all went well.
//
// You must send back a 200, within 20 seconds, to let us know
// you've successfully received the callback. Otherwise, the request
// will time out and we will keep trying to resend.
res.sendStatus(200);
}
});
function receivedMessage(event) {
let senderID = event.sender.id;
let recipientID = event.recipient.id;
let timeOfMessage = event.timestamp;
let message = event.message;
console.log("Received message for user %d and page %d at %d with message:",
senderID, recipientID, timeOfMessage);
console.log(JSON.stringify(message));
let messageId = message.mid;
let messageText = message.text;
let messageAttachments = message.attachments;
if (messageText) {
// If we receive a text message, check to see if it matches a keyword
// and send back the example. Otherwise, just echo the text we received.
switch (messageText) {
case 'generic':
sendGenericMessage(senderID);
break;
default:
sendTextMessage(senderID, messageText);
}
} else if (messageAttachments) {
sendTextMessage(senderID, "Message with attachment received");
}
}
function sendGenericMessage(recipientId) {
let messageData = {
recipient: {
id: recipientId
},
message: {
attachment: {
type: "template",
payload: {
template_type: "generic",
elements: [{
title: "rift",
subtitle: "Next-generation virtual reality",
item_url: "https://www.oculus.com/en-us/rift/",
image_url: "http://messengerdemo.parseapp.com/img/rift.png",
buttons: [{
type: "web_url",
url: "https://www.oculus.com/en-us/rift/",
title: "Open Web URL"
}, {
type: "postback",
title: "Call Postback",
payload: "Payload for first bubble",
}],
}, {
title: "touch",
subtitle: "Your Hands, Now in VR",
item_url: "https://www.oculus.com/en-us/touch/",
image_url: "http://messengerdemo.parseapp.com/img/touch.png",
buttons: [{
type: "web_url",
url: "https://www.oculus.com/en-us/touch/",
title: "Open Web URL"
}, {
type: "postback",
title: "Call Postback",
payload: "Payload for second bubble",
}]
}]
}
}
}
};
callSendAPI(messageData);
}
function sendTextMessage(recipientId, messageText) {
let messageData = {
recipient: {
id: recipientId
},
message: {
text: messageText
}
};
callSendAPI(messageData);
}
function callSendAPI(messageData) {
request({
uri: 'https://graph.facebook.com/v2.6/me/messages',
qs: { access_token: CONFIGURATION.access_token },
method: 'POST',
json: messageData
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
let recipientId = body.recipient_id;
let messageId = body.message_id;
console.log("Successfully sent generic message with id %s to recipient %s",
messageId, recipientId);
} else {
console.error("Unable to send message.");
console.error(response);
console.error(error);
}
});
}
let server = app.listen(port, function () {
InterfaceLogin.process();
console.log('App listening on port 8080!');
});
server.timeout = 1000;
In my console, During the last respond I see :
Received message for user xxx and page xxx at xxx with message:
{"mid":"mid.$xxx","seq":xxxx,"text":"k"}
Successfully sent generic message with id mid.$xxx to recipient xxxxx
the bot don't respond, and after some minutes I see :
Received message for user xxx and page xxx at xxx with message:
{"mid":"mid.$xxx","seq":xxxx,"text":"l"}
Successfully sent generic message with id mid.$xxx to recipient xxxx
Webhook received unknown event: { sender: { id: 'xxx' },
recipient: { id: 'xxx' },
timestamp: xxx,
delivery:
{ mids: [ 'mid.$xxx' ],
watermark: xxx,
seq: 0 } }
the bots responds
My last respond is :
Received message for user xxx and page xxx at xxx with message:
{"mid":"mid.$xxx","seq":xx,"text":"k"}
Successfully sent generic message with id mid.$xxx to recipient xxx
Received message for user xxx and page xxx at xxx with message:
{"mid":"mid.$xxx","seq":xx,"text":"l"}
Successfully sent generic message with id mid.$xxx to recipient xxxx
Webhook received unknown event: { sender: { id: 'xxx' },
recipient: { id: 'xxx' },
timestamp: xxx,
delivery:
{ mids: [ 'mid.$xxx' ],
watermark: xxx,
seq: 0 } }

The messenger bot platform has rate limit, if you exceed it the requests will queue up and execute later.
https://developers.facebook.com/docs/messenger-platform/reference/send-api/

Related

how to toggle notification sounds on/off depending on user preference when sending thru background trigger

I'm sending a notification to users using a background trigger in a flutter. Now the problem I'm facing is how can I make a switch to the sounds payload so that the user can toggle it from my app settings if he wants to control notification sounds.
below is a sample of my function.
export const onTestCreate = functions.firestore
.document("testers/{testerId}")
.onCreate(async (snap, context)=> {
// message senderId
const testerId: string = context.params.testerId;
functions.logger.log(context);
try {
const payload = {
"notification": {
title: "test title",
body: "this is a test",
sound: "default",
badge: "1",
},
"data": {
click_action: "FLUTTER_NOTIFICATION_CLICK",
screen: "person_chat_screen",
id: testerId,
},
};
const tokens: string[] = ["token"];
console.log(`tokens is ====> ${tokens}`);
return await admin.messaging()
.sendToDevice(tokens, payload).then(()=> {
return;
});
} catch (e) {
functions.logger.log(e);
throw e;
}
});

Firebase Admin SDK sendMulticast

This code with a smaller list of tokens works correctly, but I don't know why it fails to send the notification to all the tokens when individually the token is valid.
I am doing something wrong? when the token list contains fewer tokens, all notifications are sent. There is a maximum of 30 tokens.
let notificationData = {
Id: messageInfo.ChatId,
Type: notificationType.ChatMessage,
Data: chatRoom
};
var payload = {
notification: {
title: title,
body: body,
},
data: {
NotificationData: JSON.stringify(notificationData),
},
apns: {
payload: {
aps: {
sound: "default",
},
},
},
};
payload.tokens = chatRoom.FCMTokens;
return admin.messaging().sendMulticast(payload).then(response => {
if (response.failureCount > 0) {
const failedTokens = [];
response.responses.forEach((resp, idx) => {
if (!resp.success) {
failedTokens.push(payload.tokens[idx]);
}
});
console.log('List of tokens that caused failures: ' + JSON.stringify(response));
console.log('List of tokens that caused failures: ' + failedTokens);
}
else {
console.log("Successsfully MulticastMessage");
}
return null;
}).catch(error => {
console.log("Error sending notification", error);
return null;
});
more info:
The problem was that being many users in the payload exceeded 4kb
Notification messages can contain an optional data payload. Maximum payload for both message types is 4KB, except when sending messages from the Firebase console, which enforces a 1024 character limit.

How to dynamically set aws ses TemplateData using node.js?

I'm trying to send an email with custom data.
async function sendEmailToSalesTeamOnBookingDemoClass(email,name,phoneNumber,utmSource,utmTerm,utmMedium,deviceType,browser,referrer) {
try {
let emailMe = "abc#gmail.com"
const ses = new AWS.SES({ apiVersion: "2010-12-01" });
const params = {
Destination: {
ToAddresses: [emailMe]
},
Source: senderEmail,
Template: "Sales_Email_Demo",
TemplateData : JSON.stringify({
"name": name,
"email": email,
"phone": phoneNumber,
"utmSource":utmSource,
"utmTerm":utmTerm,
"utmMedium":utmMedium,
"browser":browser,
"referrer":referrer,
"deviceType":deviceType
}),
Tags: [
{
Name: 'SomeName',
Value: 'info'
}
]
};
const sendEmailReceiver = ses.sendTemplatedEmail(params).promise();
sendEmailReceiver
.then(data => {
console.log(senderEmail,emailMe)
console.log("Email submitted to SES", data);
})
.catch(error => {
console.log("Email not submitted to SES:" + error);
});
}
catch (e) {
console.error(`Error: ${e}`);
}
}
The response says that the email was sent successfully.
Email submitted to SES {
ResponseMetadata: { RequestId: 'c08b9948-7be3-465d-a72c-fcca23f9f059' },
MessageId: '010901793bd95a02-813072dc-bb87-4b65-91c4-03f7f29dcbd0-000000'
}
But there is no email received even though the sender email has been verified and tested. Is there something wrong with my template?
{
"Template": {
"TemplateName": "Sales_Email_Demo",
"SubjectPart": "some subject!",
"HtmlPart": "<html> <body><p>Hello,</p><p>Details:</p><p>Name:{{name}}</p><p>Phone Number: {{phoneNumber}}</p><p>Email Id: {{emailId}}</p><p>Source: {{utmSource}}</p><p>Medium: {{utmMedium}}</p><p>Term:{{utmTerm}}</p><p>Device:{{deviceType}}</p><p>Browser:{{browser}}</p><p>Referrer: {{referrer}}</p></body></html>"
}
}
I figured it out. The issue was with the template. The sending and receiving variables were named differently.

Push notification returns ECONNRESET in Google Cloud Functions

I am having a function in Firebase Cloud Functions that is retrieves the user's device group id in order to send a push notification, and after sends a push notification. This works well if the function gets called only once, but if I have an array of users I want to send a push notification too, the sendPushNotification function returns error : FAILED err= { RequestError: Error: read ECONNRESET
at new RequestError (/user_code/node_modules/request-promise/node_modules/request-promise-core/lib/errors.js:14:15) for every try to send push
From what i understand ECONNRESET means that the connection gets closed at one end before finishing the operation, can some help/explain me why this is:
here is my code:
function sendFollowNotification(snapshot) {
const notificationMsg = getFollowNotificationMsg() //returns a string
snapshot.forEach(function(singleUser, index) {
const userId = singleUser.key;
const userObject = singleUser.val();
console.log("will get device group")
if (index + 1 == snapshot.numChildren()) {
return getDeviceGroupNotificationKey(userId, "Discover new artists", notificationMsg, "", true);
} else {
getDeviceGroupNotificationKey(userId, "Discover new artists", notificationMsg, "", false);
}
}
function getDeviceGroupNotificationKey(groupId, notificationTitle, notificationBody, notificationSubject, shouldReturn) {
const pathToDeviceGroup = admin.database().ref('deviceGroups').child(groupId);
pathToDeviceGroup.once("value").then( function(snapshot) {
const deviceGroupObj = snapshot.val();
const notification_key = deviceGroupObj.notification_key;
console.log("got notification key")
console.log(notification_key)
if (notification_key !== undefined) {
return sendPushToDeviceGroupOld(notification_key, notificationTitle, notificationBody, "notificationKeyOld2", notificationSubject, shouldReturn);
} else {
return
}
}).catch(reason => {
console.log("user device group not there")
return
})
}
function sendPushToDeviceGroupOld(notification_key, title, body, subject, message, shouldReturn) {
console.log('sending push to ' + notification_key)
const serverKey = '-';
const senderId = '-';
const options = {
method: 'POST',
uri: 'https://fcm.googleapis.com/fcm/send',
headers: {
'Authorization': 'key=' + serverKey,
'project_id': senderId
},
body: {
to: notification_key,
data: {
subject: message
},
notification: {
title: title,
body: body,
badge: 1,
sound: "default",
},
priority : 'high',
content_available: true
},
json: true
};
return rqstProm(options)
.then((parsedBody) => {
console.log('SUCCESS response=', parsedBody);
return
})
.catch((err) => {
console.log('FAILED', err);
return
});
}

Adding 'typing_on' sender action bubble before each response from Wit.ai chatbot

I've built a flow-base chat bot using FB messenger, Wit.ai and node.js. It's working well, but in order to make the interaction seem more natural I want my bot to pause for a short while and appear to be typing each of its responses.
I want the 'typing' bubble to be displayed briefly before each response my bot sends, ideally being able to define the time the bubble is visible for before the response is sent. At the moment there are sections of my conversation where the bot sends consecutive messages and they are all sent too quickly at once.
The FB Messenger Send API says that either the 'message' or 'sender_action' property must be set. I've tried setting both like so:
const fbMessage = (id, text) => {
if(fruits.apples.indexOf(text) >= 0 || fruits.oranges.indexOf(text) >= 0) {
var body = JSON.stringify({
recipient: { id },
"sender_action":"typing_on",
message: {
attachment: {
"type": "image",
"payload": {
"url": text
}
}
},
});
} else {
var body = JSON.stringify({
recipient: { id },
"sender_action":"typing_on",
message: { text },
});
}
const qs = 'access_token=' + encodeURIComponent(FB_PAGE_TOKEN);
return fetch('https://graph.facebook.com/me/messages?' + qs, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body,
})
.then(rsp => rsp.json())
.then(json => {
if (json.error && json.error.message) {
throw new Error(json.error.message);
}
return json;
});
};
But I get the following error:
I'm not sure what I need to do - I'm assuming I've got to set up some sort of 'sender_action' bot response that's triggered before each conversational response but I don't know how I'd do this.
To display the typing bubble you simply send a sender action of typing_on. This displays the typing indicator for up to 20 seconds, during which time you will send the actual message you want to send.
The JSON for this would be:
{
"recipient":{
"id":"USER_ID"
},
"sender_action":"typing_on"
}
The call is documented here
Got it working, can't work out how to control bubble timing but it's fine for now. The code below will make the typing bubble display briefly before each of my bot's responses without mucking up the flow of my conversation.
FB Messenger code:
const typingBubble = (id, text) => {
var body = JSON.stringify({
recipient: { id },
"sender_action":"typing_on"
});
const qs = 'access_token=' + encodeURIComponent(FB_PAGE_TOKEN);
return fetch('https://graph.facebook.com/me/messages?' + qs, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body,
})
.then(rsp => rsp.json())
.then(json => {
if (json.error && json.error.message) {
throw new Error(json.error.message);
}
return json;
});
};
const fbMessage = (id, text) => {
if(scenarioCombos.trends.indexOf(text) >= 0 || scenarioCombos.disruptions.indexOf(text) >= 0) {
var body = JSON.stringify({
recipient: { id },
message: {
attachment: {
"type": "image",
"payload": {
"url": text
}
}
},
});
} else {
var body = JSON.stringify({
recipient: { id },
message: { text },
});
}
const qs = 'access_token=' + encodeURIComponent(FB_PAGE_TOKEN);
return fetch('https://graph.facebook.com/me/messages?' + qs, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body,
})
.then(rsp => rsp.json())
.then(json => {
if (json.error && json.error.message) {
throw new Error(json.error.message);
}
return json;
});
};
Wit.ai send action code (within 'actions'):
send({sessionId}, {text}) {
const recipientId = sessions[sessionId].fbid;
if (recipientId) {
return typingBubble(recipientId, text), fbMessage(recipientId, text)
.then(() => null)
.catch((err) => {
console.error(
'Oops! An error occurred while forwarding the response to',
recipientId,
':',
err.stack || err
);
});
} else {
console.error('Oops! Couldn\'t find user for session:', sessionId);
return Promise.resolve()
}
},

Resources