Send notification using Cloud Functions for Firebase to specific user - javascript

I'm using Cloud Functions for Firebase to send notifications to the user. I'm able to get the notification but the problem is that everyone is getting the notification, I'm trying to send the notification to a particular user. I'm saving user's device id in Firebase's database and then send that particular person the notification. Here is my code:
To save user's data, which is actually working fine:
DatabaseReference root = FirebaseDatabase.getInstance().getReference();
DatabaseReference groupsRef = root.child("users").child(Settings.Secure
.getString(ctx.getContentResolver(), Settings.Secure.ANDROID_ID));
groupsRef.child("isLogin").setValue(2);
In first activity subscribing to the topic:
FirebaseMessaging.getInstance().subscribeToTopic("android");
And finally javascript code(something I know very little about):
var functions = require('firebase-functions');
var admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/users')
.onWrite(event => {
var eventSnapshot = event.data;
var str = "This is notification"
console.log(str);
var topic = "android";
var payload = {
data: {
isLogin: eventSnapshot.child("975af90b767584c5").child("isLogin").val()
}
};
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);
});
});
Here instead of "975af90b767584c5" which is hardcoded right now, I want to send device id, which I don't know how to do in javascript. Or if there is any other way.
Any help is appreciated, thanks.

First in app, get User FCM token by
String Token = FirebaseInstanceId.getInstance().getToken();
Now, send this token to your server and save it with other user details in Database.
Then when you want to send Notification to specific use, fetch that user's FCM token from Database via user id or something else.If you want to send notification to multiple user then fetch multiple user FCM token from database and put it in arrayList.
Now for main part, call fcm endpoint with your KEY, notification content
and most important: token or token array.
In your case, do not use sendToTopic, use send to: Token/Array
You can google for java script syntax, but this is main logic.For more info:
https://firebase.google.com/docs/cloud-messaging/admin/send-messages

Related

How do I register new vapidKeys outside FCM console UI

Update
As Gerardo pointed out, vapidKey != FCM Token.
I finally got things going, though im unsure if this is the best way to do it.
This was what i needed to generate a NEW token:
try{
if(currentToken) await deleteToken(messaging,currentToken)
if(Notification.permission !== 'granted') await Notification.requestPermission();
currentToken = await getToken(messaging,vapidKey);
$.ajax({
type:"GET",
url:"/fcm/register-token",
data:{token:currentToken},
success:(saved)=>{
if(saved) console.log(saved,' New token registered ',currentToken)
else console.log('Unable to save token!')
}
})
}
catch(e){console.log(e)}
Question
I am testing FCM for push notifications in javascript. I have a single "Web Push Certificate" (vapidKey) generated manually in the FCM console UI. This works for sending a test notification to a single token, but i am unclear on how any additional users would be able to register a new key with FCM.
I must be misunderstanding something fundamental here, and am not even sure what to be searching for.
// TODO: Find out how to generate new registration token as needed
const vapidKey = {vapidKey:"BLgq_6FY0suqAy9llMyYX03o9K9n4FHba1gCnPz6KmG9oh6K8v3Ws4SIG9KTILfN0_8np9PfT5bPqgTDXfCA9dc"};
async function fcm_init(){
try{
// TODO: user interaction for notification permission
await Notification.requestPermission()
const currentToken = await getToken(messaging,vapidKey);
// TODO: Send token to server to save in tbl_fcm
console.log(currentToken);
}
catch(err){
console.log(err);
}
}fcm_init()
In this example, the vapidKey is hard-coded, but how would i go about getting a different key for each user in the app without manually doing so in the FCM Console UI?
This is where i have generated the key to get a token for a user. This is what im trying to avoid doing manually for each user on the system.
The vapidKey is needed for identifying the app (not the user). This is mentioned in the second screenshot "Firebase Cloud Messaging can use Application Identity key pairs to connect with external push services".
With this information, your app can generate an FCM Token (or registration token) in the client:
const currentToken = await getToken(messaging,vapidKey);
You can use these FCM tokens to send messages to a specific device. These FCM Tokens identify the device (user) and they will be different for each one.
Check this example on how you would send a message from the back end to a specific device using it's FCM token (registration token).

Sending FCM messages to web apps through firebase cloud functions

