Can we batch multiple onCreate events in one cloud function? - firebase

I have a cloud function newMessage to listen on any onCreate() event in firebase realtime database. When the newMessage function is triggered, it will get the snapshot of the message content, and use mailgun to send email notification for the new message.
pseudo code is like:
exports.newMessage = functions.database
.ref('/privateMessages/{userA}/{userB}/{messageId}')
.onCreate((snap, context) => {
let senderEmail, receiverEmail, messageContent;
// 1. get message senderEmail, receiverEmail and messageContent from the snap and context.
// 2. use mailgun to send email to receiverEmail for the new message
let data = {
from: senderEmail,
to: receiverEmail,
subject: 'New Message from Freebies',
text: messageContent
};
mailgun.messages().send(data, (error, body) => {
console.log(body);
});
})
return null;
})
My concern is if user is flooding the chat messages, from the newMessage, it will send email every time there comes a new message. On receiver's side, it would be annoying if there are too many email notifications in the inbox. So my question is that is it possible to have multiple onCreate() to get multiple new messages, say within 1 hour, and send only 1 email message for those new messages?

Related

Firebase Messaging Get Cloud Function Name

I have 2 different cloud functions that send a notification to the device. one is sendNewChatMessageNotification and the other one is sendNewFriendRequestNotification.
How can I get the function name that triggered the onMessage method of the firebaseMessaging instance? because I want to show different messages for the different cloud functions.
final firebaseMessaging = FirebaseMessaging();
firebaseMessaging.getToken().then((tokenVal) {
fcmToken = tokenVal;
firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
// -----> how can I get the cloudFunctionName ??
if(cloudFunctionName == "sendNewFriendRequestNotification"){
//show friend request notification
}else if(cloudFunctionName == "sendNewChatMessageNotification"){
//show new chat message notification
}
}
You will have to code your function to send that string value in the payload of the message. It will not be sent automatically.

Send SMS using POST request from Firebase Cloud Function

I have an SMS API from textlocal which I want to use to send SMS using Cloud function when a new Document is created in Firestore Collection.
Collection name: Booking SMS. It will have a document created with fields 'number', 'name' & 'service'. Now SMY API need few parameters to send SMS.
API URL: 'https://api.textlocal.in/send/'
Apikey
Sender
Message. Message will be constructed as 'Hi, 'name' your booking of 'service' is confirmed.
Name, service & number will be from Firestone document and apikey and sender will be coded in cloudfunction code.
Now I want to create cloudfunction trigger which will send sms when document is created. Below is incomplete code I tried please help me to complete it.
const functions = require('firebase-functions');
const admin = require("firebase-admin");
const axios = require("axios");
admin.initializeApp()
exports.sendSMS = functions.firestore
.document('BookingSMS/{BookingSMSId}')
.onCreate((snap, context) => {
const smsOptions = axios.create( {
baseURL: "https://api.textlocal.in/",
params: {
apiKey: "xQ1Fvg7uv14NaAEQHl2D", //Text local api key
sender: "ASDASD",
test: "true",
number: snap.data().phone,
service: snap.data().service,
message: `Hi, Your Booking for ${snap.data().service} is
Confirmed. Thank You.`
}
});
smsOptions.post("/send");
});
I also want to add +91 before number. and construct message using name and service.
As per the documentation, there is no direct support for node.js. You should be able to use their get request though. (Hope you're familiar with async/await)
Try:
async function bookSMS(user) {
// 5. Send booking SMS to users
const smsoption = {
apikey: 'asasasasasasasasas',
sender: 'DDDDDD'
to: '${user.phone}',
message: 'Welcome!',
}
// 6. Process the sending of this SMS
await fetch(`https://api.textlocal.in/send/?apikey=${apiKey}&numbers=${user.phone}&message=Welcome!&sender=DDDDDD`)
}
You must enable billing to access external APIs in firebase

Ionic Firestore Chat-unable to constantly check for updates

Currently, I am newbie at Ionic and I am trying to implement a chat service for an app I am working on. I wanted to try and use firestore and this is what I have so far.
I have managed to read and receive messages from firestore but only after I send my message.
In other words, the function to retrieve the messages only activates after I have clicked the send button.
I was wondering if there was a way for me to constantly check firestore for updates, so that the chat can be updated in "real time".
This is the code that I have for receiving the chat messages. I have placed them in the ionviewwilload() to receive all messages when entering the chatroom and when I click send on my messages.
retrieveCollection() : void
{
this._DB.getChatMessages(this._COLL,this._COLL2)
.then((data) =>
{
console.log(data);
// IF we don't have any documents then the collection doesn't exist
// so we create it!
if(data.length === 0)
{
// this.generateCollectionAndDocument();
}
// Otherwise the collection does exist and we assign the returned
// documents to the public property of locations so this can be
// iterated through in the component template
else
{
this.chats = data;
}
})
.catch();
}
The getchatmessages function is then linked to my provider to then retrieve my messages from firestore before returning it as a promise.
getChatMessages(collectionObj: string, collectionObj2) : Promise<any>{
let user : string = firebase.auth().currentUser.uid;
return new Promise((resolve, reject) => {
this._DB.collection(collectionObj).doc(collectionObj2).collection("messages")
.orderBy("sendDate", "asc")
.onSnapshot
((querySnapshot) => {
let obj : any = [];
querySnapshot
.forEach(function(doc) {
console.log(typeof doc);
console.log(doc);
obj.push({
id : doc.id,
message : doc.data().message,
type : doc.data().type,
user :doc.data().user,
image: doc.data().image
});
});
resolve(obj);
})
});
}
So my question is to ask if there is a special method or function that I have missed in the documentation that allows me to constantly check for updates like that in a chat app.
Many thanks to those who reply.

Botbuilder doesn't respond after sending 'accepted' code when using cloud functions

I'm using Dialogflow as the handler for my botbuilder endpoint, which is a Azure Bot Service bot, and the handler is deployed on a Firebase Cloud Function, but for every botframework request I make, the function returns a 202 (that's the default behaviour of botbuilder I believe), and the function stops working in the middle of the code.
I saw in this response from Frank van Puffelen that the functions may halt if there's a response from the function.
Cloud Functions for Firebase: serializing Promises
Is this enough to stop my function from executing? If so, is there a way to stop this from happening?
Edit: I'm using the Universal Bot to setup the callback for the messages.
const bot = new builder.UniversalBot(this.connector, botFrameworkCallback)
.set('storage', new builder.MemoryBotStorage());
And here's the botFrameworkCallback:
const botFrameworkCallback = (session) => {
const message = session.message.text;
const userRef = new UserRef('user');
let userInfo;
userRef
.get()
.then((userInformation) => {
console.log('user information', userInformation);
userInfo = userInformation;
const userData: IUser = {
...userInfo,
ref: userRef
};
return makeDialogflowRequest(userData, message);
})
.then((intentResult: any) => {
console.log('intent result', intentResult);
const response = intentResult.answer;
session.send(response);
})
.catch((err) => {
console.log('Error on BotFramework', err);
const response = 'Sorry. An error happened while getting your response.';
session.send(response);
});
}
The whole integration part is there to give user specific responses, so this code does a lot of API requests, like Firestore ones, the Dialogflow one, and because of that we've set it up this way.

How to send email verification after user creation with Firebase Cloud functions?

I'm trying to send the verification email after the user is created. Since there's no way on Firebase itself, I'm trying it with cloud functions.
I cannot really find a lot of documentation about it. What I tried to do so far is:
exports.sendEmailVerification = functions.auth.user().onCreate(event => {
return user.sendEmailVerification()
});
But I get the error that user is not defined.
How can I create this function?
Thanks!
There are two possibilities to send an "email verification" email to a user:
The signed-in user requests that a verification email be sent. For that, you call, from the front-end, the sendEmailVerification() method from the appropriate Client SDK.
Through one of the Admin SDKs, you generate a link for email verification via the corresponding method (e.g. auth.generateEmailVerificationLink() for the Node.js Admin SDK) and you send this link via an email sent through your own mechanism. All of that is done in the back-end, and can be done in a Cloud Function.
Note that the second option with the Admin SDKs is not exactly similar to the first option with the Client SDKs: in the second option you need to send the email through your own mechanism, while in the first case, the email is automatically sent by the Firebase platform
If you'd like that ability to be added to the Admin SDK, I'd recommend you file a feature request.
This is how I implemented it successfully using Firebase cloud functions along with a small express backend server
Firebase Cloud function (background) triggered with every new user created
This function sends a "user" object to your api endpoint
const functions = require('firebase-functions');
const fetch = require('node-fetch');
// Send email verification through express server
exports.sendVerificationEmail = functions.auth.user().onCreate((user) => {
// Example of API ENPOINT URL 'https://mybackendapi.com/api/verifyemail/'
return fetch( < API ENDPOINT URL > , {
method: 'POST',
body: JSON.stringify({
user: user
}),
headers: {
"Content-Type": "application/json"
}
}).then(res => console.log(res))
.catch(err => console.log(err));
});
Server Middleware code
verifyEmail here is used as middleware
// File name 'middleware.js'
import firebase from 'firebase';
import admin from 'firebase-admin';
// Get Service account file from firebase console
// Store it locally - make sure not to commit it to GIT
const serviceAccount = require('<PATH TO serviceAccount.json FILE>');
// Get if from Firebase console and either use environment variables or copy and paste them directly
// review security issues for the second approach
const config = {
apiKey: process.env.APIKEY,
authDomain: process.env.AUTHDOMAIN,
projectId: process.env.PROJECT_ID,
};
// Initialize Firebase Admin
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
// Initialize firebase Client
firebase.initializeApp(config);
export const verifyEmail = async(req, res, next) => {
const sentUser = req.body.user;
try {
const customToken = await admin.auth().createCustomToken(sentUser.uid);
await firebase.auth().signInWithCustomToken(customToken);
const mycurrentUser = firebase.auth().currentUser;
await mycurrentUser.sendEmailVerification();
res.locals.data = mycurrentUser;
next();
} catch (err) {
next(err);
}
};
Server code
// Filename 'app.js'
import express from 'express';
import bodyParser from 'body-parser';
// If you don't use cors, the api will reject request if u call it from Cloud functions
import cors from 'cors';
import {
verifyEmail
} from './middleware'
app.use(cors());
app.use(bodyParser.urlencoded({
extended: true,
}));
app.use(bodyParser.json());
const app = express();
// If you use the above example for endpoint then here will be
// '/api/verifyemail/'
app.post('<PATH TO ENDPOINT>', verifyEmail, (req, res, next) => {
res.json({
status: 'success',
data: res.locals.data
});
next()
})
This endpoint will return back the full user object and will send the verification email to user.
I hope this helps.
First view the documentation by Firebase here.
As the registration phase completes and result in success, trigger the following function asynchronously :
private void sendVerification() {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
user.sendEmailVerification().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
system.print.out("Verification Email sent Champion")
}
}
});
}
The user will now be provided with a verification Email. Upon clicking the hyper linked the user will be verified by your project server with Firebase.
How do you determine whether or not a user did verify their Email?
private void checkEmail() {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user.isEmailVerified()) {
// email verified ...
} else {
// error : email not verified ...
}
}
Sadly, you may not customize the content/body of your verification Email ( I have been heavily corresponding with Firebase to provide alternative less hideous looking templates ). You may change the title or the message sender ID, but that's all there is to it.
Not unless you relink your application with your own supported Web. Here.
Since the release of the Version 6.2.0 of the Node.js Admin SDK on November 19, 2018 it is possible to generate, in a Cloud Function, a link for email verification via the auth.generateEmailVerificationLink() method.
You will find more details and some code samples in the documentation.
You can then send an email containing this link via Mailgun, Sendgrid or any other email microservice. You'll find here a Cloud Function sample that shows how to send an email from a Cloud Function.
If you want to let Admin SDK do it, as of now there is no option other than generating the email verification link and sending with your own email delivery system.
However
You can write a REST request on cloud functions and initiate the email verification mail this way.
export async function verifyEmail(apiKey : string, accessToken : string) {
// Create date for POST request
const options = {
method: 'POST',
url: 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/getOobConfirmationCode',
params: {
key: apiKey
},
data: {
requestType : "VERIFY_EMAIL",
idToken : accessToken
}
};
return await processRequest(options); //This is just to fire the request
}
As soon as you signup, pass the access token to this method and it should send a mail to the signup user.
apiKey : Is the "Web API key" listed in General tab of your project settings in firebase console
access token : Access token of the current user (I use signup rest api internally so there is an option to request token in response)

Resources