I am working with Google apps script and would like to create a script which picks up mail from the drafts and sends them if they have label "send-tomorrow". Finding drafts with a certain label is pretty simple:
var threads = GmailApp.search('in:draft label:send-tomorrow');
However I don't see an API to send the message! The only option I see is to: - open the message - extract body/attachments/title/from/to/cc/bcc - send a new message with the above params - destroy the previous draft
which seems pretty annoying and I'm not sure would work well with embedded images, multiple attachments etc...
any hint?
The only option I see is to: - open the message - extract body/attachments/title/from/to/cc/bcc - send a new message with the above params - destroy the previous draft
This is the exact topic of this blog by Amit Agarawal. His script does just what you describe, but doesn't handle inline images. For those, you can adapt the code from this article.
But you're right - what's the point of even having a draft message if you can't just send the stupid thing?!
We can use the GMail API Users.drafts: send from Google Apps Script to send a draft. The following stand-alone script does that, and handles the necessary authorization.
The full script is available in this gist.
/*
* 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 );
}
}
To use Google's APIs, we need to have an OAuth2 token for the current user - just as we do for Advanced Services. This is done using ScriptApp.getOAuthToken()
.
After copying the code to your own script, open Resources -> Advanced Google Services, open the link for the Google Developers Console, and enable the Gmail API for your project.
As long as the script contains at least one GMailApp method that requires user authority, the authentication scope will be set properly for the OAuthToken. In this example, that's taken care of by GmailApp.search()
in sendDayOldDrafts()
; but for insurance you could include a non-reachable function call directly in the functions using the API.
I did it using the GmailMessage.forward method.
It works with upload images and attachments, but I had to set the subject to avoid the prefix "Fwd:", and the user name because it only displayed the user email to the recipients.
I didn't find a way to dispose the draft, so I just remove the label to prevent sending it again.
Script:
function getUserFullName(){
var email = Session.getActiveUser().getEmail();
var contact = ContactsApp.getContact(email);
return contact.getFullName();
}
function testSendTomorrow(){
var threads = GmailApp.search('in:draft label:send-tomorrow');
if(threads.length == 0){
return;
}
var labelSendTomorrow = GmailApp.getUserLabelByName("send-tomorrow");
for(var i = 0; i < threads.length; i++){
var messages = threads[i].getMessages();
for(var j = 0; j < messages.length; j++){
var mssg = messages[j];
if(mssg.isDraft()){
mssg.forward(mssg.getTo(), {
cc: mssg.getCc(),
bcc: mssg.getBcc(),
subject: mssg.getSubject(),
name: getUserFullName()
});
}
}
threads[i].removeLabel(labelSendTomorrow);
}
}
You can search through all drafts and then send that specific draft no problem.
function sendMessage(id){
GmailApp.getDrafts().forEach(function (draft) {
mes = draft.getMessage()
if (mes.getId() == id) {
draft.send()
}
})
}
First, GmailDraft
now has a send()
function you can call directly. See: https://developers.google.com/apps-script/reference/gmail/gmail-draft#send()
Their code sample:
var draft = GmailApp.getDrafts()[0]; // The first draft message in the drafts folder
var msg = draft.send(); // Send it
Logger.log(msg.getDate()); // Should be approximately the current timestamp
Second, may not even need it now that google has released scheduled sending.
Send
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With