How to use twilio with Firebase Cloud Function? - node.js

I want to send a WhatsApp message to a particular number when a Firebase Realtime Database is triggered. But unable to implement the code properly -
const accountSid = 'AC565656563389214ace8531';
const authToken = '[AuthToken]';
const client = require('twilio')(accountSid, authToken);
client.messages
.create({
body: 'Your appointment is coming up on July 21 at 3PM,
from: 'whatsapp:+1415454545386',
to: 'whatsapp:+9196456454566'
})
.then(message => console.log(message.sid))
.done();
I want to use this code in Firebase function for the trigger in the Realtime database.

The following should do the trick (untested):
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// Use one of the options detailed in the doc (https://firebase.google.com/docs/functions/config-env)
// for storing the two following secrets
const accountSid = ...
const authToken = ...
const client = require('twilio')(accountSid, authToken);
exports.sendMessage = functions.database.ref('/.../{pushId}') // Adapt the path
.onCreate((snapshot, context) => { // Adapt the trigger? E.g. OnWrite?
return client.messages
.create({
body: 'Your appointment is coming up on July 21 at 3PM',
from: 'whatsapp:+1415454545386',
to: 'whatsapp:+9196456454566'
})
.then(message => {
console.log(message.sid);
return null;
});
});

Related

how can i add the sendgrid webhook event Json response in a firebase cloud firestore using node.js

I have no idea how to implement this thing but before that, I have done a part of SendGrid where any document is created then it will send the email to the user. but this part what I am asking I has no idea how to proceed.this is my first part of this implementation wherein any collection if a new record is created then it will send email to the particular email and there is a response called event Object I want to write a cloud function to store the data. and I don't know how to start this function or proceed with this problem.
"use strict";
const functions = require("firebase-functions");
const admin = require("firebase-admin");
var serviceAccount1 = require("./key.json");
const newProject = admin.initializeApp({
credential: admin.credential.cert(serviceAccount1),
databaseURL: "xyz"
});
const sgMail = require("#sendgrid/mail");
const sgMailKey = "key";
sgMail.setApiKey(sgMailKey);
exports.sentMail = functions.firestore
.document("/Offices/{officeId}")
.onCreate((documentSnapshot,event) => {
const documentData = documentSnapshot.data()
const officeID = event.params.officeId;
console.log(JSON.stringify(event))
const db = newProject.firestore();
return db.collection("Offices").doc(officeID).get()
.then(doc => {
const data = doc.data();
const msg = {
to: "amarjeetkumars34#gmail.com",
from: "singhamarjeet045#gmail.com",
text: "hello from this side",
templateId: "d-8ecfa59aa9d2434eb8b7d47d58b4f2cf",
substitutionWrappers: ["{{", "}}"],
substitutions: {
name: data.name
}
};
return sgMail.send(msg);
})
.then(() => console.log("payment mail sent success"))
.catch(err => console.log(err));
});
and the expected output of my question be like a collection name XYZ wherein an object there are three fields like
{email:"xyz#gmail.com",
event:"processed",
timestamp:123555558855},
{email:"xyz#gmail.com",
event:"recieved",
timestamp:123555558855},
{email:"xyz#gmail.com",
event:"open",
timestamp:123555558855}
As you will read in the Sendgrid documentation:
SendGrid's Event Webhook will notify a URL of your choice via HTTP
POST with information about events that occur as SendGrid processes
your email
To implement the HTTP endpoint in your Firebase Project, you will implement an HTTPS Cloud Function that will be called by the Sendgrid webhook through an HTTPS POST request.
Each call from the Sendgrid webhook will concern a specific event and you will be able, in your Cloud Function, to get the value of the event (processed, delivered, etc...).
Now, you need in your Cloud Function to be able to link a specific event with a specific email that was previously sent through your Cloud Function. For that you should use custom arguments.
More precisely, you would add to your msg object (that you pass to the send() method) a unique identifier. A classical value is a Firestore document ID, like event.params.officeId but could be any other unique ID that you generate in you Cloud Function.
Example of implementation
In your Cloud Function that sends the email, pass the officeId in a custom_args object, as shown below:
exports.sentMail = functions.firestore
.document("/Offices/{officeId}")
.onCreate((documentSnapshot,event) => {
const documentData = documentSnapshot.data();
const officeId = event.params.officeId;
const msg = {
to: "amarjeetkumars34#gmail.com",
from: "singhamarjeet045#gmail.com",
text: "hello from this side",
templateId: "d-8ecfa59aa9d2434eb8b7d47d58b4f2cf",
substitutionWrappers: ["{{", "}}"],
substitutions: {
name: documentData.name
},
custom_args: {
"officeId": officeId
}
};
return sgMail.send(msg)
.then(() => {
console.log("payment mail sent success"));
return null;
})
.catch(err => {
console.log(err)
return null;
});
});
Note that you get the data of the newly created document (the one which triggers the Cloud Function) through documentSnapshot.data(): you don't need to query for the same document in your Cloud Function.
Then, create a simple HTTPS Cloud Function, as follows:
exports.sendgridWebhook = functions.https.onRequest((req, res) => {
const body = req.body; //body is an array of JavaScript objects
const promises = [];
body.forEach(elem => {
const event = elem.event;
const eventTimestamp = elem.timestamp;
const officeId = elem.officeId;
const updateObj = {};
updateObj[event] = true;
updateObj[event + 'Timestamp'] = eventTimestamp;
promises.push(admin.firestore().collection('Offices').doc(officeId).update(updateObj));
});
return Promise.all(promises)
.then(() => {
return res.status(200).end();
})
})
Deploy it and grab its URL as shown in the terminal: it should be like https://us-central1-<your-project-id>.cloudfunctions.net/sendgridWebhook.
Note that here I use admin.firestore().collection('Offices').... You may use const db = newProject.firestore(); ... db.collection('Offices')...
Also note that the body of the HTTPS POST request sent by the Sendgrid webhook contains an array of JavaScript objects, therefore we will use Promise.all() to treat these different objects, i.e. write to the Firestore document with officeId the different events.
Then you need to set-up the Webhook in the Sendgrid platform, in the "Mail Settings/Event Notification" section, as explained in the doc and as shown below.

