Send Notification to device tokens - node.js

I'm using Cloud Functions with Cloud Messaging and I want to send a notification to all devices which have a specific userRole (see userRoleList).
Unfortunately, I have no idea how to do that.
For example, I just want to push the deviceTokens with userRole "Aktive" to the deviceTokens.
And here is my code for Cloud Functions so far:
exports.sendNotificationAusschuss = functions.firestore.document('news/{newsId}').onCreate(async snapshot => {
const news = snapshot.data();
console.log('Message received');
//var deviceTokens = ??
const payload = {
notification:{
title: 'Message received',
body: `${news.newsText}`,
sound: "default"
}
};
return admin.messaging().sendToDevice(deviceTokens, payload);
});
Thank you very much

You can query Firestore to retrieve the users with a given role.
const snap = await admin.firestore().collection('users')
.where('userRoleList', 'array-contains', 'Aktive')
.get();
const tokens = [];
snap.docs.forEach((doc) => {
tokens.push(doc.data().deviceToken);
});
Then split tokens into batches of 500, and:
await admin.messaging().sendMulticast({
tokens,
});

Related

Cloud functions for Firebase FCM notifications to multiple users

I am using nodeJS with firebase for my flutter/firebase mobile app
I would like to send notifications to all users that have a certain query met. ie all users who have radiology as their specialty. So that they will be notified when a new article is added to the database
However I am unsure why my code (below) doesn't work to get notification tokens for all users with this query.
My database structure is users/notificationTokens/Ids of all tokens for that user stored in field 'token'
exports.sendToDevice5 = functions.firestore
.document('Articles/{paper}')
.onCreate(async (snapshot, context) => {
const paper = context.params.paper;
const item = snapshot.data();
if (item.subspecialty == "RadMSK" || item.subspecialty == "RadMS") {
const tokens = await admin.firestore().collection('users').where("specialty", "==", "RADIOLOGY").get().then(
snapshot.forEach((doc) => {
const docs = admin.firestore().collection('users').doc(doc.id).collection('notificationTokens').get();
return docs.data().token;
}));
const payload = {
notification: {
title: `${item.title}!`,
body: `New Journal`,
sound: "default",
},
data: {click_action: 'FLUTTER_NOTIFICATION_CLICK'},
};
return admin.messaging().sendToDevice(tokens, payload);
}
});

How can I get the value of children in Firebase database using Javascript?

