FCM delivers duplicated copies of Push Notifications - node.js

I am using fcm node modules to send push notifications. Tried to send to a group(group with one device token). And all it delivers duplicated copies(4 copies everytime) of the same push for a single alert. It was supposed to be delivered only one. I have used fcm-node (version: 1.2.1), fcm-push (version: 1.1.3) etc.
The nodejs code is as shown below:
Alerts.pushTry = function (cb) {
var serverKey = "abcd"; //put your server key here
var deviceToken = 'group_key'; // required
var FCM = require('fcm-node');
var message = { //this may vary according to the message type (single recipient, multicast, topic, et cetera)
to: deviceToken,
notification: {
title: 'Group 555 test'+ ((new Date()).getTime()),
body: 'Body of your push notification'
},
data: {
my_key: 'my value',
my_another_key: 'my another value'
}
};
fcm.send(message, function(err, response) {
console.log("sent");
if (err) {
console.log(err);
console.log("Something has gone wrong!");
cb(err);
} else {
console.log("Successfully sent with response: ", response);
cb(null, response);
}
});
};
Can anyone help me?

There are some open issues exists for the above mentioned node modules.. I have used the request module and connected the fcm push notification url directly without depending on these types of npm modules with open issues.
var request = require('request');
request({
url: 'fcm.googleapis.com/fcm/send',
method: "POST",
headers: { "content-type" : "application/json",
"Authorization": key=${constants.FCM_SERVER_KEY},
"project_id": constants.FCM_SENDER_ID, },
json: message
}, function(error, response, body) {
//You code
});

Related

Problem with validating header/data fields from JQuery AJAX to Backend flask API

