Notify Users when Child is added on specific node using Cloud function - node.js

Is it possible for cloud function to listen for specific node for when a child is added and then send a notification to users located on a different node, and if that is possible how so? I am using node.js with Firebase realtime database and not Firestore.
This is my database:
I want the cloud function to listen every time a child is added on "Emergencies", and then notify all the users in the "Registered Admins"
This is the contents of the users in "Registered Admins" node, it has a child "Notification" containing the message, and I want to send that message to all the users, when a child is added on "Emergencies" node.
This is my cloud function using node.js. I've deployed it however it does not work, does not send any notification at all.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.listen = functions.database.ref("/Emergencies")
.onWrite(async (change, context) => {
change.after.val();
context.params.pushId;
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database()
.ref("/Registered Admins/{uid}/Token").once("value");
// The snapshot to the user's tokens.
let tokensSnapshot;
// The array containing all the user's tokens.
let tokens;
const results = await Promise.all([getDeviceTokensPromise]);
tokensSnapshot = results[0];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return functions.logger.log(
'There are no notification tokens to send to.'
);
}
functions.logger.log(
'There are',
tokensSnapshot.numChildren(),
'tokens to send notifications to.'
);
// Notification details.
const payload = {
notification: {
title: "New Emergency Request!",
body: "Someone needs help check Emergenie App now!",
}
};
// Listing all tokens as an array.
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
const response = await admin.messaging().sendToDevice(tokens, payload);
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
functions.logger.error(
'Failure sending notification to',
tokens[index],
error
);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});

Yes, that sounds possible and is in fact quite close to what the example on notifying users when something interesting happens does.
To send a message to a specific device, you 'll need to know the token for that device. If you want to broadcast a message to multiple users, you could subscribe those users to a topic. Just keep in mind that anyone can subscribe to a topic if they know its name, so you can't use that to send messages that only a certain group of users is allowed to see.

Related

I have this node.js cloud function but it does not work?

I have this cloud function using node.js that listen every time a child is added on a specific node, then it sends a notification to the users. However when I added something on the database, it does not send anything. I am working on android studio java. Should I connect the function to the android studio, if it will only listen on the database and then send FCM messages on the device tokens.
also how to do debugging on this, I am using VS code.
This is my code:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.listen = functions.database.ref("/Emergencies/{pushId}")
.onCreate(async (change, context) => {
change.after.val();
context.params.pushId;
// Get the list of device notification tokens. Note: There are more than 1 users in here
const getDeviceTokensPromise = admin.database()
.ref("/Registered Admins/{uid}/Token").once("value");
// The snapshot to the user's tokens.
let tokensSnapshot;
// The array containing all the user's tokens.
let tokens;
const results = await Promise.all([getDeviceTokensPromise]);
tokensSnapshot = results[0];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return functions.logger.log(
'There are no notification tokens to send to.'
);
}
functions.logger.log(
'There are',
tokensSnapshot.numChildren(),
'tokens to send notifications to.'
);
// Notification details.
const payload = {
notification: {
title: "New Emergency Request!",
body: "Someone needs help check Emergenie App now!",
}
};
// Listing all tokens as an array.
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
const response = await admin.messaging().sendToDevice(tokens, payload);
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
functions.logger.error(
'Failure sending notification to',
tokens[index],
error
);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
This seems wring:
const getDeviceTokensPromise = admin.database()
.ref("/Registered Admins/{uid}/Token").once("value");
The {uid} in this string is not defined anywhere, and is also going to be treated as just a string, rather than the ID of a user - which is what I expect you want.
More likely, you'll need to:
Load all of /Registered Admins
Loop over the results you get from that
Get the Token value for each of them
If you are new to JavaScript, Cloud Functions for Firebase is not the easiest way to learn it. I recommend first using the Admin SDK in a local Node.js process or with the emulator suite, which can be debugged with a local debugger. After those you'll be much better equipped to port that code to your Cloud Functions.

How can i get the values of the field in Firebase cloud function node

I want to send notifications to a specific device using the Firebase cloud function in nodejs. I successfully get the token that I store inside the firestore.
As in this screenshot, the name field and record field are in the same path. The problem is, I want to get the name's value and send it through the notification. Is there is possible way?
This is my NodeJS code:
const admin = require("firebase-admin");
const functions = require("firebase-functions");
admin.initializeApp(functions.config().firebase);
// get the token of the device
async function getToken() {
var instance = await admin.firestore().collection('fcm').doc('token').get();
let _token = instance.data()['token'];
console.log(_token);
return _token;
}
// Send notification when new record are created.
exports.timeInNotification = functions.firestore.document('employee/{employeeId}/record/{recordId}').onCreate(async (event) => {
// get the fcm token
var token = await getToken();
// get the name (the name field is on the same path with the record field)
var name;
let body = name + "has time in";
// time in message
let timeIn = "Time in";
var message = {
notification: {
title: timeIn,
body: body
}
};
// send the notification
let response = await admin.messaging().sendToDevice(token,message);
console.log(response);
});
// Send notification when the field inside record update.
exports.timeOutNotification = functions.firestore.document('employee/{employee}/record/{recordId}').onUpdate(async (event) => {
// get the fcm token
var token = await getToken();
// get the name (the name field is on the same path with the record field)
var name;
let body = name + " has time out";
// time out message
let timeOut = "Time out";
// notification message
var message = {
notification: {
title: timeOut,
body: body
}
}
// send the notification message
let response = await admin.messaging().sendToDevice(token,message);
console.log(response);
});
I tried to get it like the way i get the fcm token here. But there is a several employees that I would not know what employee will create a new documents or update the documents.
You are using the ID Token from firebase you can not use it to send notification.
You need to create a FCM Token for each device and put it in the database and use that token to send notification.
here is the difference between them :
-Firebase Authentication ID tokens identify a user. This means that if the same user is signed in on two different devices, they have ID tokens that identify the same user.
-FCM tokens (also called Instance ID tokens) identify an application installation. If you have two tokens from two different devices, there is nothing that is shared between those tokens.

Why doesn't firebase admin messaging send notifications to all device token in multicast?

I know there are lot of questions around this question, and I promise I've checked quiet a number of them but non seems to give me an exact answer
I'm using firebase cloud function's admin messaging SDK to send push notifications to an array of device token I put together from my users collection.
The code:
let deviceToken = [<device tokens>];
let payload = {
notification: {
title: main_title,
body: notf_body,
},
data: {
<data object>
},
};
await messaging
.sendToDevice(deviceToken, payload)
.then((response) => {
console.log("Successfully sent message to::", response.successCount);
})
.catch((error) => {
console.log("Error sending message:", error);
});
} else {
console.log("ARTICLE PUBLISHED BUT NOT BROADCASTED");
return;
}
seems to be working fine but the push notification is never sent to all the device token in the array...
Below is a log of the function triggered, where 49 device token are present in the array but only 32 notifications are successful
What could be the reason for this, as some clients have been complaining they aren't getting notification
There are quite some reasons why sending a message to a token might fail. The specific reason for each token that failed is specified in the response.results that you get back.
The most common reasons are that tokens get outdated/expired over time, meaning they won't work anymore. In a well working app, you'll register new tokens for the those same devices, but failure to clean up the old tokens from your database will result in more and more failures over time.
For a good example of how to deal with these errors and clean up outdated tokens, see this code from the example of sending notifications in Cloud Functions:
// Listing all tokens as an array.
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
const response = await admin.messaging().sendToDevice(tokens, payload);
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
functions.logger.error(
'Failure sending notification to',
tokens[index],
error
);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);

