Why am I Getting Duplicate Notifications? - node.js

I'm trying to use Firebase functions to automatically send push notifications to iOS devices. More specifically, right now when a user creates a post, it will contain an ID, which is actually the user's FCM token, from which the push notification will be sent to that user.
Why does it happen that, upon creating a post, my iOS device doesn't necessarily receive a single push notification, but many? Why is the getResult function being triggered potentially more than once for a given post?
Please see my code below. Thanks!
const functions = require('firebase-functions');
var admin = require('firebase-admin');
var firebase = require('firebase');
admin.initializeApp(functions.config().firebase);
var config = {
apiKey: "XXXXX",
authDomain: "YYYYY",
databaseURL: "ZZZZZ"
}
firebase.initializeApp(config);
firebase.database().ref('posts').on('child_added', function(snapshot1) {
snapshot1.forEach((child) => {
if (child.key == 'id') {
var token = child.val();
admin.database().ref('users').on('value', function(snapshot2) {
snapshot2.forEach(function(user) {
if (user.val().fcmToken == token) {
var newBadgeCount = user.val().badge + 1;
const payload = {
notification: {
title: 'Hello, World!',
body: 'Test Message!',
badge: '' + newBadgeCount,
sound: 'default'
}
};
function getResult(token) {
return result = admin.database().ref('fcmToken/' + token).once('value').then(allToken => {
if (allToken.val()) {
const token = Object.keys(allToken.val());
return admin.messaging().sendToDevice(token, payload).then(function (response) {
console.log("Successfully sent message: ", response.results[0].error);
}).catch(function (error) {
console.log("Error sending message: ", error);
});
};
});
}
function updateBadgeCount(badgeCount, userID) {
firebase.database().ref('users/' + userID + '/badge').set(badgeCount);
}
Promise.all([getResult(token)]).then(function(snapshots) {
updateBadgeCount(newBadgeCount, user.key);
});
};
});
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
};
});
});

Firebase triggers for each record of all records with 'child_added' option once and will re-trigger after each new child added. So, If you like to trigger a function on new write options, you need use other ways.
exports.sendNotifycation = functions.database.ref('/posts/{postId}')
.onWrite(event => {
Your new re-designed codes. (in case I will complete remain)
})

Related

Firebase Functions onCreate method not working on Firestore after getting the userID

I am trying to get the userID of the user and then running a onCreate function on the firestore in Firebase Functions for background notifications, but the onCreate function doesn't run. The function shows its executed and finished.
import { https, firestore, logger } from "firebase-functions";
import { initializeApp, messaging, firestore as _firestore } from "firebase-admin";
initializeApp();
const fcm = messaging();
const db = _firestore();
export const friendRequest = https.onCall((data, context) => {
const userID = context.auth.uid;
try {
db.collection("users")
.doc(userID)
.collection("tokens")
.doc("token")
.get()
.then((value) => {
const token = value.data().token;
firestore
.document(`users/${userID}/recievedRequests/{request}`)
.onCreate((snapshot) => {
const senderName = snapshot.data().name;
logger.log(
"New Notification to " + token + " ,by :" + senderName
);
const payload = {
notification: {
title: "New Friend Request",
body: `Friend Request from ${senderName}`,
},
};
fcm.sendToDevice(token, payload).then((response) => {
logger.log("Response" + response.successCount);
});
});
});
} catch (error) {
logger.log("Error : " + error);
}
});
This is the Friend Request function I want to send notification to user when he receives a notification. My firebase log shows
You have the onCreate() within another function so that's not deployed to Cloud Functions at first place unlike the friendRequest. It seems you are trying to notify a user who has received the request. You can try the following function:
export const notifyUser = firestore
.document(`users/{userId}/recievedRequests/{request}`)
.onCreate(async (snapshot, context) => {
const userId = context.params.userId;
const senderName = snapshot.data().name;
logger.log("New Notification to " + userId + " ,by :" + senderName);
const payload = {
notification: {
title: "New Friend Request",
body: `Friend Request from ${senderName}`,
},
};
// Get Token of the User
const tokenSnap = await db
.collection("users")
.doc(userId)
.collection("tokens")
.doc("token")
.get();
const token = tokenSnap.data()!.token;
return fcm.sendToDevice(token, payload).then((response) => {
logger.log("Response" + response.successCount);
return null;
});
});
To send the request at first place, you can simply add the a document in users/RECEIVER_ID/friendRequests/ sub-collection that'll trigger the above function that'll fetch receivers FCM token and send the notification. There's no need of the onCall() function.

How to retrieve FCM Token and userId from database then send Push Notification with Node.js and Cloud Functions

I'm very new to Node.js and I'm having trouble figuring out how to send a push notification.
This is my code so far.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.sendPushOrderOnTheWay = functions.https.onRequest((req, res) => {
const userId = req.body.id;
const registrationToken = ''
db.collection('users').doc(userId).get().then(function (doc) {
registrationToken = doc.data().registrationToken;
});
const payload = {
token: registrationToken,
notification: {
title: 'Din levering er på vej!',
body: 'Vi er der indenfor 30 min.'
},
data: {
body: message,
}
};
admin.messaging().send(payload).then((response) => {
console.log('wuhuu:', response);
return { success: true };
}).catch((error) => {
return { error: error.code };
});
});
I know something is missing to make it work, but I can't seem to figure out what it is.
My problem:
I don't know how to properly get the userId and FCM token.
Or if there are any other mistakes in my code.
This is my firestore structure:
Picture here

Error sending notification through firebase function flutter

I am trying to send push notification to the user whenever the new event is added into the firestore of the firebase.
Whenever I do so it returns me the error saying Error sending notification
node.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var newData;
exports.messageTrigger = functions.firestore.document('Messages/{messageId}').onCreate(async(snapshot, context)=> {
if(snapshot.empty){
console.log('No Devices');
return;
}
var tokens = ['mobile-token'];
newData = snapshot.data();
var payload = {
notification : {title: 'Push Title', body: 'Push body', sound: 'default'},
data: {click_action: 'FLUTTER_NOTIFICATION_CLICK', message: newData.message},
};
try{
const response = await admin.messaging.sendToDevice(tokens, payload);
console.log('Notification sent successfully');
} catch (err) {
console.log('Error sending notifications');
}
});
I am passing the mobile token in place of mobile-token
SOLUTION
Changing admin.messaging.sendToDevice() to
const response = await admin.messaging().sendToDevice(tokens, payload)
As admin.messaging.sendToDevice() is not a method

Firebase functions promises not firing correctly

I'm having a hard time understanding promises in Firebase functions. I have a function that listens for new files in a storage bucket and then emails the user as well as sending them a Discord message. I'm getting inconsistent results and I'm pretty sure its to do with promises and callbacks being setup incorrectly.
exports.sendSetup = functions.storage.bucket('*the-bucket-id*').object().onFinalize((object) => {
// Get a URL for the new config file
console.log('New conf file: ' + object.name);
const { Storage } = require('#google-cloud/storage');
const storage = new Storage({
projectId: '*the-project-id*',
keyFilename: 'googleServiceAccountKey.json'
});
var bucket = storage.bucket('*the-bucket-name*');
const file = bucket.file(object.name);
console.log('Generating download url for file ' + object.name);
return file.getSignedUrl({
promptSaveAs: '*the-file-name*',
action: 'read',
expires: '03-09-2491'
}).then(signedUrls => {
var split = object.name.split("/");
var env = split[0];
var customer_id = split[1];
getCustomerDetails(customer_id, signedUrls[0], env);
});
});
function getCustomerDetails(customer_id, fileUrl, env) {
console.log('Retrieving customer details for customer id ' + customer_id + ' from Stripe');
var stripe = stripeLive;
if (env == 'test') {
stripe = stripeTest;
}
stripe.customers.retrieve(
customer_id,
function (err, customer) {
if (err == null) {
sendMail(fileUrl, customer.email, customer_id, customer.metadata.name);
console.log('discordId= ' + customer.metadata.discordId);
if (customer.metadata.discordId != 'undefined') {
sendDiscord(fileUrl, customer.metadata.discordId, customer.metadata.discordName);
}
console.log('Finished');
} else {
console.log(err);
}
}
);
}
function sendDiscord(fileUrl, discordId, discordName) {
console.log('Attempting to send a discord message to Discord id ' + discordId);
const Discord = require('discord.js');
const client = new Discord.Client();
client.login('*discord-api-key*');
client.once('ready', () => {
console.log('Discord client ready');
client.fetchUser(discordId)
.then((User) => {
console.log('Got Discord user object. Attempting to send message');
return User.send({
embed: {
color: 3447003,
fields: [
{
name: 'Hey ' + discordName + '!',
value: 'Below are the instructions to get you up and running'
},
{
name: '**Step 1**',
value: 'some instructions'
}
]
}
});
})
.catch((err) => {
console.log(err);
})
});
}
function sendMail(fileUrl, customer_email, customer_id, customer_name) {
console.log('customer_name in sendMail function = ' + customer_name);
var firstName = customer_name.substring(0, customer_name.indexOf(' '));
console.log(firstName);
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(*sendGridApiKey*);
sgMail.setSubstitutionWrappers('{{', '}}'); // Configure the substitution tag wrappers globally
const msg = {
to: customer_email,
subject: 'Welcome!',
from: {
email: 'noreply#example.com.au',
name: 'me'
},
text: 'Let\'s get you setup...',
html: '<p></p>',
templateId: '*template-id*',
substitutions: {
first_name: firstName,
file_url: fileUrl
},
};
console.log('Sending email to ' + customer_email + ' customer id:' + customer_id);
sgMail.send(msg);
}
I've read a heap of articles about promises and callbacks but can't seem to wrap my head around it. The "sendSetup" function actually returns OK but appears to stop right at the start of the getCustomerDetails function. Appreciate any assistance! I'm a bit lost!

How to send push notifications to specific users with Cloud Functions for Firebase

I am using Firebase as my back end for my Android app and am very new to using Cloud Functions for Firebase and I was wondering how I would send specific users push notification when an event occurs.
For example how would I send the user with uId in the below code a push notification when a write occurs at adminName node on the database:
exports.sendNotification = functions.database.ref('/users/{uId}/groups/{adminName}')
.onWrite(event => {
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = event.data;
var str1 = "Author is ";
var str = str1.concat(eventSnapshot.child("author").val());
console.log(str);
var topic = "android";
var payload = {
data: {
title: eventSnapshot.child("title").val(),
author: eventSnapshot.child("author").val()
}
};
// Send a message to devices subscribed to the provided topic.
return admin.messaging().sendToTopic(topic, payload)
.then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
Make the below changes. it works for me
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var newData;
exports.myTrigger = functions.firestore.document('TestCollection/{id}').onWrite(async (snapshot, context) => {
//
if (snapshot.empty) {
console.log('No Devices');
return;
}
newData = snapshot.data();
const deviceIdTokens = await admin
.firestore()
.collection('DeviceIDTokens')
.get();
var tokens = [];
for (var token of deviceIdTokens.docs) {
tokens.push(token.data().device_token);
}
var payload = {
notification: {
title: 'Push Title',
body: 'Push Body',
sound: 'default',
},
data: {
push_key: 'Push Key Value',
key1: newData.data,
},
};
try {
const response = await admin.messaging().sendToDevice(tokens, payload);
console.log('Notification sent successfully');
} catch (err) {
console.log(err);
}
});

Resources