使用 AppScript 回复 Gmail 中更改收件人的电子邮件最终会出现在新线程中
我的邮箱中有一封电子邮件,我希望 AppScript 程序仅以我和一个特殊的 google 组作为收件人来回复它.这样做的目的是程序与我进行通信,因为程序在处理完消息后会回复消息,并在回复正文中提供有关处理的必要细节.原始消息中可能还有除我之外的其他收件人,我不希望程序将回复发送给他们.
I have an email in my mailbox and I want the AppScript program to reply to it with just me and a special google group as the recipients. The purpose of this is communication of the program with me as the program replies to the message once it has processed it with necessary details about the processing in the reply body. There might also be other recipients apart from me in the original message and I don't want the program to send the reply to them.
所以我需要回复一组更改后的收件人.当我在 Gmail GUI 中执行此操作时,它工作得很好,我点击回复,更改收件人,发送消息,回复最终出现在原始线程中.但是,当我在脚本中执行此操作时,回复总是以新线程结束.最初我认为 Gmail 会根据电子邮件的主题来决定,但似乎还有更多内容(也许它最近发生了变化,因为我认为它曾经是这样工作的).
So I need to reply with a changed set of recipients. When I do it in the Gmail GUI it works just fine, I hit reply, change the recipients, send the message and the reply ends up in the original thread. However when I do it in the script the reply always ends up in a new thread. Originally I thought Gmail decides based on the subject of the email but it seems there's more to it (perhaps it has recently changed as I think it used to work that way).
我尝试了多种略有不同的方法,其中之一是:
I tried multitude of slightly different approached, one of them being:
var messageBody = "foo";
var newRecipients = "me@gmail.com, my-group@gmail.com";
var messageToReplyTo = ...;
var advancedParams = {from : "my-alias@gmail.com"};
var replyDraft = messageToReplyTo.createDraftReply(messageBody);
var replySubject = replyDraft.getMessage().getSubject();
var replyBody = replyDraft.getMessage().getBody();
replyDraft.update(newRecipients, replySubject, replyBody, advancedParams);
replyDraft.send();
推荐答案
为了实现这一点,您需要做一些有趣的事情,但您可以轻松完成.您绝对应该查看草稿指南.
There are a couple fun things you need to do in order to achieve this, but you can do it without too much trouble. You should definitely review the guide to Drafts.
根据 API 规范:
要成为话题的一部分,消息或草稿必须满足以下条件:
In order to be part of a thread, a message or draft must meet the following criteria:
- 请求的
threadId
必须在您随请求提供的Message
或Draft.Message
中指定. - References 和 In-Reply-To 标头的设置必须符合 RFC 2822 标准.
- 主题标头必须匹配.
- The requested
threadId
must be specified on theMessage
orDraft.Message
you supply with your request. - The References and In-Reply-To headers must be set in compliance with the RFC 2822 standard.
- The Subject headers must match.
首先,您需要获取要更新的草稿的参考.这可能是使用 GmailApp
最简单的方法:
To start, you need to get a reference to the draft you want to update. This is probably simplest by using GmailApp
:
const thread = /** get the thread somehow */;
const newBody = /** your plaintext here */;
const reply = thread.createDraftReply(newBody);
Gmail 的主要问题Drafts 是 Draft
是对服务器资源的不可变消息.如果你改变其中任何一个,你就会改变所有.因此,要更改诸如收件人地址之类的标头值,您需要完全重建邮件.这就是为什么使用 GmailApp
更新草稿的方法 无法维护现有线程信息 - 您不能将其指定为构建新邮件的高级选项之一.因此,您必须为此任务使用 Gmail REST API:
The primary issue with Gmail & Drafts is that a Draft
is an immutable message to server resources. If you change any of it, you change all of it. Thus, to change a header value such as the recipient address, you need to completely rebuild the message. This is why using the GmailApp
methods to update a draft fail to maintain the existing thread information - you can't specify it as one of the advanced options for building the new message. Thus, you must use the Gmail REST API for this task:
const rawMsg = Gmail.Users.Drafts.get("me", reply.getId(), {format: "raw"}).message;
要更新草稿,您需要提供以 base64 编码的 RFC 2822 格式的消息.如果您愿意将丰富格式的消息部分转换为这样一个有效的字符串,请务必使用非原始格式,因为您可以直接访问 message.payload
.
To update a draft, you need to supply an RFC 2822 formatted message encoded in base64. If you are comfortable converting the rich format message parts into such a valid string, by all means work with the non-raw format, as you have direct access to the headers in the message.payload
.
要处理原始消息,请知道 Apps 脚本在上述调用中将描述的 base64 编码字符串转换为字节数组.然后飞跃是将该字节数组视为字符串字节,特别是 charCode
s:
To work with the raw message, know that Apps Script casts the described base64 encoded string to a byte array in the above call. The leap is then to treat that byte array as string bytes, specifically, charCode
s:
const msg_string = rawMsg.raw.reduce(function (acc, b) { return acc + String.fromCharCode(b); }, "");
console.log({message: "Converted byte[] to str", bytes: rawMsg.raw, str: msg_string});
将消息作为字符串后,您可以使用正则表达式来更新所需的标题:
Once you have the message as a string, you can use regular expressions to update your desired headers:
const pattern = /^To: .+$/m;
var new_msg_string = msg_string.replace(pattern, "To: <....>");
// new_msg_string += ....
自从 Gmail API 端点到 update
Draft
需要一个 base64 网络安全编码字符串,您可以计算:
Since the Gmail API endpoint to update
a Draft
expects a base64 web-safe encoded string, you can compute that:
const encoded_msg = Utilities.base64EncodeWebSafe(new_msg_string);
剩下的唯一一点是执行调用(和/或 发送
更新的草稿).
And the only remaining bit is to perform the call (and/or send
the updated draft).
const resource = {
id: <draft id>, // e.g. reply.getId()
message: {
threadId: <thread id>, // e.g. thread.getId()
raw: encoded_msg
}
}
const resp = Gmail.Users.Drafts.update(resource, "me", reply.getId());
const sent_msg = Gmail.Users.Drafts.send({id: resp.id}, "me");
console.log({message: "Sent the draft", msg: sent_msg});
我并没有声称从 Message.raw
属性返回的 Byte
数组的处理是 100% 正确的,只是它看起来正确并且没有导致我发送的测试消息中出现任何错误.还有一种更简单的方法,因为 Apps 脚本服务有一个 Drafts.update
端点,它接受 Blob
输入,我还没有研究过如何使用它.
I don't claim that the handling of the Byte
array returned from the Message.raw
property is 100% correct, only that it seems correct and didn't result in any errors in the test message I sent. There may also be an easier approach, as the Apps Script service has a Drafts.update
endpoint which accepts a Blob
input and I have not investigated how one would use that.
相关文章