How to properly send a message from firebase cloud function? - firebase

Im trying to learn how to read the official firebase docs. but I cant seem to get it right
I want to send notifications from a cloud function to a phone, but I was only able to do it using some functions not found on the docs ( see code below )
I know the docs say to use getMessaging().send(message), but I cant get it to work {see image attached}
code where I am able to send notification
const functions = require("firebase-functions");
const admin = require("firebase-admin");
// eslint-disable-next-line max-len
const tokens = ["REDACTED_TOKEN"];
admin.initializeApp();
exports.onCreate = functions.firestore
.document("chat/{docId}")
.onCreate((snapshot, context) => {
console.log(snapshot.data());
console.log("fake data");
});
exports.onUpdate = functions.firestore
.document("chat messages/{docId}")
.onCreate( (snapshot, context) => {
const payload = {
// eslint-disable-next-line max-len
notification: {title: "Push Title", body: "Push Body", sound: "default"},
// eslint-disable-next-line max-len
data: {click_action: "FLUTTER_NOTIFICATION_CLICK", message: "Sample Push Message"},
};
try {
admin.messaging().sendToDevice(tokens, payload);
console.log("NOTIFICATION SEND SUCCESFULLY");
} catch (e) {
console.log("ERROR SENDING NOTIFICATION");
console.log(e);
}
});
[2] https://firebase.google.com/docs/cloud-messaging/send-message

You are probably interested in something like this:
const messaging = require("firebase-admin/messaging");
messaging.getMessaging() //continued with what you want to accomplish.
The firebase-admin node package has a sub packaging for messaging where getMessaging() lives, but the documentation also seems to note that getMessaging() is the equivalent of admin.messaging() so using getMessaging() would make your functions closer in line with the documentation. If you wanted to be even more similar to the documentation, you may want to switch from require to import to selectively load only the parts of the package you need. An example of using import instead of require may look like this:
import {getMessaging} from 'firebase-admin/messaging';
getMessaging().send(message);

Related

Send an email using Firebase Cloud Functions in React Native

I would like to send emails in my React Native using Firebase Cloud Functions. Users should be able to send an email for reporting issues/feedback in the app. I have created a text input and a button in my react native app. The user should be able to specify their issue/feedback in the text input box and when they press the button I will receive their response as an email in my gmail or hotmail account. Can I achieve this using onCreate in Firebase Cloud Functions? If so how can I achieve this? What would the onCreate method look like as well as the button function in react native? I am very new to react native and firebase cloud functions. Unfortunately, I haven't seen any links about this.
Thank you.
Please see below:
const nodemailer = require('nodemailer');
const email = functions.config().email_credentials.email;
const password = functions.config().email_credentials.password;
const mailTransport = nodemailer.createTransport(`smtps://${email}:${password}#smtp.gmail.com`);
functions.database.ref('/feedbacks/{currentId}').onCreate((snapshot, context) => {
const feedback = snapshot.val().feedback;
const name = snapshot.val().name;
const mailOptions = {
from: snapshot.val().email,
replyTo: snapshot.val().email,
to: functions.config().email_credentials.email,
subject: `Feedback from `+name,
html: feedback,
};
try {
mailTransport.sendMail(mailOptions);
} catch(error) {
console.log(error);
}
return null;
});
Realtime database:
Your cloud function could look like this:
import * as functions from "firebase-functions";
import admin from "firebase-admin";
import nodemailer from "nodemailer";
const { email, password } = functions.config().gmail;
const mailTransport = nodemailer.createTransport(
`smtps://${email}:${password}#smtp.gmail.com`
);
export default functions.database
.ref("/feedbacks/{uid}")
.onCreate(async (eventSnapshot, context) => {
const data = eventSnapshot.val();
const { feedback } = data;
const mailOptions = {
from: functions.config().email_credentials.email,
replyTo: functions.config().email_credentials.email,
to: snapshot.val().email,
subject: `Feedback from `+name,
html: feedback,
};
await mailTransport.sendMail(mailOptions);
return null;
});
Make sure to save your email credentials under the firebase cloud function configs and NOT in the code. If you put it anywhere in the code it could potentialy been read by someone in some time. This is very importand.
In your Gmail ensure "Unsercure Apps" are enabled. More about it here.
Now if someon adds some data to the path feeds and email will be send.
Don't forget to deplyo your function with the configs.

cloud function using typrescript and if statment