How do you get the value of a specific key-value pair in firebase using javascript? I am creating a function for firebase cloud messaging. My function looks like this:
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/notifications/{receiver_user_id}/{notification_key}').onWrite((event, context)=>{
const receiver_user_id = context.params.receiver_user_id;
const notification_key = context.params.notification_key;
console.log('We have a notification to send to : ', receiver_user_id);
// Grab the current value of what was written to the Realtime Database.
const snapshot = event.after.val();
console.log('Uppercasing', context.params.notification_key, snapshot);
console.log('original value : ', snapshot);
if(!event.after.val()){
console.log('A notification has been deleted: ', notification_key);
return null;
}
const sender_fullname = admin.database().ref(`/notifications/${receiver_user_id}/{notification_key}/notifying_user_fullname`).once('value').toString();
console.log('full name value : ', sender_fullname);
const DeviceToken = admin.database().ref(`/tokens/${receiver_user_id}/device_token`).once('value');
return DeviceToken.then(result=>{
const token_id = result.val();
console.log('token id value : ', token_id);
const payload = {
notification: {
title: sender_fullname.toString(),
body: "You have a new message!",
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(response=>{
console.log('Message has been sent');
});
});
});
Right now sender_fullname produces [object Promise] in the console log and the notification that is sent. I am uncertain how to get the exact value. An example entry in my realtime database looks like this:
original value : { date_created: '02-21-2020T17:50:32',
my_id: '0ntpUZDGJnUExiaJpR4OdHSNPkL2',
notification_key: '-M0dwVL3w1rKyPYbzUtL',
notification_type: 'liked',
notifying_user: 'OiBmjJ7yAucbKhKNSHtYHsawwhF2',
notifying_user_fullname: 'Captain Proton',
post_key: '-LzSJrOq9Y7hGgoECHRK',
read: 'false' }
Is there any way to get the exact value of say, "notifying_user_fullname"? Any help would be appreciated.
To get the value of sender_fullname, you have to do exactly the way you do for DeviceToken!
The once() method returns a promise which resolves with a DataSnapshot, so you need to use the then() method in order to get the DataSnapshot and then, use the val() method.
So the following should do the trick (untested):
exports.sendNotification = functions.database.ref('/notifications/{receiver_user_id}/{notification_key}')
.onWrite((event, context) => {
const receiver_user_id = context.params.receiver_user_id;
const notification_key = context.params.notification_key;
console.log('We have a notification to send to : ', receiver_user_id);
// Grab the current value of what was written to the Realtime Database.
const snapshot = event.after.val();
console.log('Uppercasing', context.params.notification_key, snapshot);
console.log('original value : ', snapshot);
if (!event.after.val()) {
console.log('A notification has been deleted: ', notification_key);
return null;
}
let sender_fullname;
return admin.database().ref(`/notifications/${receiver_user_id}/${notification_key}/notifying_user_fullname`).once('value')
.then(dataSnapshot => {
sender_fullname = dataSnapshot.val();
return admin.database().ref(`/tokens/${receiver_user_id}/device_token`).once('value');
})
.then(dataSnapshot => {
const token_id = dataSnapshot.val();
console.log('token id value : ', token_id);
const payload = {
notification: {
title: sender_fullname,
body: "You have a new message!",
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload)
})
.then(() => {
console.log('Message has been sent');
return null; // <-- Note the return null here, to indicate to the Cloud Functions platform that the CF is completed
})
.catch(error => {
console.log(error);
return null;
})
});
Note how we chain the different promises returned by the asynchronous methods, in order to return, in the Cloud Function, a Promise, which will indicate to the platform that the Cloud Function work is complete.
I would suggest you watch the 3 videos about "JavaScript Promises" from the Firebase video series which explains the importance of this point.

Bot Framework v4 Node.js Location

I'm in the process of designing a chat bot and trying to find some Node.js sample code and/or documentation on how to implement the Azure Maps service as part of Bot Framework V4. There are many examples of how this is accomplished in V3, but there seems to be no examples of a V4 solution for Node.js. I'm looking to create a step in my botbuilder-dialog flow that would launch a simple "where do we ship it too" location dialog that would guide the user through the dialog and store the address results as part of that users profile. Any help or advice on this would be appreciated.
Yes, this is doable. I created a class (probably overkill, but oh well) in which I make my API call, with my supplied parameters, to get the map. I decided to use Azure Maps (vs Bing Maps) only because I was curious in how it differed. There isn't any reason you couldn't do this with Bing Maps, as well.
In the bot, I am using a component dialog because of how I have the rest of my bot designed. When the dialog ends, it will fall off the stack and return to the parent dialog.
In my scenario, the bot presents the user with a couple choices. "Send me a map" generates a map and sends it in an activity to the client/user. Anything else sends the user onward ending the dialog.
You will need to decide how you are getting the user's location. I developed this with Web Chat in mind, so I am getting the geolocation from the browser and returning it to the bot to be used when getMap() is called.
const { ActivityTypes, InputHints } = require('botbuilder');
const fetch = require('node-fetch');
class MapHelper {
async getMap(context, latitude, longitude) {
var requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow'
};
const result = await fetch(`https://atlas.microsoft.com/map/static/png?subscription-key=${ process.env.AZURE_MAPS_KEY }&api-version=1.0&layer=basic&zoom=13&center=${ longitude },${ latitude }&language=en-US&pins=default|al.67|la12 3|lc000000||'You!'${ longitude } ${ latitude }&format=png`, requestOptions)
.then(response => response.arrayBuffer())
.then(async result => {
const bufferedData = Buffer.from(result, 'binary');
const base64 = bufferedData.toString('base64');
const reply = { type: ActivityTypes.Message };
const attachment = {
contentType: 'image/png',
contentUrl: `data:image/png;base64,${ base64 }`
};
reply.attachments = [attachment];
await context.sendActivity(reply, null, InputHints.IgnoringInput);
})
.catch(error => {
if (error) throw new Error(error);
});
return result;
};
};
module.exports.MapHelper = MapHelper;
const { ChoicePrompt, ChoiceFactory, ComponentDialog, ListStyle, WaterfallDialog } = require('botbuilder-dialogs');
const { MapHelper } = require('./mapHelper');
const CONFIRM_LOCALE_DIALOG = 'confirmLocaleDialog';
const CHOICE_PROMPT = 'confirmPrompt';
class ConfirmLocaleDialog extends ComponentDialog {
constructor() {
super(CONFIRM_LOCALE_DIALOG);
this.addDialog(new ChoicePrompt(CHOICE_PROMPT))
.addDialog(new WaterfallDialog(CONFIRM_LOCALE_DIALOG, [
this.askLocationStep.bind(this),
this.getMapStep.bind(this)
]));
this.initialDialogId = CONFIRM_LOCALE_DIALOG;
}
async askLocationStep(stepContext) {
const choices = ['Send me a map', "I'll have none of this nonsense!"];
return await stepContext.prompt(CHOICE_PROMPT, {
prompt: 'Good sir, may I pinpoint you on a map?',
choices: ChoiceFactory.toChoices(choices),
style: ListStyle.suggestedAction
});
}
async getMapStep(stepContext) {
const { context, context: { activity } } = stepContext;
const text = activity.text.toLowerCase();
if (text === 'send me a map') {
const { latitude, longitude } = activity.channelData;
const mapHelper = new MapHelper();
await mapHelper.getMap(context, latitude, longitude);
const message = 'Thanks for sharing!';
await stepContext.context.sendActivity(message);
return await stepContext.endDialog();
} else {
await stepContext.context.sendActivity('No map for you!');
return await stepContext.endDialog();
}
}
}
module.exports.ConfirmLocaleDialog = ConfirmLocaleDialog;
module.exports.CONFIRM_LOCALE_DIALOG = CONFIRM_LOCALE_DIALOG;
Hope of help!
---- EDIT ----
Per request, location data can be obtained from the browser using the below method. It is, of course, dependent on the user granting access to location data.
navigator.geolocation.getCurrentPosition( async (position) => {
const { latitude, longitude } = position.coords;
// Do something with the data;
console.log(latitude, longitude)
})

