如何使用谷歌应用脚本发送电子邮件草稿
Posted
技术标签:
【中文标题】如何使用谷歌应用脚本发送电子邮件草稿【英文标题】:How to send a draft email using google apps script 【发布时间】:2015-01-28 04:07:22 【问题描述】:我正在使用 Google 应用程序脚本,并希望创建一个脚本,该脚本从草稿中提取邮件并在其标签为“send-tomorrow”的情况下发送它们。 查找带有特定标签的草稿非常简单:
var threads = GmailApp.search('in:draft label:send-tomorrow');
但是我没有看到发送消息的 API! 我看到的唯一选择是: - 打开消息 - 提取正文/附件/标题/from/to/cc/bcc - 使用上述参数发送新消息 - 销毁之前的草稿
这看起来很烦人,我不确定嵌入图像、多个附件等是否能很好地工作......
有什么提示吗?
【问题讨论】:
【参考方案1】:我看到的唯一选项是: - 打开消息 - 提取正文/附件/标题/from/to/cc/bcc - 使用上述参数发送新消息 - 销毁以前的草稿
这是 Amit Agarawa 的 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 -> Advanced Google Services,打开 Google Developers Console 的链接,并为您的项目启用 Gmail API。
只要脚本包含至少一个需要用户权限的 GMailApp 方法,就会为 OAuthToken 正确设置身份验证范围。在这个例子中,这是由GmailApp.search()
in sendDayOldDrafts()
处理的;但为了保险起见,您可以使用 API 直接在函数中包含不可访问的函数调用。
【讨论】:
已更新 - 功能齐全(哇!) 非常非常非常(等等...)干得好。已经是昨天了,但现在很完美!非常感谢我的朋友 ;-) 再次 +1【参考方案2】:我使用GmailMessage.forward 方法做到了。
它适用于上传图片和附件,但我必须设置主题以避免前缀“Fwd:”和用户名,因为它只向收件人显示用户电子邮件。
我没有找到处理草稿的方法,所以我只删除了标签以防止再次发送。
脚本:
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);
【讨论】:
非常聪明的方法! @Mogsdad 的解决方案仍然更完整,但您的解决方案肯定更简单(即使有些限制) 更新:今天可以添加线程[i].moveToTrash();并将草稿副本发送到 trach(您仍然会在已发送的邮件中保留另一份)【参考方案3】:您可以搜索所有草稿,然后发送该特定草稿没问题。
function sendMessage(id)
GmailApp.getDrafts().forEach(function (draft)
mes = draft.getMessage()
if (mes.getId() == id)
draft.send()
)
【讨论】:
【参考方案4】:首先,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
其次,现在谷歌已经发布了预定发送,可能甚至不需要它。
-
单击
Send
旁边的箭头
-
选择您的首选发送时间
【讨论】:
【参考方案5】:更简单的替代方法是使用 gmail api 而不是 gmailApp:
function sendtestDraft(draftId)
var request = Gmail.Users.Drafts.send(id : draftId,'me');
Logger.log(request);
上面的函数示例在https://script.google.com 的gs 脚本中使用。 它需要 DraftId(而不是消息 Id)并且草稿将被发送。图片和附件都OK! 信息:https://developers.google.com/gmail/api/v1/reference/users/drafts/send
【讨论】:
我还认为@mogsdad 的上述解决方案使用了 API,我错了吗? 此解决方案是否消耗API quota?多少钱?send()
中的id
是userId
。请查看您提供的链接中的 API。
@evan 不,你错了。我的例子确实有效。 userId 是“me” - 来自文档: userId:“用户的电子邮件地址。特殊值 me 可用于指示经过身份验证的用户”。 id 是“草稿的不可变 ID”。 - 请阅读我提供的链接中的 API。
@user2677034 - 你完全正确。我重新查看了该链接,它确实将 draftId 作为输入。我一定是打开了另一个窗口并感到困惑。很好的发现!【参考方案6】:
我是新来的,没有足够的“声誉”来发表评论,所以无法评论 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://***.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://***.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;
【讨论】:
以上是关于如何使用谷歌应用脚本发送电子邮件草稿的主要内容,如果未能解决你的问题,请参考以下文章
如何将Outlook发送的电子邮件唯一匹配到其对应的Outlook草稿(MAPI MailItem)