š®`MMailApp` for Google Apps Script: GmailApp+MailApp+Gmail API
This article describes the MmailApp library created for Google Apps Scripts. Please see the library documentation here:
The library lets you send emails, using MailApp, GmailApp, and Gmail API. The library takes care of parameters, so you do not need to care about which method to use.
Problem
I had trouble with my project when I wanted to automatically send emails and suddenly for me weāve reached the daily limit. This is why Iāve decided to make some research and be sure fewer bad things happen in the future.
MailApp OR GmailApp?
Apps Script native methods for sending emails are:
MailApp.sendEmail(...)
GmailApp.sendEmail(...)
They are mostly identical, but GmailApp has an option āfromā. This option is available for paid Google accounts and lets you to send emails from one of your aliases:
var aliases = GmailApp.getAliases();
Youād say then, letās always use āGmailAppā. But, thereās an issue with emojis.
šEmojis
If sending an email with emoji in a subject or body, it may show ugly question marks instead. This issue happens when you use GmailApp:
GmailApp.sendEmail(
'test.test@test.com',
'šYammy',
'šEat my cake!'
);/* =>
ļæ½ļæ½ļæ½ļæ½ļæ½ļæ½Yammy ļæ½ļæ½ļæ½ļæ½ļæ½ļæ½Eat my cake!
*/
ļæ½ļæ½ļæ½ļæ½ļæ½ļæ½ is not yummy at all! Thatās why you may want to use MailApp instead.
Now you may agree, that we always should use MailApp but use GmailApp in case we want to send emails from your aliases. Thatās good until you reach the daily email limit and you want to send more.
Quota
You can send ~100 emails/day and ~1500 for paid accounts. This quota has changed and may change in the future. The latest official quotas are here.
According to this source, the quota is for 24 hours:
If you have a quota of 1500/day, and start sending mails at around 3pm and then exhaust that quota at 10pm, you can expect the quota will refresh sometime around 3pm the next day.
You may get and log your daily quota with this code:
/**
* Get and log your daily mail quota
*/
function getRemainingMailQuota() {
var quota = MailApp.getRemainingDailyQuota();
console.log('You can send ~ ' + quota + ' emails this day.');
return quota;
}
My test showed that the use of both āMailAppā and āGmailAppā reduced my remaining quota. If you want to increase the quota, you may try paid account. And even this is not the guarantee:
After you convert from a free trial account to a paid subscription, your account limits automatically increase when both of the following are true:
* Your domain has cumulatively paid at least USD $100 (or equivalent).
* At least 60 days have passed since reaching that payment threshold.
One way out of this issue is to try an external API and use Apps Script to launch it. I suggest trying Gmail API before you start with other vendors like Amazon SES.
Gmail API
Whatās the limit for the use of Gmail API? I donāt know, really. I cannot believe my calculations. According to this:
You have 1,000,000,000 quota units per day.
messages.send = takes 100 units
ā Does this mean one can send 10,000,000 emails per day? Letās consider also this limit:
Per User Rate Limit 250 quota units per user per second
If I send 2 emails per second, it would be 24*60*60*2 = 172,800 emails per day. Still much better than 1500. Iām not sure about that. Anyways Iāve checked that using Gmail API does not change my daily quota calculated earlier. And it seems to be free.
Ok, how to use Gmail API in Apps Script?
The code sample of this was provided by š¦øTanaike in this report. Iāve taken his code as a starting point.
There are reasons why I did not want to use Gmail API:
- Installation is hard. You have to install the Gmail service into the script, and you may also need to install the API as an Advanced Service.
- Code is hard. If you see Tanaikeās report, you may notice itās not easy to understand the correct syntax.
- Out of Control. I found no way to programmatically indicate what is my remaining quota for Gmail API.
This is why my logic is to try simple methods first: āMailAppā or āGmailAppā. And then if I reach the limit, use Gmail API.
As Iāve said Gmail API uses code that is hard to implement. We are in Gmail API version 1 and it uses this:
raw RFC 2822
I had no idea what is that. RFC 2822 is an ancient standard, the document dated April 2001. RFC stands for āRequest For Commentsā and my guess is it was the easiest way to implement the API for Google engineers.
The heavy task was to understand the standard and find a way to use it in Apps Script. Luckily for me, there were many others who faced the same problem. Iāve found this code which helped me a lot. Hats off to š¦øContributors!
RFC 2822 standard was not easy to understand, but appeared you may discover your own message by selecting the āShow Originalā option in Gmail:
Open the email inside Gmail, go to 3-dot menu and choose Show Original. (source by š¦øAmit)
Hereās the code to do the same:
function getEmailRfc822() {
var emailId = '18312a64a8cf8746';
var rawContent = Gmail
.Users
.Messages
.get(
"me",
emailId,
{format: 'raw'}).raw;
var stringRfc822 = Utilities
.newBlob(
rawContent,
"message/rfc822").getDataAsString()
console.log(stringRfc822);
}
ā Thanks to this comment by š¦øRomain Vialard that helped me to figure out this problem. Note: if you struggle with finding the emailId, please use this code:
var messages = Gmail.Users.Messages.list(
"me", {q: "subject:Test Hatter", maxResults: 1}
).messages;
var emailId = messages[0].id;
console.log(emailId);
Solution
The idea was to have a simple way to send emails via Apps Script. Please think of this solution as one possible and indeed not a perfect one. Iāve decided to create a library called:
MMailApp
The method should look like this:
MMailApp.send(options);
I prefer a simple flat object for options:
{
to: 'test1@test.test,test2@test.test',
subject: 'š„øTest Tets',
htmlbody: '<b>šŖBold Hello!</b>'
}
ā In most cases, you may use JSON to pass the parameters. If you want to additionally use inline images or attachments, youāll have to use blobs. Hereās a sample code to get test parameters for blobs:
/**
* this function demonstrates
* how to get blobs by URL
* for your email
*/
function getTestBlobs_() {
var source = 'https://raw.githubusercontent.com/';
var options = {
urls: {
'coolTablesLogo':
source + 'cooltables/pics/main/logos/ct_logo_small.png',
'partyFace':
source + 'Max-Makhrov/myFiles/master/partyface.png'
},
html: "š inline CoolTables Logo<img src='cid:coolTablesLogo'> image! <br>" +
"Hoooorrraaay! <img src='cid:partyFace'>"
}var blobsArray = [], blobsObject = {};
var blob;
for (var k in options.urls) {
blob = UrlFetchApp
.fetch(options.urls[k])
.getBlob()
.setName(k);
blobsArray.push(blob);
blobsObject[k] = blob;
}return {
array: blobsArray, // for attachments
object: blobsObject, // for inline images
htmlBody: options.html
};
}
I also wanted to make different naming conventions possible:
"to" = "recipient" = "recipients"
"htmlbody" = "htmlBody" = "html_body"
etc.
Here are options possible to set:
var options = {
to: 'makhrov.max@gmail.com', // required
cc: 'max0637859167@gmail.com',
bcc: 'max0637859167@gmail.com,makhrov.max@gmail.com',
name: 'š©Mad Hatter',
from: 'max0637859167@gmail.com', // must owe alias
subject: 'š„øTest Test Test',
body: 'šŖ body!',
htmlBody: '<b>šøBold Hello</b><br>How are you?',
noReply: 1,
replyTo: 'test@test.test',
replyToName: 'š¦øStranger', // for Gmail API only
attachments: array, // [blob, blob]
inlineImages: object // {name: blob}
}
Now the solution is documented, weāre into one little problem to solve: write the code and create a library!
Gmail API Bonus
Learning how to use Gmail API was hard, but fun. I felt like I can control the process, and noticed some benefits.
When you send an email with Gmail API, youāve got a response like that:
{ "threadId":"18311c4fd396e1ad",
"id":"18311c4fd396e1ad",
"labelIds":["UNREAD","SENT","INBOX"]}
ā Notice the id here. It is the id of a message you send. What is interesting about Gmail is thereās a unique URL for each email. And ID is a part of the URL. This means the URL for that email is:
https://mail.google.com/mail/u/0/#inbox/18311c4fd396e1ad
You may use this URL to open your sent email.
Compatibility
The final task is to make the code compatible. Different methods may or may not work for all 3 methods:
GmailApp
MailApp
Gmail API
I needed to create a code that will launch each possibility for each method. Iāve compared the result for possible options to create a common code, and hereās the result:
ā In my final test message, Iāve tried all possibilities.
NoReply for Gmail API
Here are options that work differently:
noReply: 1, // will NOT work for Gmail API
from: test@mail.test // may only work if this email belongs to you
The solution Iāve used for a no-reply option is to offer a generic email address for the reply. This works only for Gmail API:
Reply-To: No Reply <noreply@test.test>
As you see, we also have a valid no-reply option now. And additionally, we may set a name to reply:
{
replyTo: test@mail.test,
replyToName: š·ļøPeter Parker // Hidden option! For Gmail API only
}
Final Words
Iāve finally resolved all the issues, and even had fun!
Thank you for reading to the end! Here again, is the link to the library:
Mmail App Library ā send emails from Apps Script.