Sending notifications when button clicked errors - javascript

I'm trying to notify my users when someone clicks the going button but I keep getting a couple of errors in my firebase functions logs.
Uncaught Exception &
Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array
exports.observeGoing = functions.database.ref('/going/{postId}/{uid}').onCreate((snapshot,context) => {
var postId = context.params.postId;
var uid = context.params.uid;
console.log('User: ' + uid + ' is going to your activity');
return admin.database().ref('/users/' + uid).once('value', snapshot => {
var creatorOfPost = snapshot.val();
admin.database().ref('/users/' + uid).once('value', snapshot => {
var userGoing = snapshot.val();
var payload = {
notification: {
body: userGoing.usernames + " is going",
sound: "default"
}
}
admin.messaging().sendToDevice(creatorOfPost.fcmToken, payload)
.then((response) => {
console.log('Successfully send message:', response);
return response
})
.catch((error) =>{
console.log('Error sending message:', error);
});
})
})
})

You reveive that error because creatorOfPost.fcmToken can be null
According to the firebase document, the first parameter of admin.messaging.Messaging.sendToDevice() cannot be null.
But in your code, there are 2 possibility to violate this precondition.
creatorOfPost.fcmToken can be null
just do null check before calling admin.messaging.Messaging.sendToDevice()
creatorOfPost can be null
firebase.database.Reference.once() returns DataSnapshot, but it doesnt mean there always be corresponding document.
so, var creatorOfPost = snapshot.val(); can be null.
maybe you can check like this:
return admin.database().ref('/users/' + uid).once('value', snapshot => {
if (!snapshot.exists()) {
return; // or do what you would like
}
var creatorOfPost = snapshot.val();
// ...
// ...
})

Related

How do you trigger cloud functions when someone follows someone else using Firebase? I'm using Atom and Xcode

I'm using Firebase and I want to trigger a notification when 'person A' follows 'person B'.
This is my code:
exports.observeFollowing = functions.database.ref('/added/{followerID}/{followingID}').onCreate((snapshot, context) => {
var followerID = context.params.followerID;
var followingID = context.params.followingID;
console.log('User: ' + followerID + ' is following: ' + followingID);
//trying to figure out fcmtoken to send a notification
return admin.database().ref('/users/' + followingID).once('value', snapshot => {
var userWeAreFollowing = snapshot.val();
return admin.database().ref('/users/' + followerID).once('value', snapshot => {
var userDoingTheFollowing = snapshot.val();
var payload = {
notification: {
title: "Someone added you as a friend",
body: userDoingTheFollowing.username + ' is now following you'
}
}
return admin.messaging().sendToDevice(userWeAreFollowing.fcmToken, payload)
.then(response => {
console.log("Successfully sent message:", response);
return response
}).catch(function(error) {
console.log("Error sending message:", error);
});
})
})
});
The console prints the ('User: ' + followerID + ' is following: ' + followingID) part but doesn't show the notification. I've tested cloud notifications before and they worked but this doesn't work for some reason. In the logs, it says:
Successfully sent message: { results: [ { error:
[FirebaseMessagingError] } ]," "failureCount: 1," "successCount: 0,"
So I know that everything up until the console.log('User: ' + followerID + ' is following: ' + followingID); works. But I'm not sure if the notification function is even being called. Am I missing a semicolon or something else? I really can't figure it out. Also, what does failureCount mean? Is it talking about the notifications function?
As explained in the Cloud Functions doc, you need to manage the asynchronous Firebase operations by using promises. You are using the callback version of the once() method: you need to use the promise version, as follows:
exports.observeFollowing = functions.database.ref('/added/{followerID}/{followingID}').onCreate((snapshot, context) => {
const followerID = context.params.followerID;
const followingID = context.params.followingID;
let userWeAreFollowing;
console.log('User: ' + followerID + ' is following: ' + followingID);
return admin.database().ref('/users/' + followingID).once('value')
.then(snapshot => {
userWeAreFollowing = snapshot.val();
return admin.database().ref('/users/' + followerID).once('value')
})
.then(snapshot => {
const userDoingTheFollowing = snapshot.val();
const payload = {
notification: {
title: "Someone added you as a friend",
body: userDoingTheFollowing.username + ' is now following you'
}
}
return admin.messaging().sendToDevice(userWeAreFollowing.fcmToken, payload)
}).catch(function (error) {
console.log("Error sending message:", error);
return null;
});
});
If you want to log a message in the console upon success do as follows:
// ...
.then(snapshot => {
const userDoingTheFollowing = snapshot.val();
const payload = {
notification: {
title: "Someone added you as a friend",
body: userDoingTheFollowing.username + ' is now following you'
}
}
return admin.messaging().sendToDevice(userWeAreFollowing.fcmToken, payload)
})
.then(response => {
console.log("Successfully sent message:", response);
return null;
}).catch(function (error) {
console.log("Error sending message:", error);
return null;
});

Javascript if conditional variable problem

I have two receiver type. 0 for guests 1 for users. I'm trying to push notification from firebase cloud functions. I can send notification user to guest succesfully. But can not send notification guest to user. Is there any problem with my if condition?
var usersRef = null;
if (receiverType === 0) {
usersRef = admin.firestore().collection('Guests').doc(receiverId);
} else {
usersRef = admin.firestore().collection('Users').doc(receiverId);
}
I want to change var usersRef variable based on receiverType. I managed to change it to Guest path but on the else part its path must change again to Users. But not changing. There is a problem in my if else statement.
var getDoc = usersRef.get()
.then(doc => {
if (!doc.exists) {
return null;
} else {
const token = doc.data().token;
const payload = {
notification: {
title: "New Message",
body: chatMessage,
}
};
admin.messaging().sendToDevice(token, payload)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
return null;
})
.catch((error) => {
console.log('Error sending message:', error);
return null;
});
}
return null;
})
.catch(err => {
console.log('Error getting document', err);
return null;
});
return null;
});

