firebase functions push Notifications to specific user - javascript

i want to send Push notification via firebase functions to the user who posted the post when some other user likes his/her post.
i want to get the highlighted user-id in the image to get fcm token of this user id stored in other tree.
here is my firebase function code below.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/user-posts/{userID}/{pushId}/stars/')
.onWrite(event=> {
var request = event.data.val();
console.log("request",request);
console.log("key",Object.keys(request)[0]);
var key = Object.keys(request)[0];
var token;
const payload = {
notification: {
title: 'You have a new follower!',
body: 'is now following you.'
}
};
const getDeviceTokensPromise = admin.database()
.ref(`/users-notifications/${key}`)
.once('value').then(function(snapshot) {
console.log("val",snapshot.val());
token= snapshot.val();
admin.messaging().sendToDevice(token,payload)
.then(response=>{
console.log("Successfully sent message:", response);
})
.catch(function(error){
console.log("error sending message",error);
})
})
}, function(error) {
// The Promise was rejected.
console.error(error);
});

You can backwards traverse the DB tree by using the event ref's parent property.
userID = event.data.ref.parent.parent.parent.key
parent of event.data.ref is "stars"
parent of "stars" is your pushID
parent of pushID is userID

Try extracting it from the uri with event.params.userID

Related

How to get cloud function URL link?

This is my code of cloud function. I'm using it if data in Firebase changes it notify users. I have already deployed the cloud function but it is not giving me any cloud function URL.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.userStatusChange = functions.database.ref('/PatientReading/{$patient}/Humidty')
.onWrite(event => {
const original = event.data.val();
const previous = event.data.previous.val();
if (event.data.exists()) {
var title = "User Signed IN";
var body = "User " + original + " signed in";
}
var payload = {
notification: {
title: title,
body: body
}
};
var topic = "OnlineUsers";
return admin.messaging().sendToTopic(topic, payload)
.then(function(response) {
console.log("Successfully sent message:", response);
return true;
})
.catch(function(error) {
console.log("Error sending message:", error);
return true;
});
});
Your code is defining a Realtime Database trigger. These functions only run in response to changes in the database at the path you specify. These functions never have a URL - they can't be invoked directly.
If you need an URL to invoke some code in Cloud Functions, you will have to write an HTTP trigger.

Send notifications to android app using Firebase Functions

I am developing a chat app and so, I need to send notifications that new messages have been received.
For that, I am using Firebase Functions.
I'm using the sendToDevice function, that needs a token to send a notification. The problem is, I can't seem to retrieve token of the user that sent the message.
This is my .js code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref("/chats/{id}/messages/{messageId}/content")
.onWrite((change,context) => {
var content = change.after.val();
var payload = {
data:{
title: "Stranger has sent you a message",
text: content
}
};
// Here I need to the ID of the person who sent the message
// And then compare this Id with the two Ids of the to users that are in the conversation
// If the two Ids are different, then save the other Id as the token
// So that I can send a notification to the other user.
const senderId = database.ref("/chats/{id}/messages/{id}/sender/{senderId}");
admin.messaging().sendToDevice(senderId, payload)
.then(function(response){
console.log("Successfully sent message: ", response);
return null;
})
.catch(function(error){
console.log("Error sending message: ", error);
})
});
As you can see, I am checking for any changes in the messages/content child.
That as the content of my notification.
Then, I am trying to retrieve the message sender ID so I can know who sent the message and retrieve the other user Id to notify him.
This might be a little confusing so here is my Firebase Realtime Database:
What am I doing wrong so this piece of code works as it should? This is the activity I have in android to receive the message:
class MyFirebaseInstanceId : FirebaseMessagingService() {
override fun onMessageReceived(p0: RemoteMessage) {
if(p0.data.size > 0){
val payload :Map<String, String> = p0.data
sendNotification(payload)
}
}
private fun sendNotification(payload: Map<String, String>) {
val builder = NotificationCompat.Builder(this)
builder.setSmallIcon(R.drawable.common_google_signin_btn_icon_disabled)
builder.setContentTitle(payload.get("username"))
builder.setContentText(payload.get("email"))
val intent = Intent(this, MainActivity::class.java)
val stackBuilder = TaskStackBuilder.create(this)
stackBuilder.addNextIntent(intent)
val resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(resultPendingIntent)
val notificationManager = (getSystemService(Context.NOTIFICATION_SERVICE)) as NotificationManager
notificationManager.notify(0, builder.build())
}
}
Following our comments above, here is how to use the once() and val() methods in your Cloud Function:
//.....
const refSenderId = database.ref("/chats/{id}/messages/{id}/sender/{senderId}");
return refSenderId.once('value')
.then(dataSnapshot => {
const senderId = dataSnapshot.val();
return admin.messaging().sendToDevice(senderId, payload)
})
.then(function(response){
console.log("Successfully sent message: ", response);
return null;
})
.catch(function(error){
console.log("Error sending message: ", error);
return null; // <- Note the return here.
})
//.....

RabbitMQ Node JS Validate User ID

