如何使用谷歌应用程序脚本发送草稿电子邮件

时间:2014-11-29 20:10:50

标签: google-apps-script gmail gmail-api

我正在使用Google应用程序脚本,并希望创建一个脚本,从草稿中提取邮件并在标签为“明天发送”时发送。 查找带有特定标签的草稿非常简单:

 var threads = GmailApp.search('in:draft label:send-tomorrow');

但是我没有看到发送消息的API! 我看到的唯一选择是:   - 打开信息   - 提取身体/附件/标题/从/到/ cc / bcc   - 发送带有上述参数的新消息   - 破坏以前的草案

这看起来很烦人,而且我不确定它是否适用于嵌入式图像,多个附件等......

任何提示?

6 个答案:

答案 0 :(得分:10)

  

我看到的唯一选择是: - 打开邮件 - 提取正文/附件/ title / from / to / cc / bcc - 发送带有上述参数的新邮件 - 销毁之前的草稿

这是Amit Agarawal this blog的确切主题。他的脚本完全符合您的描述,但不处理内联图像。对于那些,您可以调整this article的代码。

但你是对的 - 如果你不能发送愚蠢的东西,即使有一条草稿信息是什么意思?!

我们可以使用Google Apps脚本中的GMail API Users.drafts: send发送草稿。以下独立脚本执行此操作,并处理必要的授权。

脚本

完整脚本位于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 );
  }
}

授权

要使用Google的API,我们需要为当前用户提供OAuth2令牌 - 就像我们为高级服务所做的那样。这是使用ScriptApp.getOAuthToken()完成的。

将代码复制到您自己的脚本后,打开Resources - &gt;高级Google服务,打开Goog​​le Developers Console的链接,并为您的项目启用Gmail API。

只要脚本包含至少一个需要用户权限的GMailApp方法,就会为OAuthToken正确设置身份验证范围。在此示例中,由GmailApp.search()中的sendDayOldDrafts()处理;但是对于保险,您可以使用API​​直接在函数中包含不可访问的函数调用。

答案 1 :(得分:4)

我是使用GmailMessage.forward方法完成的。

它适用于上传图片和附件,但我必须设置主题以避免前缀&#34; Fwd:&#34;和用户名,因为它只向收件人显示用户电子邮件。

我没有办法处理草稿,所以我只是删除标签以防止再次发送。

<强>脚本:

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

答案 2 :(得分:1)

您可以搜索所有草稿,然后毫无问题地发送该特定草稿。

function sendMessage(id){
  GmailApp.getDrafts().forEach(function (draft) {
    mes = draft.getMessage()
    if (mes.getId() == id) {
      draft.send()
    }
  })
}

答案 3 :(得分:0)

更简单的替代方法是使用gmail api而不是gmailApp:

col
上面的函数示例

https://script.google.com的gs脚本中使用。 它需要draftId(而不是消息Id),草稿将被发送。图像和附件都可以! 信息:https://developers.google.com/gmail/api/v1/reference/users/drafts/send

答案 4 :(得分:0)

我在这里很陌生,没有足够的“信誉”来发表评论,因此无法评论Mogsdad的原始答案,因此我必须创建一个新答案:

我已经调整了Mogsdad的解决方案,使其不仅支持全新消息,还支持答复/转发现有线程。

要在现有线程上使用它,应首先创建答复/转发,然后才标记该线程。我的代码还支持多个标签并设置这些标签。

我为此创建了一个新的要点,分叉了莫格斯达德(Mogsdad),https://gist.github.com/hadasfester/81bfc5668cb7b666b4fd6eeb6db804c3

我仍然需要在文档中添加一些屏幕快照链接,但否则可以使用了,我自己一直在使用它。希望您觉得有用。

也在此内联:

/**
 * This script allows you to mark threads/drafts with a predetermined label and have them get sent the next time your trigger
 * sets off. 
 * 
 * Setup instructions:
 * 1. Make a copy of this script (File -> Make a copy)
 * 2. Follow the "Authorization" instructions on https://stackoverflow.com/a/27215474. (If later during setup/testing you get
 *    another permissions approval dialog, approve there as well).
 * 2. I created two default labels, you can edit/add your own. See "TODO(user):" below. After that, to create them in gmail,
 *    choose "setUpLabel" function above and click the play button (TODO: screenshot). Refresh your gmail tab, you should see
 *    the new labels.
 * 3. Click the clock icon above (TODO: screenshot) and set time triggers, e.g. like so: (TODO: screenshot)
 * 4. I recommend also setting up error notifications: (TODO: screenshot).
 * 
 * Testing setup:
 * When you're first setting this up, if you want to test it, create a couple 
 * of drafts and label them. Then, in this 
 * script, select "sendWeekStartDrafts" or "sendTomorrowDrafts" in the function dropdown 
 * and press play. This manually triggers the script (instead of relying on the 
 * timer) so you can see how it works. 
 *
 * Usage instructions:
 * 1. To get a draft sent out on the next trigger, mark your draft with the label you chose.
 *    NOTE: If your draft is a reply to a thread, make sure you first create the draft and only then set the label on the
 *    thread, not the other way around.
 * That's it! Upon trigger your draft will be sent and the label will get removed from the thread.
 * 
 * Some credits and explanation of differences/improvements from other existing solutions:
 * 1. This script was adapted from https://stackoverflow.com/a/27215474 to also support replying existing threads, not only
 *    sending brand new messages.
 * 2. Other solutions I've run into are based on creating a new message, copying it field-by-field, and sending the new one,
 *    but those have many issues, some of which are that they also don't handle replies and forwards very elegantly.
 *
 * Enjoy!
 **/

var TOMORROW_LABEL = '!send-tomorrow';
var WEEK_START_LABEL = '!send-week-start';
// TODO(user): add more labels here.

/**
 * Set up the label for delayed send!
 **/
function setUpLabels() {
  GmailApp.createLabel(TOMORROW_LABEL);
  GmailApp.createLabel(WEEK_START_LABEL);
  // TODO(user): add more labels here.
}

function sendTomorrowDrafts() {
  sendLabeledDrafts(TOMORROW_LABEL);
}

function sendWeekStartDrafts() {
  sendLabeledDrafts(WEEK_START_LABEL);
}

// TODO(user): add more sendXDrafts() functions for your additional labels here.

/*
 * Send all drafts labeled $MY_LABEL.
 * @param {String}     label   The label for which to send drafts.
 */
function sendLabeledDrafts(label) {
  var threads = GmailApp.search('in:draft label:' + label);

  for (var i=0; i<threads.length; i++) {
    var thread = threads[i];
    var messages = thread.getMessages();
    var success = false;
    for (var j=messages.length-1; j>=0; j--) {
      var msgId = messages[j].getId();
      if (sendDraftMsg( msgId )) {
        success = true;
      }
    }
    if (!success) { throw Error( "Failed sending msg" ) };
    if (success) {
      var myLabel = GmailApp.getUserLabelByName(label);
      thread.removeLabel(myLabel);
    }
  }
}

/**
 * Sends a draft message that matches the given message ID.
 * 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)) return null;
  
  // 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
    return null;
  }
}


/**
 * 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.
 * 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
    return null;
  }
}

答案 5 :(得分:0)

首先,GmailDraft现在具有您可以直接调用的send()函数。参见:https://developers.google.com/apps-script/reference/gmail/gmail-draft#send()

他们的代码示例:

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

第二,既然Google已经发布了预定的发送,甚至可能甚至不需要它。

  1. 点击Send旁边的箭头

Click the down arrow next to send.

  1. 选择您的首选发送时间

Select your preferred time.