6

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).

  1. Message
  2. Message_From
  3. Time
  4. Type
  5. token_To

Firebase database structure

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

Firebase cloud 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.

4 Answers 4

2

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.

Firebase message node

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);
            });
    });
});
Sign up to request clarification or add additional context in comments.

4 Comments

Is it working on your side? And doing same thing what I want to achieve
if there is 100 users on app then this will fetch complete message node. To avoid this i created 1 room for 2 users. this will fetch only particular room messages
This works as each write to the message node will trigger the function and notification sent to /users/${receiverUid}/pushToken.
You are getting fixed node (message) with push key. Your code might replace mine but my issue is how to get only newly added data like i mentioned above which can be inside any room and with any key only. If you have any ideas regarding my issue please help me. As i am not that good in Javascript
1
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();

9 Comments

How can get variable chatroom cuz it will be unique . declaring as fixed chatroom will not work for all chat rooms
did you try first one line? I think I may solve your problem
I tried but it's not working. I think I need to hit on right chat room which belong to chat users
My bad, actually .val is not needed in above line, I have updated answer, please check it
It will still not fetch newly entered node from database based on room name cuz we can't declare room name manually. We need to fetch wherever node make new entry into database
|
1

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.

2 Comments

Thanks. I will check this soon and revert back to you.
I tried alot with this and random other solution over SO and git hub bt still no solution
0
 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.

2 Comments

Making notification is not hard but getting data from database is bit complicated from particular room which ever get triggered
Thanks but i already read firebase documentation but my point is i can't fetch sub nodes whenever new data insert into room. Please read my question again. if any confusion let me know

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.