Firebase cloud messaging function returning undefined

I'm simply sending a notification when someone new has followed you, but when I try to return the users username in the notification it just says "undefined has decided to follow you" instead of the username.
exports.observeFollowing = functions.database.ref('/following/{uid}/{followingId}').onCreate((snapshot,context) => {
var uid = context.params.uid;
var followingId = context.params.followingId;
console.log('User: ' + uid + 'is following: ' + followingId);
return admin.database().ref('/users/' + followingId).once('value', snapshot => {
var userWeAreFollowing = snapshot.val();
return admin.database().ref('/users/' + uid).once('value', snapshot => {
var userDoingTheFollowing = snapshot.val();
var payload = {
notification: {
title: "Someone new has followed you",
body: userWeAreFollowing.username + " has decided to follow you...",
sound: 'default'
}
}
admin.messaging().sendToDevice(userWeAreFollowing.fcmToken, payload)
.then((response) => {
console.log('Successfully sent message:', response);
return response
})
.catch((error) => {
console.log('Error sending message:', error);
});
})
})
Instead of
body: userWeAreFollowing.username + " has decided to follow you...",
should't be the following user name
body: userDoingTheFollowing.usernames + " has decided to follow you...",
and notice in your realtime database you saved the username as usernameS, that might be the problem

How to access different node on, onUpdate event in Firebase?

I have a firebase database which has two firebase nodes in the same level. One named NotificaationKey and one named requests. I have an onUpdate cloud function on requests/status field. When the node gets updated I get a field from requests node and use it to retrieve data from the NotificationKey node, but when I access the Notification node I get the following response instead of the data I want. It is printing following log.
Following is the code how I access the database. on onUpdate function.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.notifyUser = functions.database.ref('/requests/{requestId}/status')
.onUpdate(event => {
const adminRefVar = event.data.adminRef.root;
return event.data.adminRef.parent.child('requestorId').once('value').then((snapshot)=>{
var uuid = snapshot.val();
console.log("Data : "+uuid );
return adminRefVar.child('/NotificationKey').orderByChild('uuid').equalTo(uuid)
.once('value').then((snapshot2)=>{
console.log("data: " + snapshot2);
for(var i in snapshot2)
{
console.log("data in snap: "+snapshot2[i]);
}
return snapshot2;
});
});
});
I can't figure out what I'm doing wrong and I'm new to node.js and firebase functions. Thank you in advance.
I was able to access a different node by below code. Hope it would help someone.
exports.notifyUser = functions.database.ref('/requests/{requestId}/status').onUpdate(event => {
const adminRefVar = event.data.ref.firestore;
return event.data.adminRef.parent.child('requestorId').once('value').then((snapshot)=>{
var uuid = snapshot.val();
return admin.database().ref('NotificationKey').orderByChild('uuid').equalTo(uuid).once('value').then(snapshot => {
snapshot.forEach(function (childSnapshot) {
var value = childSnapshot.val();
console.log("notification key : " + value.notificationKey);
const payload = {
notification:{
title:'CLUTS request status has been updated',
body: 'Your ride share request has been '+event.data.val(),
badge: '1',
sound: 'default'
}
};
var userToken;
userToken = value.notificationKey;
return admin.messaging().sendToDeviceGroup(userToken, payload)
.then(function(response) {
console.log("Successfully sent message:", response);
return 0;
})
.catch(function(error) {
console.log("Error sending message:", error);
return 0;
});
});
return snapshot;
});
});
});

Firebase function to fetch data from Firebase DB to make Push notification

I have chat app with firebase database and Firebase cloud messaging. I can send firebase notification via console but in real scenario it should be automatic. To make automatic notification,My friend wrote Index.js (Added in cloud functions) file for me but its not sending notifications.
As per our logic function should trigger whenever there is any new entries (in any node or in any room) and fetch these values by firebase function and make post request to FCM server to make notification to receiver device (get value of receiver device from token_To).
Message
Message_From
Time
Type
token_To
Index.js
var functions = require('firebase-functions');
var admin = require('firebase-admin');
var serviceAccount = require('./demofcm-78aad-firebase-adminsdk-4v1ot-2764e7b580.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://demofcm-78aad.firebaseio.com/"
})
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
exports.setUserNode = functions.auth.user().onCreate(event => {
// ...
});
exports.notifyMsg = functions.database.ref('/{chatroom}/{mid}/')
.onWrite(event => {
if (!event.data.val()) {
return console.log('Message Deleted');
}
const getDeviceTokensPromise = admin.database().ref('/{chatroom}/{mid}/token_to').once('value');
return Promise.all([getDeviceTokensPromise]).then(results => {
const tokensSnapshot = results[0];
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
const payload = {
notification: {
title: 'You have a new Message!',
body: event.data.val().Message
}
};
const tokens = Object.keys(tokensSnapshot.val());
return admin.messaging().sendToDevice(tokens, payload).then(response => {
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
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);
});
});
});
Firebase function Log
How can i fetch above mentioned values of any newly added node in same room(9810012321-9810012347) or any other room(9810012321-9810012325) from database and send it to FCM to make notification
Thanks in Advance.
What i did is created a Message node and I believe doing this by users key. ie, having the receiver(toId) and sender (fromId) key to send the notification.
Hope it helps.
exports.sendMessageNotification = functions.database.ref('/messages/{pushId}')
.onWrite(event => {
let message = event.data.current.val();
console.log('Fetched message', event.data.current.val());
let senderUid = message.fromId;
let receiverUid = message.toId;
let promises = [];
console.log('message fromId', receiverUid);
console.log('catch me', admin.database().ref(`/users/${receiverUid}`).once('value'));
if (senderUid == receiverUid) {
//if sender is receiver, don't send notification
//promises.push(event.data.current.ref.remove());
return Promise.all(promises);
}
let messageStats = message.messageStatus;
console.log('message Status', messageStats);
if (messageStats == "read") {
return Promise.all(promises);
}
let getInstanceIdPromise = admin.database().ref(`/users/${receiverUid}/pushToken`).once('value');
let getSenderUidPromise = admin.auth().getUser(senderUid);
return Promise.all([getInstanceIdPromise, getSenderUidPromise]).then(results => {
let instanceId = results[0].val();
let sender = results[1];
console.log('notifying ' + receiverUid + ' about ' + message.text + ' from ' + senderUid);
console.log('Sender ', sender);
var badgeCount = 1;
let payload = {
notification: {
uid: sender.uid,
title: 'New message from' + ' ' + sender.displayName,
body: message.text,
sound: 'default',
badge: badgeCount.toString()
},
'data': {
'notificationType': "messaging",
'uid': sender.uid
}
};
badgeCount++;
admin.messaging().sendToDevice(instanceId, payload)
.then(function (response) {
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
});
const getDeviceTokensPromise = event.data.child('token_To');
should be there instated of getting data from database reference.
or
with fixed path without wildcard like below
const getDeviceTokensPromise = admin.database().ref('/${chatroom}/${mid}/token_to').once('value');
where chatroom and mid is variable which contain value
Second thing:
if (!tokensSnapshot.exists()) {
should in replace of
if (!tokensSnapshot.hasChildren()) {
third thing:
I am not sure about push notification tokenId but
is it required to do?
const tokens = Object.keys(tokensSnapshot.val());
may be we can use directly like below to send push notification
const tokens = tokensSnapshot.val();
You could store all device tokens in a node called tokens like in my example. Tokens could be an array if you would like one user to be able to get notifications on multiple devices. Anyway, store them by their UID.
This works for both Andriod and iOS.
Here is my code:
function loadUsers() {
let dbRef = admin.database().ref('/tokens/' + recieveId);
console.log(recieveId)
let defer = new Promise((resolve, reject) => {
dbRef.once('value', (snap) => {
let data = snap.val();
console.log("token: " + data.token)
//userToken = data.token
resolve(data.token);
}, (err) => {
reject(err);
});
});
return defer;
}
Next we create the notification. I created a lastMessage node to capture just the last message sent in the chat. It is just updated every time a new message is sent in a chat between two users. Makes it easy to get the value. Also makes it easy to show the message on the Conversations screen where there is a list of users who are in a conversation with the current user.
exports.newMessagePush =
functions.database.ref('/lastMessages/{rcId}/{sendId}').onWrite(event => {
if (!event.data.exists()) {
console.log("deleted message")
return;
}
recieveId = event.params.rcId
//let path = event.data.adminRef.toString();
// let recieveId = path.slice(53, 81);
return loadUsers().then(user => {
console.log("Event " + event.data.child("text").val());
let payload = {
notification: {
title: event.data.child("name").val(),
body: event.data.child("text").val(),
sound: 'default',
priority: "10",
}
};
return admin.messaging().sendToDevice(user , payload);
});
});
To implement this logic on your current data structure, just change this line:
let dbRef = admin.database().ref('/tokens/' + recieveId);
and this line:
exports.newMessagePush =
functions.database.ref('/lastMessages/{rcId}/{sendId}').onWrite(event
=> {
to your token location:
let dbRef =
admin.database().ref('/${chatroom}/${mid}/token_to');
and your conversation location:
exports.notifyMsg = functions.database.ref('/{chatroom}/{mid}/')
.onWrite(event => {
Then just change the notification payload be the message you want to display and throw in your error handling on the end of the sendToDevice function, as you did in your code.
Hopefully you figured all this out already but if not maybe this will help you or others trying to use Cloud Functions for notifications.
let payload = {
notification: {
uid: sender.uid,
title: 'New message from' + ' ' + sender.displayName,
body: message.text,
sound: 'default',
badge: badgeCount.toString()
},
'data': {
'notificationType': "messaging",
'uid': sender.uid
}
};
There are two types of FCMs.
1) Data
2) Notification
For detailed overview : FCM Reference
You have to fix your payload for both FCMS. And for Data FCM you have to extract Data in your FCM Service (Client) and generate a push notification according to your need.

Categories

Resources