How to send push notification to Specific user in Web-push?

I have an existing web site , i just need to push notification to the site, for that i am using
Nodejs web-push package , I can able to receive notifications but i need to change it to User Specific,
For Example i want to send the notifications for the users based on the country
This is my code
client.js
const publicVapidKey = 'xxxxxx';
if ('serviceWorker' in navigator) {
console.log('Registering service worker');
run().catch(error => console.error(error));
}
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
async function run() {
console.log('Registering service worker');
const registration = await navigator.serviceWorker.
register('worker.js');
console.log('Registered service worker');
console.log('Registering push');
const subscription = await registration.pushManager.
subscribe({
userVisibleOnly: true,
// The `urlBase64ToUint8Array()` function is the same as in
// https://www.npmjs.com/package/web-push#using-vapid-key-for-applicationserverkey
applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
});
// subscription.user = $('.header-user-name').find('span').text();
console.log('Registered push');
console.log('Sending push');
await fetch('http://localhost:3000/subscribe?user='+$('.header-user-name').find('span').text(), {
method: 'POST',
body: JSON.stringify(subscription),
headers: {
'content-type': 'application/json'
}
});
console.log('Sent push');
}
Worker.js
console.log('Loaded service worker!');
self.addEventListener('push', ev => {
const data = ev.data.json();
console.log('Got push', data);
ev.waitUntil(self.registration.showNotification(data.title, {
body: 'Hello, World!',
registration_ids: [$('.header-user-name').find('span').text()]
icon: 'http://mongoosejs.com/docs/images/mongoose5_62x30_transparent.png'
}));
});
Server Code (localhost:3000/push)
app.get('/push',function(req,res) {
const payload = JSON.stringify({ title: 'Hello '+ user.name +' ' + req.query.title, });
console.log(req.query);
console.log("yahooooooooooooooooooooooooooooooooo");
webpush.sendNotification(newSubscription, payload).catch(error => {
console.error(error.stack);
});
res.send({result : 'Success'});
});
After long gap, I got a solution for this problem.
Steps to be followed:
Create an ExpressJs api to store the user subscription in to a database.
Get the user based on countries from database (you can use your own backend language I have chosen Nodejs).
Create an api which can send push notifications using given parameter like (Country, Users, Etc).
Happy Coding.
You can use the users' IPs to look up their countries by using a service like ip-api.com. After you get the country info, you can include it in your request body along with the push subscription object and send to your backend. So, you will have the opportunity to segment your subscribers and send them push notifications with different content.

Cloud Functions Error Firebase

I am trying to do push notification through Functions in Firebase.
Here is my code in Node.JS
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendPushNotification = functions.database.ref('Received Downs/{owner}/{propID}')
.onCreate(event => {
// get the owner name and propID
var owner = event.params.owner;
var propID = event.params.propID;
// Log it
console.log('Owner: ' + owner + ' Property ID: ' + propID);
// Get the list of device notification tokens.
return admin.database().ref(`/users/${owner}`).once('value', snapshot => {
var ownerID = snapshot.val();
// This will find requester ID
return admin.database().ref(`/Received Downs/${owner}/${propID}`).once('value', snapshot => {
// First will find the property the requester downed
var property = snapshot.val();
// Find the requester's name
return admin.database().ref('/Users Info/' + property.downedBy).once('value', snapshot => {
// Requester's ID
var downedBy = snapshot.val();
// Notification details.
const payload = {
notification: {
title: 'You have a new request!',
body: `${downedBy.name} is now following you.`,
sound: 'default'
}
};
// Listing all tokens. (the function save the keys of given variable)
// const tokens = Object.keys(getDeviceTokens.val());
// var fcmToken = "dzJLM-JdIt8:APA91bHBJJP6t3Z0_T7kEFDrLLsu5T_NpYsR6QmJz2EJhpK88SV1ZfemoyCtC_6hl3_0sCPdzkvlQFoAFhlWn4xTQOY3k5P8JMvdYFyeNBN1lHceQtytE0y-9oTP6qgKspi9p9E8V9dB";
// Send to all tokens of a device
admin.messaging().sendToDevice(ownerID.token, payload)
.then(response => {
console.log("Successfully sent message:", response);
}).catch(function(error) {
console.log("Error sending message:", error);
});
})
})
})
})
And here is what I got in LOGS at Firebase Functions
When I used a variable that has fem token , typed, it works fine, but not when i fetched from Firebase Realtime Database. Anyone could tell me why?
The problem I had the wrong path return admin.database().ref(/users/${owner})

Resources