I use RabbitMQ, SocketIO and MongoDB to make private messages for my app.
The plan is when new user register, app make new unique RabbitMQ Queue for that user, with that user is able to get messages when it is offline. So user send message through SocketIO and it is passed to RabbitMQ Publisher and then when consumer is online he get that message.
My questions now is how to set Validate User ID from sendToQueue (Publisher) function to be able later to read sender ID from consume function?
amqp.connect(CONN_URL, function(err, conn) {
conn.createChannel(function(err, channel) {
ch = channel;
console.log("RabbitMQ channel created...");
});
});
const publishToQueue = async (queueName, data) => {
ch.sendToQueue(queueName, Buffer.from(data.message));
};
const consumeToQueue = async queueName => {
ch.consume(
queueName,
function(msg) {
return msg.content.toString();
},
{ noAck: true }
);
};
I worked out...
const publishToQueue = async (queueName, data) => {
let properties = { headers: {userId: data.to }, timestamp: Date.now() };
ch.sendToQueue(queueName, Buffer.from(data.message), properties);
};
This will send headers with userId information and also timestamp of message

Message returned as undefined

I tried to send notifications through firebase functions when data is stored in my firebase database. It sends the message alright but a log tag I added to see if it got the name of the file that was uploaded to my firebase database came back as "Lecture note uploaded is: undefined". That's line 12 I don't understand why.
Below is my code.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendNotification = functions.database.ref('/Lecture_Materials/{MIS}/{MISId}/name')
.onWrite(( change,context) =>{
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
var str1 = "Lecture material uploaded is: " + eventSnapshot.name;
console.log(str1);
var topic = "Management.Information.System";
var payload = {
data: {
name: str1,
}
};
// 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);
return;
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});

How to add results from a promise based API call with message.addReply using Recast.ai?

I'm making a bot that searches restaurants based on location. Can anyone help me why this doesnt show up in FB messenger?:
restaurants(result.getMemory('location').raw)
.then(res=>{
message.addReply(res);
message.reply();
});
}
The call to the restaurants function returns the results from a YELP API call (an array of restaurants) but when I add it as a reply to message, nothing happens in FB messenger.
Here is the full code for message.js:
const recastai = require('recastai');
const restaurants = require('./restaurants');
// This function is the core of the bot behaviour
const replyMessage = (message) => {
// Instantiate Recast.AI SDK, just for request service
const request = new recastai.request(process.env.REQUEST_TOKEN,
process.env.LANGUAGE);
// Get text from message received
const text = message.content;
console.log('I receive: ', text);
// Get senderId to catch unique conversation_token
const senderId = message.senderId;
// Call Recast.AI SDK, through /converse route
request.converseText(text, { conversationToken: senderId })
.then(result => {
//Recast takes text analyses that, returns a result object, generates replies adds messages to reply stack and then sends the replies
//Call Yelp API with when the intent is Location. When Yelp returns result we add it to the result.replies array.
//Then we add everything in result.replies to the messaging queue that sends the responses to FB
if (result.action) {
console.log('The conversation action is: ', result.action.slug);
}
// If there is not any message return by Recast.AI for this current conversation
if (!result.replies.length) {
message.addReply({ type: 'text', content: 'I don\'t have the reply to this yet :)' });
} else {
// Add each reply received from API to replies stack
result.replies.forEach(replyContent => message.addReply({ type: 'text', content: replyContent }));
}
// Send all replies
message.reply()
//send initial reply generated by Recast first
.then(() => {
//call restaurant function that returns a list of results from API
//if the action is location and done
if(result.action && result.action.slug === 'location' && result.action.done){
restaurants(result.getMemory('location').raw)
.then(res=>{
console.log(res);
message.addReply(res);
message.reply();
});
}
})
.catch(err => {
console.error('Error while sending message to channel', err);
});
})
.catch(err => {
console.error('Error while sending message to Recast.AI', err);
});
};
module.exports = replyMessage;
And here is my restaurants.js code that is imported into the message.js file for the bot behavior:
const rp = require('request-promise');
// Load configuration
require('./config');
const restaurants = (location) => {
return Promise.all([
yelpCall(location)
]).then(result => {
//result contains the return value from Yelp call
return result;
});
};
const yelpCall = (location) => {
const auth = {
method: 'POST',
url: 'https://api.yelp.com/oauth2/token?grant_type=client_credentials&client_id='+ process.env.YELP_APP_ID +'&client_secret='+process.env.APP_SECRET
};
return rp(auth)
.then(result => {
const tokens = JSON.parse(result);
return tokens;
})
.then(result=>{
const options = {
url: 'https://api.yelp.com/v3/businesses/search?location=' + location + "&term=thai",
headers: {Authorization: "Bearer " + result.access_token}
};
return rp(options).then(findings =>{
return findings;
});
});
};
module.exports = restaurants;
A few thoughts :
message.reply is thenable, therefore return message.reply() in two places.
request.converseText() is thenable, therefore return request.converseText(...).
restaurants is thenable, therefore return restaurants(...).
in message.js, message.addReply() is passed object of the form {type:..., content:...} in two places but finally just res. Is that correct?
in restaurants.js, Promise.all() appears to be unnecessary. It will cause its result to be wrapped in an array. module.exports = location => yelpCall(location); seems more appropriate.

Categories

Resources