Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?

Google Apps Script Gmail Utilities

##sendAndLabel(recipient, subject, body, options, label)##

An alternative to GmailApp.sendEmail(), which applies a label to the message thread in the sender's account.

Sends an email message with optional arguments. The email can contain plain text or an HTML body. The size of the email (including headers, but excluding attachments) may not exceed 20KB.

Original StackOverflow question: How to create a Gmail message with a specific label?

###Parameters###

Name Type Description
recipient String the addresses of the recipients
subject String the subject line
body String the body of the email
options Object a JavaScript object that specifies advanced parameters, as documented for GmailApp.sendEmail()
label String the label to be applied

Details

The Google Apps Script GmailApp API does not give a way to add a label to a message as you send it, something you can do from the UI. This appears as Issue 1859 in the issue tracker, visit and star it for updates.

A work-around would be to take advantage of the thread apis. If the message is blind-copied (bcc'd) to the sender, it will also appear as a new message in their inbox, where we can use getInboxThreads() to find it. Add the label to this thread, and optionally mark it read then archive it.

##inline(htmlWstyle)##

Convert html containing <style> tags to instead have inline css.

This example uses an online service from MailChimp Labs, but the same principle could be used to leverage several other online providers.

Strictly speaking, this isn't not a gmail utility, but since it's so useful for producing cross-client compatible emails, here it is!

Original StackOverflow question: How do I use Google Apps Script to change a CSS page to one with inline styles?

###Parameters###

Name Type Description
htmlWstyle String A block of HTML text including <style>..</style> tags.

Details

There are numerous online tools that do this conversion, so you could leverage one of them from Google Apps Script. (If you only need to do this once in a while, why not just use one of those services?)

This utility builds on the getElementByVal() function from this answer.

There may appear to be some black magic in there. A previous answer described how to use the old Xml Service to examine the structure of a web page to find the bits you wanted. It's still good reading, but that service is now gone, and the new XmlService doesn't have equivalent exploratory support.

To start, we found a web service that did the job we were interested in, and used the UrlFetch Service to simulate a person pasting code into the service. Ideally, we'd like one that returned just the result we wanted, in a format we could use without any further work. Alas, what we had was a complete web page, and that meant that we'd have to farm it for our result. Basic idea there: Use the XmlService to parse and explore the page, extracting just the bit we wanted.

In the Css Inline service that was selected, Chrome's ability to "Inspect Element" was used to determine the tag type (<textarea>) and a way to uniquely identify it (name="text"). Armed with that knowledge, we had everything we needed to use getElementByVal() to dig through the HTML returned from a POST request. (Alternatively, you could use String methods to find and extract the text you want.)

But when that was all put together, XmlService kept complaining about the format of the HTML in the result page - so JavaScript String & RegExp methods were used to balance the malformed tags before passing the page on.

Example

Here's a simple example illustrating use of the inline() function. Note that style information is absorbed from both the external css link AND the tagged styling.

function test_inline() {
  var myHtml =
    '<html>'
  +   '<head>'
  +     '<title>Example</title>'
  +     '<link rel="stylesheet" href="http://inlinestyler.torchboxapps.com/static/css/example.css" ></link>'
  +   '</head>'
  +   '<body>'
  +     '<style type="text/css">'
  +        'h1{'
  +             'color:yellow'
  +        '}'
  +     '</style>'
  +     '<h1>An example title</h1>'
  +     '<p>Paragraph 1</p>'
  +     '<p class="p2">Paragraph 2</p>'
  +   '</body>'
  + '</html>';
  
  var inlined = inline(myHtml);
  debugger;  // pause in debugger, have a look at "inlined"
}

Result

  <html>
    <head>
      <title>Example</title>
    </head>
    <body>
      <h1 style="color: yellow;">An example title</h1>
      <p style="margin: 0;padding: 0 0 10px 0;">Paragraph 1</p>
      <p class="p2" style="margin: 0;padding: 0 0 10px 0;">Paragraph 2</p>
    </body>
  </html>

GMail Service OAuth2 functions

Simplified OAuth2 authorization for Google Apps Script use of the GMail API. Fair warning: while this is an illustration of how to do OAuth2 authentication, it's not really necessary since the addition of ScriptApp.getOAuthToken().

  • Requires OAuth2 for Apps Script, library ID MswhXl8fVhTFUH_Q3UOJbXvxhMjh3Sh48

  • See OAuth2 for Apps Script for details including installation of the library.

Usage:

  1. Set your project-specific values in an "authorization profile" object, e.g.

    var gmailAuthProfile = {
      // Get the client ID and secret from the Google Developers Console.
      clientId : '--redacted--.apps.googleusercontent.com',
      clientSecret : '--redacted--',
    
      // Set the project key of the script using the service.
      projectKey : '--redacted--'
    }
    
  2. Authorize. See comments on doGet().

  3. Use getGmailService().getAccessToken() as authorization bearer for calls to gmail API.

Send Draft Emails

Using the GMail service provided by Google Apps Script you can script many GMail capabilities. One thing you cannot do is directly send existing draft messages.

This script contains a few utilities (maybe the foundation of a library, someday) that deal with draft emails, including using the GMail API to retrieve and send draft messages.

No more need to resort to generating a whole new copy of a draft, send the copy, delete the draft... instead:

sendDraftMsg( msgId );  // Will find the draft with this msgId, and send it

This will preserve all the attributes of the draft email... To:, From:, CC: and BCC: lists, labels, inline images, attachments, and formatting.

###sendDayOldDrafts()

Written for StackOverflow question How to send a draft email using google apps script. Sends all draft emails labeled "send-tomorrow".

A simple example of how to use sendDraftMsg().

sendDraftMsg( msgId )

Sends a draft message that matches the given message ID.

Parameters

NameTypeDescription
messageId String Immutable Gmail Message ID to send
subject String the subject line

Returns

TypeDescription
Object Response object if successful, see https://developers.google.com/gmail/api/v1/reference/users/drafts/send#response

getDrafts()

Gets the current user's draft messages.

Returns

TypeDescription
Object[] If successful, returns an array of `Users.drafts` resources. (https://developers.google.com/gmail/api/v1/reference/users/drafts)

getDraftId( messageId )

Gets the draft message ID that corresponds to a given Gmail Message ID. Parameters

NameTypeDescription
messageId String Immutable Gmail Message ID to search for

Returns

TypeDescription
String Immutable Gmail Draft ID, or `null` if not found

getDraftMsg( messageId, optFormat )

Gets the draft message content that corresponds to a given Gmail Message ID.

Parameters

NameTypeDescription
messageId String Immutable Gmail Message ID to search for
optFormat String Optional format; `'object'` (default) or `'json'`

Returns

TypeDescription
Object or String If successful, returns a `Users.drafts` resource. (https://developers.google.com/gmail/api/v1/reference/users/drafts)

Label a message

Using the GMail service provided by Google Apps Script you can script many GMail capabilities. One thing you cannot do is label messages, you can only label threads. These utilities provide the ability to modify the labels on a single message, by use of the GMail API.

See labelMessage.gs for code.

Example of use:

function test_modifyMessage() {
  var firstThread = GmailApp.getInboxThreads(0,1)[0];
  var message = firstThread.getMessages()[0];
  var messageId = message.getId();
  
  modifyMessage('me', messageId, ['stack','overflow']);       // add two labels
  modifyMessage('me', messageId, null, ['stack','overflow']); // remove two labels
}

###modifyMessage()

Written for StackOverflow question Add label to specific message, not the thread. Adds or removes Labels on an individual message.

Parameters

NameTypeDescription
userId String User's email address. The special value 'me' can be used to indicate the authenticated user.
messageId String ID of Message to modify
labelsToAdd String[] Array of Label names to add
labelsToRemove String[] Array of Label names to remove

Returns

TypeDescription
Object Users.messages resource, see https://developers.google.com/gmail/api/v1/reference/users/messages#resource

getLabelId()

Get the Label ID for the given labelName. If Label isn't found, it will be created depending on the state of ok2Create.

Parameters

NameTypeDescription
labelName String Name of label to retrieve ID for.
ok2Create Boolean (optional) Set true if a label should be created when not found. Default is true.

Returns

TypeDescription
String ID of Label, or null if not found or created.

createLabel()

Create Label for given labelName. Parameters

NameTypeDescription
labelName String Name of label to create.

Returns

TypeDescription
String ID of Label.
/********************************************************************************
* GMail Service OAuth2 functions
*
* Adapted from Eric Koleda's Drive Service example.
*
* Requires OAuth2 for Apps Script, library ID MswhXl8fVhTFUH_Q3UOJbXvxhMjh3Sh48
* See https://github.com/googlesamples/apps-script-oauth2
*
* Usage:
*
* 1. Set your project-specific values in an "authorization profile"
* object, e.g.
*
* var gmailAuthProfile = {
* // Get the client ID and secret from the Google Developers Console.
* clientId : '--redacted--.apps.googleusercontent.com',
* clientSecret : '--redacted--',
*
* // Set the project key of the script using the service.
* projectKey : '--redacted--'
* }
*
* 2. Authorize. See comments on doGet().
*
* 3. Use getGmailService().getAccessToken() as authorization bearer
* for calls to gmail API.
*
*******************************************************************************/
/**
* Remove authorization for the service. (e.g. to re-authorize)
*/
function resetGmailService() {
var gmailService = getGmailService(gmailAuthProfile);
gmailService.reset();
}
/**
* Initialize and / or get OAuth2 token for GMail API.
*
* @param {Objectr} authProfile Object containing clientId, clientSecret
* & projectKey for authorization
*/
function getGmailService(authProfile) {
if (!authProfile) throw new Error( 'Require authProfile' );
// Create a new service with the given name. The name will be used when persisting the
// authorized token, so ensure it is unique within the scope of the property store.
return OAuth2.createService('gmail')
// Set the endpoint URLs, which are the same for all Google services.
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the client ID and secret, from the Google Developers Console.
.setClientId(authProfile.clientId)
.setClientSecret(authProfile.clientSecret)
// Set the project key of the script using this library.
.setProjectKey(authProfile.projectKey)
// Set the name of the callback function in the script referenced above that should be
// invoked to complete the OAuth flow.
.setCallbackFunction('authCallback_')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
// Set the scopes to request (space-separated for Google services).
.setScope( ['https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.compose',
'https://www.googleapis.com/auth/gmail.readonly'])
// Set any additional OAuth2 parameters, in this case requesting offline access and
// forcing the approval prompt to be shown.
.setParam('access_type', 'offline')
//.setParam('approval_prompt', 'force');
}
/**
* To complete authorization, script must have a UI. For a stand-alone script,
* publish as a web app. The doGet() and authCallback() functions are then available
* to the user for authentication at the published (or dev) URL.
*/
function doGet() {
var gmailService = getGmailService(gmailAuthProfile);
if (!gmailService.hasAccess()) {
var authorizationUrl = gmailService.getAuthorizationUrl();
var template = HtmlService.createTemplate(
'<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. ' +
'Refresh the page when authorization complete.');
template.authorizationUrl = authorizationUrl;
var page = template.evaluate();
return page;
} else {
return HtmlService.createHtmlOutput("Authorization completed.")
.setSandboxMode(HtmlService.SandboxMode.NATIVE);
}
}
/**
* This function is called to provide feedback to the user attempting
* to grant authorization.
* See OAuth2.createService().setCallbackFunction('authCallback')
*/
function authCallback_(request) {
var gmailService = getGmailService(gmailAuthProfile);
var isAuthorized = gmailService.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
/**
* Convert html containing <style> tags to instead have inline css.
*
* This example uses an online service from MailChimp Labs, but
* the same principle could be used to leverage several other
* online providers.
*
* @param {Text} htmlWstyle A block of HTML text including
* <style>..</style> tags.
*
* @returns {Text} Same HTML, converted to inline css.
*/
function inline(htmlWstyle) {
// Generate a POST request to inline css web tool.
var payload =
{
"html" : htmlWstyle,
"strip" : "checked"
};
// Because payload is a JavaScript object, it will be interpreted as
// an HTML form. (We do not need to specify contentType; it will
// automatically default to either 'application/x-www-form-urlencoded'
// or 'multipart/form-data')
var options =
{
"method" : "post",
"payload" : payload,
"muteHttpExceptions" : true
};
var url = "http://beaker.mailchimp.com/inline-css";
var response = UrlFetchApp.fetch(url, options);
// The html from this service is non-compliant, so we need
// to massage it to satisfy the XmlService.
var badlink = new RegExp('<link (.*?)[\/]*>',"igm");
var badmeta = new RegExp('<meta (.*?)[\/]*>',"igm");
var badinput = new RegExp('<input (.*?)[\/]*>',"igm");
var xml = response.getContentText()
.replace(badlink,"<link $1></link>" )
.replace(badinput,"<input $1></input>" )
.replace(badmeta,"<meta $1></meta>" )
.replace(/<br>/g,"<br/>");
// So far, so good! Next, extract converted text from page. <textarea name="text" ...>
// Put the receieved xml response into XMLdocument format
var doc = XmlService.parse(xml);
var inlineHTML = getElementByVal( doc, 'textarea', 'name', 'text' );
return (inlineHTML == null) ? null : inlineHTML.getValue();
}
/**
* Modify the Labels a Message is associated with.
* Throws if unsuccessful.
* see https://developers.google.com/gmail/api/v1/reference/users/messages/modify
*
* @param {String} userId User's email address. The special value 'me'
* can be used to indicate the authenticated user.
* @param {String} messageId ID of Message to modify.
* @param {String} labelsToAdd Array of Label names to add.
* @param {String} labelsToRemove Array of Label names to remove.
*
* @returns {Object} Users.messages resource, see reference.
*/
function modifyMessage(userId, messageId, labelsToAdd, labelsToRemove) {
labelsToAdd = labelsToAdd || [];
labelsToRemove = labelsToRemove || [];
// see https://developers.google.com/gmail/api/v1/reference/users/messages/modify
var url = 'https://www.googleapis.com/gmail/v1/users/${userId}/messages/${id}/modify'
.replace("${userId}","me")
.replace("${id}", messageId );
var headers = {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
};
var addLabelIds = [];
for (var i=0; i<labelsToAdd.length; i++) {
addLabelIds[i] = getLabelId( labelsToAdd[i] );
}
var removeLabelIds = [];
for (var i=0; i<labelsToRemove.length; i++) {
removeLabelIds[i] = getLabelId( labelsToRemove[i], false );
}
var request = {
'addLabelIds': addLabelIds,
'removeLabelIds': removeLabelIds
};
var params = {
method: "post",
contentType: "application/json",
headers: headers,
payload: JSON.stringify(request),
muteHttpExceptions: true
};
//var check = UrlFetchApp.getRequest(url, params); // for debugging
var response = UrlFetchApp.fetch(url, params);
var result = response.getResponseCode();
if (result == '200') { // OK
return JSON.parse(response.getContentText());
}
else {
// This is only needed when muteHttpExceptions == true
var err = JSON.parse(response.getContentText());
throw new Error( 'Error (' + result + ") " + err.error.message );
}
}
/**
* Get the Label ID for the given LabelName. If Label isn't found, it will be created
* depending on the state of ok2Create.
* Throws if unsuccessful.
* See https://developers.google.com/gmail/api/v1/reference/users/messages/modify.
*
* @param {String} labelName Immutable Gmail Message ID to modify
* @param {Boolean} ok2Create (optional) Set true if a label should be created when not found.
* Default is true.
*
* @returns {String} ID of Label, or null if not found or created.
*/
function getLabelId( labelName, ok2Create ) {
if (typeof ok2Create == 'undefined') ok2Create = true;
var id = null;
// see https://developers.google.com/gmail/api/v1/reference/users/labels/list
var url = 'https://www.googleapis.com/gmail/v1/users/${userId}/labels'
.replace("${userId}","me") // The user's email address. The special value me can be used to indicate the authenticated user.
var headers = {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
};
var params = {
method: "get",
contentType: "application/json",
headers: headers,
muteHttpExceptions: true
};
//var check = UrlFetchApp.getRequest(url, params); // for debugging
var response = UrlFetchApp.fetch(url, params);
var result = response.getResponseCode();
if (result == '200') { // OK
var labels = JSON.parse(response.getContentText()).labels;
var found = false;
for (var i=0; i<labels.length & !found; i++) {
if (labels[i].name == labelName) {
found = true;
id = labels[i].id;
}
}
if (!found && ok2Create) {
id = createLabel( labelName );
}
return id;
}
else {
// This is only needed when muteHttpExceptions == true
var err = JSON.parse(response.getContentText());
throw new Error( 'Error (' + result + ") " + err.error.message );
}
}
/**
* Create Label given LabelName.
* Throws if unsuccessful.
* See https://developers.google.com/gmail/api/v1/reference/users/messages/modify.
*
* @param {String} labelName Immutable Gmail Message ID to modify
*
* @returns {String} ID of Label.
*/
function createLabel( labelName ) {
var id = null;
// see https://developers.google.com/gmail/api/v1/reference/users/labels/create
var url = 'https://www.googleapis.com/gmail/v1/users/${userId}/labels'
.replace("${userId}","me") // The user's email address. The special value me can be used to indicate the authenticated user.
var headers = {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
};
var request = {
'name': labelName
};
var params = {
method: "post",
contentType: "application/json",
headers: headers,
payload: JSON.stringify(request),
muteHttpExceptions: true
};
//var check = UrlFetchApp.getRequest(url, params); // for debugging
var response = UrlFetchApp.fetch(url, params);
var result = response.getResponseCode();
if (result == '200') { // OK
var label = JSON.parse(response.getContentText());
id = label.id;
return id;
}
else {
// This is only needed when muteHttpExceptions == true
var err = JSON.parse(response.getContentText());
throw new Error( 'Error (' + result + ") " + err.error.message );
}
}
function test_modifyMessage() {
// A very basic test; grab most recent message, then add & remove labels.
var firstThread = GmailApp.getInboxThreads(0,1)[0];
var message = firstThread.getMessages()[0];
var messageId = message.getId();
modifyMessage('me', messageId, ['stack','overflow']); // add two labels
debugger; // Validate labels are visible in GMail. May need to refresh GMail.
modifyMessage('me', messageId, null, ['stack','overflow']); // remove two labels
debugger; // Validate labels have been removed from the message.
}
/**
* An alternative to GmailApp.sendEmail(), which applies a
* label to the message thread in the sender's account.
*
* Sends an email message with optional arguments. The email can
* contain plain text or an HTML body. The size of the email
* (including headers, but excluding attachments) may not
* exceed 20KB.
*
* @copyright (c) 2013 Mogsdad
* @license MIT
*
* @param {String} recipient the addresses of the recipient
* @param {String} subject the subject line
* @param {String} body the body of the email
* @param {Object} options a JavaScript object that specifies
* advanced parameters, as documented
* for GmailApp.sendEmail()
* @param {String} label the label to be applied
*/
function sendAndLabel(recipient, subject, body, options, label) {
var sender = Session.getActiveUser().getEmail();
// Add sender to bcc list
if (options.bcc) {
options.bcc = options.bcc.split(',').concat(sender).join(',');
}
else {
options.bcc = sender;
}
GmailApp.sendEmail( recipient, subject, body, options )
// Check if label already exists, create if it doesn't
var newLabel = GmailApp.getUserLabelByName(label);
if (!newLabel) newLabel = GmailApp.createLabel(label);
// Look for our new message in inbox threads
Utilities.sleep(2000); // Wait for message to be received
var inboxThreads = GmailApp.getInboxThreads();
for (var t = 0; t < inboxThreads.length; t++) {
var foundSubject = inboxThreads[t].getFirstMessageSubject();
var numLabels = inboxThreads[t].getLabels().length; // Could add more criteria
if (foundSubject === subject && numLabels === 0) {
// Found our thread
inboxThreads[t].addLabel(newLabel)
.markRead()
.moveToArchive();
break;
}
}
}
function test_sendAndLabel() {
var recipient = Session.getActiveUser().getEmail();
var subject = "Test Labelling";
var body = "This email is testing message labelling.";
// var options = {bcc:"someone@example.com"};
var options = {};
var label = "LabelTest";
sendAndLabel(recipient, subject, body, options, label);
}
/*
* Send all drafts labeled "send-tomorrow".
*/
function sendDayOldDrafts() {
var threads = GmailApp.search('in:draft label:send-tomorrow');
for (var i=0; i<threads.length; i++) {
var msgId = threads[0].getMessages()[0].getId();
sendDraftMsg( msgId );
}
}
/**
* Sends a draft message that matches the given message ID.
* Throws if unsuccessful.
* See https://developers.google.com/gmail/api/v1/reference/users/drafts/send.
*
* @param {String} messageId Immutable Gmail Message ID to send
*
* @returns {Object} Response object if successful, see
* https://developers.google.com/gmail/api/v1/reference/users/drafts/send#response
*/
function sendDraftMsg( msgId ) {
// Get draft message.
var draftMsg = getDraftMsg(msgId,"json");
if (!getDraftMsg(msgId)) throw new Error( "Unable to get draft with msgId '"+msgId+"'" );
// see https://developers.google.com/gmail/api/v1/reference/users/drafts/send
var url = 'https://www.googleapis.com/gmail/v1/users/me/drafts/send'
var headers = {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
};
var params = {
method: "post",
contentType: "application/json",
headers: headers,
muteHttpExceptions: true,
payload: JSON.stringify(draftMsg)
};
var check = UrlFetchApp.getRequest(url, params)
var response = UrlFetchApp.fetch(url, params);
var result = response.getResponseCode();
if (result == '200') { // OK
return JSON.parse(response.getContentText());
}
else {
// This is only needed when muteHttpExceptions == true
var err = JSON.parse(response.getContentText());
throw new Error( 'Error (' + result + ") " + err.error.message );
}
}
/**
* Gets the current user's draft messages.
* Throws if unsuccessful.
* See https://developers.google.com/gmail/api/v1/reference/users/drafts/list.
*
* @returns {Object[]} If successful, returns an array of
* Users.drafts resources.
*/
function getDrafts() {
var url = 'https://www.googleapis.com/gmail/v1/users/me/drafts';
var headers = {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
};
var params = {
headers: headers,
muteHttpExceptions: true
};
var check = UrlFetchApp.getRequest(url, params)
var response = UrlFetchApp.fetch(url, params);
var result = response.getResponseCode();
if (result == '200') { // OK
return JSON.parse(response.getContentText()).drafts;
}
else {
// This is only needed when muteHttpExceptions == true
var error = JSON.parse(response.getContentText());
throw new Error( 'Error (' + result + ") " + error.message );
}
}
/**
* Gets the draft message ID that corresponds to a given Gmail Message ID.
*
* @param {String} messageId Immutable Gmail Message ID to search for
*
* @returns {String} Immutable Gmail Draft ID, or null if not found
*/
function getDraftId( messageId ) {
if (messageId) {
var drafts = getDrafts();
for (var i=0; i<drafts.length; i++) {
if (drafts[i].message.id === messageId) {
return drafts[i].id;
}
}
}
// Didn't find the requested message
return null;
}
/**
* Gets the draft message content that corresponds to a given Gmail Message ID.
* Throws if unsuccessful.
* See https://developers.google.com/gmail/api/v1/reference/users/drafts/get.
*
* @param {String} messageId Immutable Gmail Message ID to search for
* @param {String} optFormat Optional format; "object" (default) or "json"
*
* @returns {Object or String} If successful, returns a Users.drafts resource.
*/
function getDraftMsg( messageId, optFormat ) {
var draftId = getDraftId( messageId );
var url = 'https://www.googleapis.com/gmail/v1/users/me/drafts'+"/"+draftId;
var headers = {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken()
};
var params = {
headers: headers,
muteHttpExceptions: true
};
var check = UrlFetchApp.getRequest(url, params)
var response = UrlFetchApp.fetch(url, params);
var result = response.getResponseCode();
if (result == '200') { // OK
if (optFormat && optFormat == "JSON") {
return response.getContentText();
}
else {
return JSON.parse(response.getContentText());
}
}
else {
// This is only needed when muteHttpExceptions == true
var error = JSON.parse(response.getContentText());
throw new Error( 'Error (' + result + ") " + error.message );
}
}

ldeck commented Jun 22, 2016

Shouldn't sendDayOldDrafts() refer to threads[i] rather than threads[0]?

foice commented Apr 8, 2017

How much API quota uses this script to send an email?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment