Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send a draft email using google apps script

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?

like image 435
Simone Avogadro Avatar asked Nov 29 '14 20:11

Simone Avogadro


4 Answers

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.

Script

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 );
  }
}

Authorization

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.

like image 127
Mogsdad Avatar answered Nov 17 '22 02:11

Mogsdad


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);
  }
}
like image 24
DeiniGS Avatar answered Nov 17 '22 00:11

DeiniGS


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()
    }
  })
}
like image 40
wackazong Avatar answered Nov 17 '22 02:11

wackazong


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.

  1. Click the arrow next to Send

Click the down arrow next to send.

  1. Select your preferred time to send

Select your preferred time.

like image 34
evan Avatar answered Nov 17 '22 02:11

evan