Twilio: Lamba Function not making Programable Web call

Relatively new to AWS Lambda, and I'm trying to integrate Twilio Programmable Voice into a Lambda function. The code in Function is the following:
'use strict';
module.exports.hello = async event => {
console.info("Program Started");
const accountSid = 'AAAAAA';
const authToken = 'BBBBBB';
const client = require('twilio')(accountSid, authToken);
client.calls
.create({
twiml: '<Response><Say>Ahoy, World!</Say></Response>',
to: '+1XXXXXXXXXX',
from: '+1YYYYYYYYY'
})
.then(call => console.log(call.sid));
console.info("Program Ended");
};
The accountSid and authToken are correct in the implementation. Twilio is inside of a Layer and the test is able to find the dependency. The logging shows both "Program Started" and "Program Ended", so the code is being called. But there is no actual call when testing. Any suggestions??
You are not returning promise from your function so there is no way for lambda to identify if your execution has completed. The last line which is console is being executed before client.calls finishes the execution as that is asynchronous. You have two choices here
Either change it to return the promise like this
'use strict';
module.exports.hello = async event => {
console.info("Program Started");
const accountSid = 'AAAAAA';
const authToken = 'BBBBBB';
const client = require('twilio')(accountSid, authToken);
return client.calls
.create({
twiml: '<Response><Say>Ahoy, World!</Say></Response>',
to: '+1XXXXXXXXXX',
from: '+1YYYYYYYYY'
})
.then(call => console.log(call.sid))
.then(() => console.info("Program Ended"));
};
OR change it to use await style
'use strict';
module.exports.hello = async event => {
console.info("Program Started");
const accountSid = 'AAAAAA';
const authToken = 'BBBBBB';
const client = require('twilio')(accountSid, authToken);
const call = await client.calls
.create({
twiml: '<Response><Say>Ahoy, World!</Say></Response>',
to: '+1XXXXXXXXXX',
from: '+1YYYYYYYYY'
});
console.log(call.sid);
console.info("Program Ended");
};

Error upon Cloud Function for Firebase deployment

I've been trying to deploy a Cloud Function to my Firebase project.
It's my first time doing so, also my first time programming with JavaScript.
Here's my code in Node.JS:
'use strict'
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
const firebaseTriggers = functions.region('europe-west1').firestore;
const db = admin.firestore();
exports.postNotification = firebaseTriggers
.document('/post notifications/{notificatioId}').onWrite((snap, context) => {
const notifcationRecieverId = snap.data().mReciever;
const payload = {
data: {
notification_type: 'POST',
title: snap.data().mTitle,
body: snap.data().mDescription,
sender_id: snap.data().mSender,
reciever_id: snap.data().mReciever,
notification_id: context.params.notificatioId
}
};
return db.collection('dog owners')
.document(notifcationRecieverId)
.get()
.then(recieverDoc => {
console.log('Retrieving FCM tokens');
const tokens = recieverDoc.data().mTokens;
console.log('Sending notification payload');
return admin.message().sendToDevice(tokens, payload);
});
});
Upong deployment, I'm getting the following error:
Can someone help me understand why?
Firstly you have got space in your colleciton name. This is bad convetion.
post notifications => postNotifications

How to send emails that loop through arrays from firestore database

