I need to send a high priority push notification on Android devices. I have implemented FCM. When I uncomment the priority section then FCM gives 'Undefined' in response.
FCM(deviceToken, message, badge, metadata={},time_to_live=false){
return new Promise((resolve, reject) => {
let fcm = new FCMlib(this._fcm_server_key);
/*Send notification to Android Device*/
var pushParam = {
to: deviceToken,
data : {
title: message, //'Title of your push notification',
body: message,
metadata: metadata
},
// android:{
// priority:high
// },
time_to_live: 10 ,
//The time in seconds
};
if(time_to_live === true){
delete pushParam.time_to_live;
}
fcm.send(pushParam, (err, response) => {
if(err){
console.log('err---1 ',err);
reject(err);
}
if(response) {
/*update user badge*/
console.log('response--- ',response);
this.mysql.query("UPDATE `user_device_token` SET badge = ? WHERE `
device_token` = ?", [badge, deviceToken])
.then(result => {
/*badge would be update*/
});
resolve(response);
}
});
});
}
Related
I am building a node.js API to manage IOT devices. The IOT device have a state which can be true or false, depending on user input. The server subscribes the user to the device topic, once the user adds the device to their account. I am able to toggle the state easily, with the following code :
exports.toggleState = async (req, res, next) => {
let deviceID = req.params.id
let userId = req.userId
try {
let userdata = await User.findById(userId)
let deviceList = userdata.deviceList
deviceList = deviceList.map(el => {
return el.toString()
})
if (deviceList.indexOf(deviceID) !== -1) {
Device.findById(mongoose.Types.ObjectId(deviceID)).then(result => {
result.state = !result.state
return result.save()
})
.then(result => {
req.mqttClient.publish(`${result.topic}`, `${result.state}`, { qos: 0, retain: true }, (error) => {
if (error) {
throw error
}
})
res.status(201).json({ message: 'Device State Changed', data: result })
})
.catch(err => {
console.log(err)
throw err
})
}
else {
res.status(500).json({ message: 'Device not linked to your account!' })
}
} catch (err) {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
}
};
I have added mqtt to make it easier for device side operation (Arduino based).
The challenge I am facing is that if the state changes from the device side (via hardware input), how do I keep the device state updated on the database through the Mqtt server? Some thing like :
Device.findById(deviceID).then(result => {
req.mqttClient.on('message', (`${result.topic}`, payload) => {
console.log('Received Message:', `${result.topic}` , payload.toString())
})
result.state = payload.toString()
return result.save()
}.then(()=>{
console.log('State successfully updated')
}
Problem is I don't know where do I plug this code so that it runs continuously, monitoring incoming messages and updating device state. Would greatly appreciate any help in this.
I'm doing a chatbot for facebook and I have a problem with the order of sending messages
I send in the chat:
-Hi
The bot responds in the console:
-Hello!
-How can I help you?
but in the chat responds like this:
-How can I help you?
-Hello!
I tried to apply async and await, but it didn't work.
let postWebhook = (req, res) => {
// Parse the request body from the POST
let body = req.body;
// Check the webhook event is from a Page subscription
if (body.object === 'page') {
// Iterate over each entry - there may be multiple if batched
body.entry.forEach(function (entry) {
// Gets the body of the webhook event
let webhook_event = entry.messaging[0];
// Get the sender PSID
let sender_psid = webhook_event.sender.id;
// Check if the event is a message or postback and
// pass the event to the appropriate handler function
if (webhook_event.message) {
handleMessage(sender_psid, webhook_event.message);
}
});
// Return a '200 OK' response to all events
res.status(200).send('EVENT_RECEIVED');
}};
let handleMessage = async (sender_psid, received_message) => {
let response;
if (received_message.text) {
addUrl(`url/facebook?mensage=${received_message.text}&usuario=${user}&session=${sender_psid}&origem=${chanel}`)
.then(res => res.json())
.then(async json => {
for (var i = 0; i < json.length; i++) {
console.log(json[i].text || json[i].title)
response = {json[i].text || json [i] tittle}
await callSendAPI(sender_psid, response) //function to verify user and send message
}
})
await callSendAPI(sender_psid, response);
}
};
How can I ensure the correct order?
Well you can simplify this way:
const callSendAPII = (sender_psid, response) => {
return new Promise((resolve, reject) => {
let request_body = {
"recipient": {
"id": sender_psid
},
"message": response
}
request({
uri: 'https://graph.facebook.com/v12.0/me/messages',
qs: {"access_token": MY_IG_PAGE_TOKEN},
method: 'POST',
json: request_body,
}, (error, response, body) => {
if (error) {
reject(error);
} else {
resolve(response);
}
});
})
now just apply await in the calling function:
await callSendAPII(sender_psid, response)
So I am new to javascript, and I am pretty sure the code is less than ideal. I am running into some issues getting the data from Firestore.
The personalMessage function takes around 50 seconds to complete, and I a have no idea why it takes so long.
This code in swift will return from the database under 1000ms.
Also any pointers in code style is recommended.
function sendMessageToDevice(token, payload, options) {
admin.messaging().sendToDevice(token, payload, options)
.then(response => {
console.log('Successfully sent message:', response, response.results[0].error);
return response
})
.catch(error => console.log('Error sending message:', error));
}
function getUser(userId) {
return admin.firestore().collection('users').doc(userId).get()
.then(snapshot => {
if (!snapshot.exists) {
console.log('No such document!');
return null;
}
return snapshot.data()
})
.catch(err => {
console.log('Error getting document', err);
return err;
});
}
exports.personalMessage = functions.firestore
.document('/messages/{id}')
.onCreate((snapshot, context) => {
var messageData = snapshot.data();
var userId = messageData.user;
var fromId = messageData.from;
Promise.all([getUser(userId), getUser(fromId)])
.then(([dataA, dataB]) => {
console.log(dataA.fcmToken, dataB.name);
var payload = {
notification: {
title: dataB.name + ' messaged you.',
body: 'Go check it out it',
clickAction: 'NEW_PERSONAL_MESSAGE'},
data: {
messageId: context.params.id}
};
var options = {
contentAvailable: false,
priority: 'high'
}
return sendMessageToDevice(dataA.fcmToken, payload, options);
})
.catch(error => console.log('Error sending message:', error));
return Promise.resolve('success');
});
As Doug talk about the incorrect promises. I change a little in your code.
However, the message may be not come immediately for some reason like network,...
function sendMessageToDevice(token, payload, options) {
return admin.messaging().sendToDevice(token, payload, options)
}
function getUser(userId) {
return admin.firestore().collection('users').doc(userId).get()
}
exports.personalMessage = functions.firestore
.document('/messages/{id}')
.onCreate((snapshot, context) => {
var messageData = snapshot.data();
var userId = messageData.user;
var fromId = messageData.from;
return Promise.all([getUser(userId), getUser(fromId)])
.then(result=> {
if (!result[0].exists || !result[1].exists) {
console.log('No such document!');
return null;
}
return [result[0].data(),result[1].data()]
})
.then(([dataA, dataB]) => {
console.log(dataA.fcmToken, dataB.name);
var payload = {
notification: {
title: dataB.name + ' messaged you.',
body: 'Go check it out it',
clickAction: 'NEW_PERSONAL_MESSAGE'},
data: {
messageId: context.params.id}
};
var options = {
contentAvailable: false,
priority: 'high'
}
return sendMessageToDevice(dataA.fcmToken, payload, options);
})
.then(response => {
console.log('Successfully sent message:', response,
response.results[0].error);
return Promise.resolve('success');
})
.catch(error => console.log('Error sending message:', error));
});
up to now I have created my push notification service for my angular app using service worker, app manifest and fire-base.
I'm getting the server key and sender_id. I' m registering my service worker and subscribe to the push_messenger.
also I'm using google local server extension to host my server.
main.ts
Notification.requestPermission(function (status) {
console.log('Notification permission status:', status);
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./service-worker.js', { scope: './' }).then(function (registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
console.log('registration: ', (registration));
navigator.serviceWorker.ready.then(reg => {
reg.pushManager.getSubscription().then(sub => {
if (sub == undefined) {
console.log('sub : ' + 'undefined');
navigator.serviceWorker.getRegistration().then((reg) => {
reg.pushManager.subscribe({
userVisibleOnly: true
}).then(sub => {
console.log('sub : ' + JSON.stringify(sub));
localStorage.setItem("sub", JSON.stringify(sub));
}, err => {
console.log('registration error occured: ' + err);
})
}, err => {
console.log('registration error occured: ' + err);
})
} else {
console.log('sub : ' + sub);
// subs = sub;
localStorage.setItem("sub", JSON.stringify(sub));
}
}, err => {
console.log('registration error occured: ' + err);
});
})
}).catch(function (err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
}
service-worker.js
self.addEventListener('notificationclose', function(e) {
var notification = e.notification;
var primaryKey = notification.data.primaryKey;
console.log('Closed notification: ' + primaryKey);
});
self.addEventListener('notificationclick', function(e) {
var notification = e.notification;
var primaryKey = notification.data.primaryKey;
var action = e.action;
if (action === 'close') {
notification.close();
} else {
clients.openWindow('samples/page' + primaryKey + '.html');
notification.close();
}
// TODO - close all notifications when one is clicked
});
self.addEventListener('push', function(e) {
var body;
if (e.data) {
body = e.data.text();
} else {
body = 'Push message no payload';
}
var options = {
body: body,
icon: 'images/notification-flat.png',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: 1
},
actions: [
{action: 'explore', title: 'Explore this new world',
icon: 'images/checkmark.png'},
{action: 'close', title: "I don't want any of this",
icon: 'images/xmark.png'},
]
};
e.waitUntil(
self.registration.showNotification('Push Notification', options)
);
});
node server
var webPush = require('web-push');
var pushSubscription = {<subscription object>}
};
var payload = 'Sup Dude!';
var options = {
gcmAPIKey: *<server key>*,
TTL: 60,
};
webPush.sendNotification(
pushSubscription,
payload,
options
);
in the above main.ts I was able to get the subscription object when the app was initialized. and able to send the push notification at that moment. but when I open this same server IP from chrome I'm getting a different subscription object. also sometime I'm getting different subscription object using chrome.
the questions is How can I send push notifications for all users, since the subscription object is differ from time to time and browser to browser.
(cannot store all the data to a database which will be excessive amount of storage)
I think you should use FCM for this purpose. Where you can create a group and send notification to all of them. But even for creating group you would require deviceId for each device.
You can store all these id's with you in backend and send FCM web push to all.
https://firebase.google.com/docs/cloud-messaging/js/client
You can go through FCM documentation achieve this.
This is official firebase cloud-messaging documention very usefull...!
also checkout below link,
firebase cloud-messaging google
firebase cloud-messaging staring (subscribe this channel)
This is my working code for push notification token registration at client-side, may be work for you
<script>
messaging.requestPermission()
.then(function() {
console.log('Notification permission granted.');
// TODO(developer): Retrieve an Instance ID token for use with FCM.
messaging.getToken()
.then(function(currentToken) {
if (currentToken) {
console.log(currentToken);
settingTokenToServer(currentToken);
} else {
// Show permission request.
console.log('No Instance ID token available. Request permission to generate one.');
setTokenSentToServer(false);
refreshToken();
}
})
.catch(function(err) {
console.log('An error occurred while retrieving token............................... ', err);
});
})
.catch(function(err) {
console.log('Unable to get permission to notify.', err);
});
messaging.onTokenRefresh(function() {
messaging.getToken()
.then(function(refreshedToken) {
console.log('Token refreshed.');
// Indicate that the new Instance ID token has not yet been sent to the
// app server.
setTokenSentToServer(false);
// Send Instance ID token to app server.
sendTokenToServer(refreshedToken);
// ...
})
.catch(function(err) {
console.log('Unable to retrieve refreshed token ', err);
showToken('Unable to retrieve refreshed token ', err);
});
});
function settingTokenToServer(subscription_id) {
if (!isTokenSentToServer()) {
//setting token to FCM server
var ref = firebase.database().ref("notes/token");
ref.push({subscription_id}).then(function() {
console.log("Token saved Successfully..!");
}).catch(function(error) {
alert("Token not saved..." + error);
});
setTokenSentToServer(true);
} else {
console.log('Token already sent to server so won\'t send it again unless it changes');
}
}
function isTokenSentToServer() {
return window.localStorage.getItem('sentToServer') == 1;
}
function setTokenSentToServer(sent) {
window.localStorage.setItem('sentToServer', sent ? 1 : 0);
}
</script>
Here is the current code (subscription to a single item)
podio.authenticate('password', {
'username': podioUser.username,
'password': podioUser.password
}, function (response, body) {
const item_id = 1234567;
podio.get('/item/' + item_id, {}, function (response, body) {
push.addSubscription(body.push);
});
});
Here is the complete code,
// Initialize faye client
var fayeClient = new faye.Client('https://push.podio.com/faye');
// Extend faye client with signature and timestamp used for authentication
fayeClient.addExtension({
'outgoing': function (message, callback) {
message.ext = message.ext || {};
message.ext = {private_pub_signature: push.channel.signature, private_pub_timestamp: push.channel.timestamp};
callback(message);
}
});
const push = {
subscription: null,
channel: null,
addSubscription: function (channel) {
this.channel = channel;
this.subscription = fayeClient.subscribe(this.channel.channel).withChannel(function (channel, message) {
var client = new faye.Client('http://localhost:8000/');
client.publish('/messages', {
text: message
});
});
this.subscription.then(function () {
console.log('Subscription is now active');
}, function (error) {
console.error('Subscription failed: ', error.message, error);
});
}
};
podio.authenticate('password', {
'username': podioUser.username,
'password': podioUser.password
}, function (response, body) {
const item_id = 1234567;
podio.get('/item/' + item_id, {}, function (response, body) {
push.addSubscription(body.push);
});
});
bayeux.attach(server);
server.listen(8002);
Is it possible to subscribe multiple items? I tried item id loop through and subscribe, it doesn't work.