I'm trying to pass JWT token and data fields via Jquery and then consume those values in Flask.
So, my client side query looks like this:
function getData() {
var data = {
UserPoolId : 'AWS CognitoUser Pool Id',
ClientId : 'AWS CognitoUser client Id'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(data);
var cognitoUser = userPool.getCurrentUser();
cognitoUser.getSession(function(err, session) {
if (err) {
alert(err);
return;
}
console.log('session validity: ' + session.isValid());
console.log(cognitoUser.signInUserSession.accessToken.jwtToken);
//
$.ajax({
url: "/api/v1/count",
type: "POST",
headers: { "X-Test-Header": cognitoUser.signInUserSession.accessToken.jwtToken },
// data: JSON.stringify(data),
data: JSON.stringify("{'UserPoolId': 'XXXX','ClientId': 'XXXX'}"),
contentType: 'application/json; charset=utf-8',
error: function(err) {
switch (err.status) {
case "400":
// bad request
break;
case "401":
// unauthorized
break;
case "403":
// forbidden
break;
default:
//Something bad happened
break;
}
},
success: function(data) {
console.log(data);
}
});
//
});
}
Now, in my serverside flask:
I tried to catch the token value using below: which is not working
#app.route("/api/v1/count", methods=["GET", "POST"])
def get_data_count():
if 'X-Test-Header' in request.headers:
headers = request.headers
app.logger.info(headers)
auth = headers.get("X-Test-Header")
app.logger.info('testing info log' + auth)
Also I tried to catch the data fields , using result = request.get_json() also not working.
I tried to inspect in chrome, and I don't see these values being added to the request header.
Can anyone suggest to me if I'm doing it correctly while passing the values from client to server? I also don't see console.log(cognitoUser.signInUserSession.accessToken.jwtToken) in the console log.
if not can anyone suggest to me, how to fix it?

Why is JSON.parse(event.body) returning null despite form input being successfully transferred to backend? (Code and Logs included)

I'm trying to get values being passed into a form to themselves be passed to my lambda function backend.
The first roadblock I encountered on this project was that the event instance that is passed as a parameter to my handler was empty. That is, when I would try and log the event instance, the event would come back empty.
As a rough example, this code:
module.exports.handler = (event, context, callback) => {console.log(event);} would return "{}."
After some research, I learned that the reason the event instance would come back as empty, is because I had failed to check the lambda proxy integration option at the "Integration Requests" page on the API's Resource/Method page.
So, I enabled lambda proxy integration, and it sort of worked. the event instance is no longer empty; It doesn't return "{}." anymore.
However, although the event instance is now full of information, the properties/values I'm trying to recover from the event instance are now null, specifically body.
So, although event is no longer empty, JSON.parse(event.body); returns null
I don't understand where or why the values being passed into the form are being lost when transmitted to my lambda function backend.
You can see Cloudwatch logs yourselves here:
And here are both the handlers and the front-end codes, respectively.
const AWS = require('aws-sdk');
const validator = require("validator");
module.exports.handler = function(event, context, callback) {
var response = {
statusCode:200,
headers:{
"Access-Control-Allow-Origin": "*",
'Access-Control-Allow-Credentials':"true",
"Content-Type":"application/json"
},
body: JSON.stringify({"body":event})
}
console.log(response);
console.log(context);
try{
console.log("inside the try loop", null, 2);
console.log("before parsing the json from the post method", null, 2);
console.log(event);
var data = JSON.parse(event.body);
console.log(data);
console.log("after parsing the json from the post method", null, 2);
var email = data.email;
var comment = data.comment;
if(validator.isEmail(email) == "false" || validator.isEmpty(comment) == "false"){
callback(new Error("The email or comment were defectous."));
return;
}
email = validator.escape(email);
comment = validator.escape(comment);
callback(null, response);
return;
} catch(error){
console.log("inside the error loop", null, 2);
console.log(error);
console.log(error.description);
callback(null, response);
return;
}
};
$(function(){
$('.contactForm').submit(function(event){
event.preventDefault();
console.log("Submit event is fired");
var data = {
email: getEmail(),
comment: getComment()
};
$.ajax({
url: "SOMEURL",
type: 'post',
contentType: "application/json",
body: JSON.stringify(data),
success: function(){
console.log("Lambda Function successully invoked.");
console.log(data);
$('.btn').attr('disabled', true);
$('#emailFormInput').val('');
$('#commentFormInput').val('');
},
error: function(jqXHR, textStatus, errorThrown){
if (jqXHR.status == 500) {
console.log('Internal error: ' + jqXHR.responseText+" "+errorThrown);
} else {
console.log(errorThrown);
}
}
});
});
});

Respond to a bot call in Microsoft Teams with Graph API

I trying to respond to a call in Teams but actually I'm not getting a respond from the bot.
First I get access_token from Graph API.
Then I have a route that intercept bot calls.
app.post("/api/call", function(req, res) {
if (j === 1) {
j = j + 1;
res.status(204).send();
} else {
var answerbody = {
callbackUri: "https://8a73b7ad.ngrok.io/api/call",
acceptedModalities: ["audio"],
mediaConfig: {
"#odata.type": "#microsoft.graph.serviceHostedMediaConfig",
preFetchMedia: [
{
uri: "https://cdn.contoso.com/beep.wav",
resourceId: "1D6DE2D4-CD51-4309-8DAA-70768651088E"
},
{
uri: "https://cdn.contoso.com/cool.wav",
resourceId: "1D6DE2D4-CD51-4309-8DAA-70768651088F"
}
]
}
};
POST(
"https://graph.microsoft.com/beta/" + req.body.resource + "/answer",
answerbody
)
.then(
data => console.log(data) // I get undefined
)
.catch(function(err) {
console.log("err " + err);
res.status(200).send();
});
}
});
Here's POST function
function POST(url, BB) {
return new Promise(function(resolve, reject) {
var options = {
url: url,
method: "POST",
headers: {
Accept: "application/json",
Authorization: "Bearer " + token
},
body: BB,
json: true
};
request(options)
.then(function(body) {
resolve(body);
})
.catch(function(err) {
reject(err);
});
});
}
As mentionned in documentation , Server sould first reply 204 in order to get response in Graph API protocol.
Actually I don't get a response. Bot still ringing until It gets voice message : " You can't talk to the bot just yet , we are working on it".
As mentioned in Teams API documentation, I should get callback with the ressource id and other information to be able to answer to the call.
So I use my POST function to answer. but here I don't get any 202 Accepted response as indicated in docs, instead I get more than one callback with different ressource ids, then after some seconds I get the voice message.
The solution is to change acceptedModalities: ["audio"]
to acceptedModalities: ["Audio"]

How to use facebook messenger send image API in Node js?

I'm building a facebook bot in nodejs with facebook messenger API. I'm trying to send a image from the bot by directly uploading the image file from the heroku server itself (not through URL) and it does not work.
Here is a error log from the console.
Failed calling Send API 400 Bad Request { message: '(#100) Incorrect
number of files uploaded. Must upload exactly one file.',type:
'OAuthException', code: 100,error_subcode: 2018005,fbtrace_id:
'E32ogm/ofxd' }
The official facebook document only contains an example in curl format and I'dont know how to replicate this curl into node format.
I've tested with curl and it worked like a charm.
curl \ -F 'recipient={"id":"recipientId"}' \ -F 'message={"attachment":{"type":"image", "payload":{}}}' \ -F 'filedata=#resource/pdf_img/sample.jpg;type=image/jpeg' \ "https://graph.facebook.com/v2.6/me/messages?access_token=PAGE_ACCESS_TOKEN"
This is my node implementation that seems to be problematic,
//file_loc = __dirname+"/resource/pdf_img/sample.jpg"
function sendImageMessage(recipientId, file_loc){
let fs = require('fs');
var readStream = fs.createReadStream(file_loc);
var messageData = {
recipient : {
id : recipientId
},
message : {
attachment : {
type : "image",
payload :{}
}
},
filedata:readStream
}
callSendAPI(messageData);
}
function callSendAPI(messageData) {
request({
uri: "https://graph.facebook.com/v2.6/me/messages",
qs: {access_token: process.env.PAGE_ACCESS_TOKEN},
method: "POST",
json: messageData
}, function(error, response, body) {
if (!error && response.statusCode == 200) {
var recipientId = body.recipient_id;
var messageId = body.message_id;
if (messageId) {
console.log("Successfully sent message with id %s to recipient %s",
messageId, recipientId);
} else {
console.log("Successfully called Send API for recipient %s",
recipientId);
}
} else {
console.error("Failed calling Send API", response.statusCode, response.statusMessage, body.error);
}
});
}
Please any help with fixing my node implementation or translating that curl in to node would be appreciated.
You can use forms /form-data/ in the request module (which has integrated module 'form-data').
But all requests needs to be stringified.
So, based on your example, the following should do the job >>
function sendImageMessage(recipientId, file_loc){
let fs = require('fs');
var readStream = fs.createReadStream(file_loc);
var messageData = {
recipient : {
id : recipientId
},
message : {
attachment : {
type : "image",
payload :{}
}
},
filedata:readStream
}
callSendAPI(messageData);
}
function callSendAPI(messageData) {
var endpoint = "https://graph.facebook.com/v2.6/me/messages?access_token=" + process.env.PAGE_ACCESS_TOKEN;
var r = request.post(endpoint, function(err, httpResponse, body) {
if (err) {return console.error("upload failed >> \n", err)};
console.log("upload successfull >> \n", body); //facebook always return 'ok' message, so you need to read error in 'body.error' if any
});
var form = r.form();
form.append('recipient', JSON.stringify(messageData.recipient));
form.append('message', JSON.stringify(messageData.message));
form.append('filedata', messageData.filedata); //no need to stringify!
}
Details here https://github.com/request/request#forms
I think sending variable messagedata as formdata would solve the problem.
var FormData = require('form-data');
var fs = require('fs');
var https = require('https');
function sendImageMessage(recipientId, file_loc){
var readStream = fs.createReadStream(file_loc);
var messageData = new FormData();
messageData.append('recipient', '{id:' +recipientId+ '}');
messageData.append('message', '{attachment :{type:"image", payload:{}}}');
messageData.append('filedata', readStream);
callSendAPI(messageData);
}
Secondly you need to change request a bit since now you are using formdata. I have done it using module https and so have changed the callSendAPI() code accordingly. You can find out how to send the formdata using request module.
function callSendAPI(messageData) {
var options = {
method: 'post',
host: 'graph.facebook.com',
path: '/v2.6/me/messages?access_token=' + pagetoken,
headers: messageData.getHeaders()
};
var request = https.request(options);
messageData.pipe(request);
request.on('error', function(error) {
console.log("Unable to send message to recipient %s", recipientId);
return;
});
request.on('response', function(res) {
if (res.statusMessage == "OK") {
console.log("Successfully sent message to recipient %s", recipientId);
} else {
console.log("Unable to send message to recipient %s", recipientId);
}
return;
});
}

Can't we use anything other than displayText from webhook response

I am new to api.ai and I was doing the webhook implementation. I've noticed that only the "speech" or "displayText" from webhook response is shown to the user. Is there any technique to use any other params from the response?
I would be glad, if anyone tell me how could I format the response text like making it bold, change font etc.
Thank you!
Note that if the client you are sending the response to (such as Facebook Messenger) does not support special formats such as bold, this is an exercise in futility.
That said, there are many richer types of responses you can send than just plain text, and if you want to do this programmatically rather than build rich responses in API.ai, I'd suggest injecting your custom server side solution between the client and API.ai rather than have it at the end of the process.
In other words:
Client interface <-> Custom solution <-> API.ai
rather than
Client <-> API.ai <-> Custom fulfillment
This gives you more customization and fulfillment options, including building entirely custom response/prompt logic without even hitting the API.ai endpoint, or further editing API returns after they are processed, for example appending a database query result to the text of an API.ai fulfillment.
Assuming you have a setup like this, in node.js a solution for sending more advanced payloads than text to Facebook Messenger would look like this:
//Send gif or image reply
function sendGifMessage(recipientId) {
var messageData = {
recipient: {
id: recipientId
},
message: {
attachment: {
type: "image",
payload: {
url: config.SERVER_URL + "FILE LOCATION"
}
}
}
};
callSendAPI(messageData);
}
// Send custom payload buttons
function sendButtonMessage(recipientId, text, buttons) {
var messageData = {
recipient: {
id: recipientId
},
message: {
attachment: {
type: "template",
payload: {
template_type: "button",
text: text,
buttons: buttons
}
}
}
};
callSendAPI(messageData);
}
// Send quickReply buttons
function sendQuickReply(recipientId, text, replies, metadata) {
var messageData = {
recipient: {
id: recipientId
},
message: {
text: text,
metadata: isDefined(metadata)?metadata:'',
quick_replies: replies
}
};
callSendAPI(messageData);
}
function callSendAPI(messageData) {
request({
uri: 'https://graph.facebook.com/v2.6/me/messages',
qs: {
access_token: config.FB_PAGE_TOKEN
},
method: 'POST',
json: messageData
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
var recipientId = body.recipient_id;
var messageId = body.message_id;
if (messageId) {
console.log("Successfully sent message with id %s to recipient %s",
messageId, recipientId);
} else {
console.log("Successfully called Send API for recipient %s",
recipientId);
}
} else {
console.error("Failed calling Send API", response.statusCode, response.statusMessage, body.error);
}
});
}
Hope that helps

Resources