Is it possible to send FCM notifications through Firebase Cloud Functions, when a Firestore data field changes, but for a website, not an app. There is lots of guidance out there for Android and iOS but nothing for simply web apps, outside of sending notifications from the Firebase Console).
I've been trying to find out how to trigger a notification from Cloud Functions but can't find anything useful.
As an example, my database has the following structure:
Collection: users
Documents: documents named using userID
Data Fields: Fields 1 through 5. Field 5 stores the FCM Token. Field 1 stores their status (online, offline, offline pending messages).
I would like to ensure that when Data Field 1 changes (to 'offline pending messages), that the relevant user gets notified (based on the Doc ID).
Edit: adding code below for reference
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/users/{doc}/{Hears}')
.onUpdate(async (change, context) => {
const db = admin.firestore();
db.collection('users').doc(context.params.userId) // get userId
.get()
.then(doc => {
//this is intended to get the FCM token stored in the user's document
const fcmToken = doc.data().usrTkn;
// Notification details
const payload = {
notification: {
title: 'You have a new message.',
body: 'Open your app'
}
};
})
//This should send a notification to the user's device when web app is not in focus.
//FCM is set up in service worker
const response = await admin.messaging().sendToDevice(fcmToken, payload);
console.log(response);
});
Sending messages to a web app is no different from sending it to a native mobile app, so the sending part of guidance you've found is equally applicable. The Firebase documentation even contains an example of sending notifications on a Realtime Database trigger, and doing the same for Firestore would not be much different.
If you're having a specific problem sending messages, I recommend showing what you tried, and what isn't working about it.
Update: your code doesn't work (no matter what sort of device you send the notification to), because you're not handling the asynchronous nature of get() in your code.
The simplest way to fix that is to use await there too, just like you do when calling sendToDevice. So:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/users/{doc}/{Hears}')
.onUpdate(async (change, context) => {
const db = admin.firestore();
const doc = await db.collection('users').doc(context.params.userId).get();
const fcmToken = doc.data().usrTkn;
const payload = {
notification: {
title: 'You have a new message.',
body: 'Open your app'
}
};
const response = await admin.messaging().sendToDevice(fcmToken, payload);
console.log(response);
})
I highly recommend spending some time on learning about asynchronous calls, closures, async/await, and how to debug something like this by adding logging.

How to send a fcm notification using javascript cloud functions in flutter?

I've created an app to test push notifications in a flutter app.
I am able to send a notification from the firebase messaging console and I can receive it as well in the foreground and background.
Once I've done this, I moved to the next step which is to send it automatically using the firebase cloud messaging service and I've used javascript and I've deployed function and it gets executed without any problem.
But the problem is that I can't receive a notification like this:-
but when I open my app since I've configured the firebase messaging inside initState(); I can see the notification and data get printed as well but I can't receive it like the photo above.
What should I do?
try to have a background handler or a top-level method as firebase messaging plugin's read me file says.
About my javascript index.js file:
It sends a notification for all the tokens in the pushTokens collection when a new document added to the posts collection and it does this but the problem is that I've mentioned above.
Index.js:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var notificationMessageData;
exports.fcmTester = functions.firestore.document('posts/{postID}').onCreate((snapshot, context) => {
const notificationMessageData = snapshot.data();
return admin.firestore().collection('pushTokens').get()
.then(snapshot => {
var tokens = [];
if (snapshot.empty) {
console.log('No Devices');
throw new Error('No Devices');
} else {
for (var token of snapshot.docs) {
tokens.push(token.data().tokenID);
}
var payload = {
"notification": {
"title": "from" + notificationMessageData.writer,
"body": "from" + notificationMessageData.name,
"sound": "default"
},
"data": {
"sendername": notificationMessageData.writer,
"message": notificationMessageData.name
}
}
return admin.messaging().sendToDevice(tokens, payload)
}
})
.catch((err) => {
console.log(err);
return null;
})
});
Put your app in background and try sending notification again bcoz flutter firebase messaging plug-in will not create notification if you are using the app while the notification has been sent although you can see its data in but to generate notification you have to do it manually if user is currently using the app.
On the other cases plug-in will create notification like if app is terminated or its in background.

Emit event for particular user if login functionality in application in Socket.io with Node.js

I have used methods socket.on and io.emit, And i got response to all users. But, i want to get response for particular user.
But my application contains login functionality and i followed this post on stackoverflow, and they are saying we need unique userId and socketId in an object for a particular user to emit an event for a particular user.
But i am getting the userId after login, But we want it when user connect to app.
So can anyone please help me with the same?
In your node.js, create a global array 'aryUser', each element contains the socketid and loginid.
node.js onConnect (new connection), add a new element to the array with the socketid and set loginid = empty.
after the user login, emit an event from client to the server, e.g:
socket.emit('userloginok', loginid)
in node.js, define a function:
socket.on('userloginok', loginid)
and in this function, search the aryUser with the socketid and replace the empty loginid inside the array element with the parm loginid.
in node.js, define the function:
socket.on('disconnect')
and in this function, search the aryUser, use aryUser.splice(i,1) to remove the user just disconnected.
that means, aryUser contains all users connected, some of them logined, some of them not logined. And you can use the socketid of the array to send message to particular user, and/or all users.
Example Source Code:
server.js
http://www.zephan.top/server.js
server.html
http://www.zephan.top/server.html.txt
rename server.html.txt to server.html, put server.html and server.js in the same directory, and run:
node server.js
Yes, you definitely need socketId in order to send and receive messages between two specific users.
UserId is required just to keep track of socketId associated with the particular user or you can manage it with some other way as well that's up to you.
As per your question, you have userId of the user and you need socketId of that user! So, in this case, you can pass userId when that particular connects to a socket server from the client side as shown in below snippet,
const socket = io(this.SOCKET_SERVER_BASE_URL, { query: `userId=${userId}` });
And you can read this user on nodejs server like this,
const userId= socket.request._query['userId'],
const socketId= socket.id
Now store this socketId in somewhere, for example, Redis or some sort of caching mechanism again up to you, just make sure fetching and retrieval should be fast.
Now while sending a message just pull the socketId from your cache and emit the message on that socketId by using below code,
io.to(socket.id).emit(`message-response`, {
message: 'hello'
});
I have written a complete blog post on this topic on both Angular and AngularJs, you can refer those as well.
Edit 1:
Part 1 =>
When your user completes the login request, then make the connection to the socket server.
Assuming you are using React Or Angular After a successful login you will redirect your user to home component(page). On the Home component(page) make the socket server connect by passing the userId just like this,
const socket = io(SOCKET_SERVER_BASE_URL, { query: `userId=${userId}` });
P.S. you can get userID from URL or maybe using a cookie that is up to you.
Once you receive this socket connection request on the server, then you can read the userID query and you can get socketId associated with it and store it in cache like this,
io.use( async (socket, next) => {
try {
await addSocketIdInCache({
userId: socket.request._query['userId'],
socketId: socket.id
});
next();
} catch (error) {
// Error
console.error(error);
}
});
Part 2 =>
Now, let's say you have a list of the users on the client side, and you want to send a message to particular users.
socket.emit(`message`, {
message: 'hello',
userId: userId
});
On the server side, fetch the socketId from the cache using UserId. Once you get the socketId from cache send a specific message like this,
io.to(socketId).emit(`message-response`, {
message: 'hello'
});
Hope this helps.

Cloud Firestore: Retrieve dynamic field keys with Javascript

I am sending a notification using a device token with Cloud Firestore and Cloud Function. Now the system will store the device token in which user logs in. Since a user may have multiple device or may logged in using different device, all I want is to send this notification to those device using the stored device token. This is how a users document looks like, I store the tokens as a nested object.
{
name: "Frank Kemerut",
device_tokens: { 23qweq: "LG G6", Os23pk: "Samsung S6", asd231: "Samsung S9" },
age: 12
}
Now I want to iterate and get all key and value then send a notification to those device using the collected tokens. How am I going to perform this? Is this the best approach?
OK here is a cloud function that will send a notification to all user devices when a firestore event triggered. Assuming you have the user id in that trigger from the event object or another way. The function will use that id to get the user document from the database depending on how you stored it then get the notification token and send it to all of his devices in the device_tokens map
export const sendEventNotification = functions.firestore.document('events/${eventId}')
.onCreate((data, context) => {
const userId = "someId"
//Get the user document to get the notification tokens.
return firestore.doc(`users/${userId}`).get().then((user) => {
//dummy notification payload
const payload = {
data: {
event: JSON.stringify(data.data())
}
}
//The device tokens mapped to device name.
const device_tokens = user.data().device_tokens
//Array of notification tokens that we will send a notification to.
const promises = []
Object.keys(device_tokens).forEach(token => {
promises.push(admin.messaging().sendToDevice(token, payload))
})
return Promise.all(promises)
}).catch((error) => {
console.log(`Failed to send user notification. Error: ${error}`)
return null
})
})

Categories

Resources