credentials used to authenticate does not have permission

I am trying to use firebase cloud messaging to deploy a http push notifications onto an actual device. When so, I am getting the error
"The credential used to authenticate this SDK does not have permission to send messages to the device corresponding to the provided registration token. Make sure the credential and registration token both belong to the same Firebase project."
I have checked the credentials on both the frontEnd and backEnd side and they all match up with my firebase correctly. I have tried to follow plenty of examples on here and I have missed on all of them. My node.js file looks like
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
export GOOGLE_APPLICATION_CREDENTIALS = "/Users/myname/mylocation/myfile.json"
exports.sendPushNotifications = functions.https.onRequest((req, res) => {
res.send("Attempting to send push notification")
console.log("LOGGER --- Trying to send push message..");
var uid = 'randomUIDString'
var fcmToken = 'myToken'
return admin.database().ref('/users/' + uid).once('value', snapshot => {
var user = snapshot.val();
console.log("username is " + user.name);
var payload = {
notification: {
title: 'Push Notification Title',
body: 'Test Notification Message'
}
}
admin.messaging().sendToDevice(fcmToken, payload)
.then(function(response) {
console.log('Succesfully sent message:', response);
console.log(response.results[0].error);
})
.catch(function(error) {
console.log('Error sending message', error);
});
})
})
The users name prints from calling the uid, but I am having trouble accessing the fcmToken.
I have checked my info.plist and the correct project is listed. Same with apple developer and appDelegate. is there something more that I am missing??

How to send push notification to more than 10000 users using firebase in nodejs?

If I have more than 10k user, and I have an array of token, how can I send to all of user ? I tried to chunk array 1000 user each time, push 1000 user to a Topic, and remove the user from a topic ( in a loop). But it is running false. Does anyone meet this case? thank you!
Sample code:
let registrationTokens =[
token1,
token2,...
token10000
]
let promises = [];
for (let i = 0; i < 10; i++) {
promises.push(
admin
.messaging()
.subscribeToTopic(registrationTokens, topic) // subscrible topic
.then(function(response) {
// send message to topic
admin
.messaging()
.send(message)
.then(response => {
// remove user from topic
admin
.messaging()
.unsubscribeFromTopic(registrationTokens, topic);
})
.catch(error => {
console.log('Error sending message:', error);
});
})
.catch(function(error) {
console.log('Error subscribing to topic:', error);
console.log(error);
return res.send(error);
})
);
}
Promise.all(promises);
Topics are meant for a use-case where your user subscribes to receive messages about a certain... topic. What you have here does not seem like a good usage of topics.
Since you already have the device tokens of the instance you want to send the message to:
You can call the API to send a message to a specific device 10.000 times.
Alternatively, you can use the legacy HTTP API to send downstream messaging to 1000 devices at a time, using the registration_ids parameter.
You can send push notification to a list containing up to 500 registration tokens, using 'sendMulticast' on Firebase Admin SDK package check firebase docucumentation for more information.

Resources