I'm trying to send a user receipt from an eCommerce store. How do I loop through the data to send
I have tried using [] on the dynamic data.
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
const db = admin.firestore();
// Sendgrid Config
import * as sgMail from "#sendgrid/mail";
const API_KEY = functions.config().sendgrid.key;
const TEMPLATE_ID = functions.config().sendgrid.template;
sgMail.setApiKey(API_KEY);
//FUNCTIONS
export const newOrder = functions.firestore
.document("checkout/{checkoutId}/products/{productId}")
.onCreate(async (change, context) => {
// Read booking document
const postSnap = await db
.collection("checkout/{checkoutId}/products")
.doc(context.params.productId)
.get();
const booking = postSnap.data() || {};
//Email
const msg = {
to: "wilmutsami#gmail.com",
from: "test#example.com",
templateId: TEMPLATE_ID,
dynamic_template_data: {
subject: "Hey there, thank you for your order!",
name: booking.name,
amount: booking.amount
}
};
//Send it
return sgMail.send(msg);
});
Expected results are an email to the user displays a table of items that you ordered
If you want to get the data of the document that triggered the Cloud Function at checkout/{checkoutId}/products/{productId} you don't need to do
await db
.collection("checkout/{checkoutId}/products")
.doc(context.params.productId)
.get();
As explained in the doc:
When a function is triggered, it provides a snapshot of the data
related to the event. You can use this snapshot to read from or write
to the document that triggered the event, or use the Firebase Admin
SDK to access other parts of your database.
You can easily get the values of the document fields through the snap DocumentSnapshot as follows:
export const newOrder = functions.firestore
.document("checkout/{checkoutId}/products/{productId}")
.onCreate(async (snap, context) => {
const docData = snap.data();
const name = docData.name;
const amount = docData.amount;
// You can then use those values in the rest of your code
const msg = {
to: "wilmutsami#gmail.com",
from: "test#example.com",
templateId: TEMPLATE_ID,
dynamic_template_data: {
subject: "Hey there, thank you for your order!",
name: name,
amount: amount
}
};
return sgMail.send(msg);
});

Promise not returning value on request

I have been trying to get this to work, but am new to NodeJS. I suspect the issue is due to async, but am not familiar with how it works.
The idea behind this code is that it monitors a firebase database change and sends an email to the users. I am getting everything from the change snapshot, and using the values to check another table for user data. The request is not returning before the email gets sent and I am unsure why.
Edit I should specify that the email function sgMail is firing off before I get the results from the requests. I've tried putting a delay, but I am still not getting the result to return in time.
Here's my index.js
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
var requestify = require('requestify');
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database.ref('Order/{orderID}')
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = 'https://shlep-me-f516e.firebaseio.com/User/'+shipperInfo+'.json';
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
});
function getTravelerData() {
return new Promise(resolve => {
requestify.get('https://shlep-me-f516e.firebaseio.com/User/' + travelerInfo + '.json')
.then(function (response) {
resolve(response.getBody())
});
});
}
var TravelD = getTravelerData();
//Send an email
const msg = {
to: 'andrew#shlepme.com',
from: 'support#shlepme.com',
subject: 'New Follower',
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: 'd1ccfeb9-2e2d-4979-a3ca-c53975fe486e',
substitutionWrappers: ['%', '%'],
substitutions: {
'%shipper_name%': "Test",
'traveler_name': TravelD.name
// and other custom properties here
}
};
console.log('Sending email');
console.log(TravelD);
return sgMail.send(msg)
});
Any ideas? I have been trying to figure this out.
It seems that you need to understand about Promises first.
When you start using promises you will need to ALWAYS use them and chain one with the other.
So I would rewrite your code like this: (not tested)
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require("firebase-functions");
var requestify = require("requestify");
//SendGrid
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require("#sendgrid/mail");
sgMail.setApiKey(SENDGRID_API_KEY);
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.packingListEmail = functions.database
.ref("Order/{orderID}")
.onUpdate(event => {
// Grab the current value of what was written to the Realtime Database.
const eventSnapshot = event.data;
//Here You can get value through key
var shipperInfo = eventSnapshot.child("fk_shipper_id").val();
var travelerInfo = eventSnapshot.child("fk_traveler_id").val();
//Print value of string
console.log(shipperInfo);
//Get Shipper Info
const shipperPath = "https://shlep-me-f516e.firebaseio.com/User/" + shipperInfo + ".json";
requestify.get(shipperPath)
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
var shipperResult = response.getBody();
console.log(shipperResult.email);
return shipperResult;
})
.then(function (shipperResult) {
//Send an email
const msg = {
to: "andrew#shlepme.com",
from: "support#shlepme.com",
subject: "New Follower",
// text: `Hey ${toName}. You have a new follower!!! `,
// html: `<strong>Hey ${toName}. You have a new follower!!!</strong>`,
// custom templates
templateId: "d1ccfeb9-2e2d-4979-a3ca-c53975fe486e",
substitutionWrappers: ["%", "%"],
substitutions: {
"%shipper_name%": "Test",
traveler_name: shipperResult.name
// and other custom properties here
}
};
console.log("Sending email");
console.log(shipperResult);
return sgMail.send(msg);
});
});

Resources