hello I'm new to cloud functions and I want to ask if there is some why to get more than one topic in one if statement
here is my cloud function :
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
const fcm = admin.messaging();
export const sendToTopic = functions.firestore
.document("Doctor2019/{documentId}")
.onCreate(async snapshot => {
const payload: admin.messaging.MessagingPayload = {
notification: {
title: 'NEW POST!',
body: `Click here to see New Post`,
icon: 'your-icon-url',
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
};
return fcm.sendToTopic('Doctor2019', payload);
});
the problem is that I have more than one topic what I want to do is to check the creation of document on other collections and send the notification based on that , I really don't know what to do , any help ?
I see that you want to send messages to different topics on FCM that are tied to different documents creations.
You cannot use one function to achieve that as the Function is tied to document creation on a specific collection. what you will need to do is to create different functions to different collections.
If I correctly understand your goal, you can use a {wildcard} in place of the document ID as well as in place of the collection ID. Then you use the context object to get the value of the collection ID as follows:
export const sendToTopic = functions.firestore
.document("{collectionId}/{documentId}")
.onCreate(async (snap, context) => { // Note the addition of context
const collectionId = context.params.collectionId;
const payload: admin.messaging.MessagingPayload = {
notification: {
title: 'NEW POST!',
body: `Click here to see New Post`,
icon: 'your-icon-url',
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
};
return fcm.sendToTopic(collectionId, payload);
});
In case you have root collections that should not trigger a message, just adapt your data model and make the collections that need to trigger a message subcollections of a specific document. Something like:
export const sendToTopic = functions.firestore
.document("messagingTriggers/triggers/{collectionId}/{documentId}")
.onCreate(async (snap, context) => {...});
Then any document creation in, for example, a users collection will not trigger the Cloud Function.

Is there a way to get email or text notifications each time data is written to my Google Cloud Firestore bucket?

I have a google cloud bucket and firebase writes my app data there. I would like to monitor my data, and have any new update (write) to my firebase database it sent via a text or email to me. I currently have Twilio set up on Nodejs to send texts on Firebase and my code is:
const functions = require('firebase-functions');
var twilio = require('twilio');
const admin = require('firebase-admin');
admin.initializeApp();
var accountSid = 'account id'; // Account SID from www.twilio.com/console
var authToken = 'account token'; // Auth Token from www.twilio.com/console
var client = new twilio(accountSid, authToken);
exports.useWildcard = functions.firestore
.document('comments/{commentContent}')
.onWrite((change, context) => {
client.messages.create({
body: context.params.commentContent,
to: '+15555555555', // Text this number
from: '+15555555556' // From a valid Twilio number
})
.then((message) => console.log(message.sid));
});
Currently, I would like to build it out for just the comments document, which are organized inside firebase through comments/{commentContent}. Later, I would like to expand to other trees. I am however, unsure if the above will run each time there is a write to my comments tree. Does it require the firebase-admin module as I have put above? Thanks!
Yes, the onWrite method will not only run when there is a write to the comments tree, but will also be triggered by any change in any document and on the deletion of a document. This means that right now your code will responde in the same way to any of the above cases, and this could cause problems, especially in the case of a document being deleted since it will try to send a comment that doesent exist and will likely get some null exceptions.
Said that you have different solutions.
If you only want the function to react to a new comment, but not to an update or deletion you should use onCreate trigger instead of onWrite.
If you also want to handle a comment update notification you can use both onCreate and onUpdate, but sending different messages by doing something like:
exports.useWildcardCreate = functions.firestore
.document('comments/{commentContent}')
.onCreate((change, context) => {
client.messages.create({
body: context.params.commentContent,
to: '+15555555555', // Text this number
from: '+15555555556' // From a valid Twilio number
})
.then((message) => console.log(message.sid));
});
exports.useWildcardUpdate = functions.firestore
.document('comments/{commentContent}')
.onUpdate((change, context) => {
const newComment = change.after.data();
const previuosComment = change.before.data();
client.messages.create({
body: 'The comment ${previuosComment} has been changed to ${newComment}',
to: '+15555555555', // Text this number
from: '+15555555556' // From a valid Twilio number
})
.then((message) => console.log(message.sid));
});
At last if you also need to notify when a comment has been deleted you should use onWrite method but differentiating between the 3 different cases as shown below:
exports.useWildcard = functions.firestore
.document('comments/{commentContent}')
.onWrite((change, context) => {
var textBody;
const oldComment = change.before.data();
const newComment = change.after.data();
if (change.after.exists == false) { // comment has been deleted
textBody = 'The comment ${oldComment} has been deleted';
}
else if (oldComment != newComment) { // comment has been updated
textBody = 'The comment ${oldComment} has been changed to ${newComment}';
}
else { // if its not an update or a deletion its a new comment
textBody = newComment;
}
client.messages.create({
body: textBody,
to: '+15555555555', // Text this number
from: '+15555555556' // From a valid Twilio number
})
.then((message) => console.log(message.sid));
});
Finally require('firebase-admin') is needed since it will allow you to interact with Firebase from privileged environments. Here you can find all the information to the Firebase Admin SDK

Firebase cloud functions https, calling API not working for me

I'm trying to learn firebase cloud functions and I want to call an API using it but the console log shows that no data is being fetched, even though the function is deployed successfully/
Firebase function:
const functions = require('firebase-functions');
const axios = require('axios');
exports.fetchList = functions.https.onRequest((request, response) =>{
axios.get('https://rallycoding.herokuapp.com/api/music_albums').then((data) =>{
response.send(data)
}).catch((e) =>{
console.log(e)
})
})
App component:
componentWillMount() {
axios({
method:'POST',
url: 'link from the console website',
}).then((data) =>{
console.log(data.data);
}).catch((e) =>{
console.log(e);
})
}
If you want to see that data from the fetchList data logged to the Firebase console, you need to insert a console.log before you send a response.
const dataToLog = axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(dataToLog => {
console.log(dataToLog);
response.send(dataToLog);
}).catch // etc.
Also, probably obvious, but of course you'd need to put in the actual function URL endpoint in your component, not the string link from the console website.
Apologies if I'm misunderstanding your question!

Unable to send SMS through Twilio and Google Functions

I am attempting to send a text (a one-time pass code) using Twilio, firebase and Google Functions, and using Postman.
I have run $ npm install --save twilio#3.0.0 -rc.13 in the functions directory.
When I run $ firebase deploy, it completes. But on Postman, when I do POST, Body and feed a JSON { "phone": "555-5555" }, I get an "Error: could not handle the request."
I am able to send a text in Twilio Programmable SMS from my Twilio number to an actual outside number direct to the mobile phone. I'm using live credentials for Sid and AuthToken.
Is this an issue with Twilio, Google Functions and some configurations?
Here are the logs on Functions:
// White flag sign//
Function execution took 1452 ms, finished with status: 'crash'
//Red Warning sign//
TypeError: handler is not a function
at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:26:41)
at /var/tmp/worker/worker.js:676:7
at /var/tmp/worker/worker.js:660:9
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickDomainCallback (internal/process/next_tick.js:128:9)
Also, the google eslint forces consistent-return, which is why I put "return;" in the request-one-time-password.js. I cannot seem to turn it off by adding "consistent-return": 0 in eslintrc.
My code(with secret keys and phone numbers redacted):
//one-time-password/functions/service_account.js
has my keys copied and pasted.
//one-time-password/functions/twilio.js
const twilio = require('twilio');
const accountSid = 'redacted';
const authToken = 'redacted';
module.exports = new twilio.Twilio(accountSid, authToken);
//one-time-password/functions/request-one-time-password.js
const admin = require('firebase-admin');
const twilio = require('./twilio');
module.export = function(req, res) {
if(!req.body.phone) {
return res.status(422).send({ error: 'You must provide a phone number!'});
}
const phone = String(req.body.phone).replace(/[^\d]/g, '');
admin.auth().getUser(phone).then(userRecord => {
const code = Math.floor((Math.random() * 8999 + 1000));
// generate number between 1000 and 9999; drop off decimals
twilio.messages.create({
body: 'Your code is ' + code,
to: phone,
from: '+redacted'
}, (err) => {
if (err) { return res.status(422).send(err); }
admin.database().ref('users/' + phone).update({ code: code, codeValid: true }, () => {
res.send({ success: true });
})
});
return;
}).catch((err) => {
res.status(422).send({ error: err })
});
}
/////////////////////////////////
//one-time-password/functions/index.js
const admin = require('firebase-admin');
const functions = require('firebase-functions');
const createUser = require('./create_user');
const serviceAccount = require('./service_account.json')
const requestOneTimePassword = require('./request_one_time_password');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://one-time-password-650d2.firebaseio.com"
});
exports.createUser = functions.https.onRequest(createUser);
exports.requestOneTimePassword =
functions.https.onRequest(requestOneTimePassword);
You have
module.exports = new twilio.Twilio(accountSid, authToken);
on one line, and further down
module.export = function(req, res) { ... }.
Try changing export to exports.
One thing that tripped me up for a long time was how twilio sent the request body to the cloud function. It sends it in a body object so to access your request body it will look something like this
req.body.body
On top of that it passed it as a JSON string so I had to JSON.parse()
Example I got working:
export const functionName= functions.https.onRequest((req, res) => {
cors(req, res, () => {
let body = JSON.parse(req.body.body);
console.log(body);
console.log(body.service_id);
res.send();
});
});
This also may depend on the Twilio service you are using. I was using their Studio HTTP Request Module.
Hope this helps a little, not sure if it was your exact problem though :(

Resources