在 Google Apps 脚本中使用高级 Gmail 服务创建与表情符号兼容的 Gmail 草稿
Creating an Emoji-Compatible Gmail draft with Advanced Gmail Service in Google Apps Script
我正在尝试 create a Gmail draft with the Gmail Advance Service. What I need is to have the body
contain data
in bytes format。我构建了以下函数:
const createDraftWithAdvancedService = () => {
Gmail.Users.Drafts.create({
message: {
payload: {
parts: [
{
body: {
data: [
42,
123,
123,
80,
114,
],
}
}
],
headers: [
{
"value": "This is a test subject",
"name": "Subject"
},
]
}
}
}, 'user@domain.com');
}
但是,当我 运行 它时,出现以下错误:
GoogleJsonResponseException: API call to gmail.users.drafts.create failed with error: Invalid JSON payload received. Unknown name "data" at 'draft.message.payload.parts[0].body': Proto field is not repeating, cannot start list.
错误看起来很奇怪,因为我正确地遵循了索引(或者我相信在检查了一百次之后)。
我在这里错过了什么?
更新
我将字节字符串格式化为数组的原因是,当您 阅读 草稿时,Gmail API return 就是这样。如果您有不同的 工作 代码,我洗耳恭听。而且我不能使用raw
,我需要设置字节串。
以下是如何检索消息对象及其格式的示例:
我的消息草稿:
检索此草稿消息的脚本:
const getMessage = () => {
const id = 'r-8326849559354985208';
const msg = Gmail.Users.Drafts.get('user@domain.com', id);
console.log(JSON.stringify(msg, null, 2));
}
脚本的输出:
{
"message": {
"internalDate": "1633701716000",
"snippet": "Draft body",
"labelIds": [
"DRAFT"
],
"historyId": "954861",
"sizeEstimate": 534,
"payload": {
"filename": "",
"parts": [
{
"partId": "0",
"headers": [
{
"value": "text/plain; charset=\"UTF-8\"",
"name": "Content-Type"
}
],
"filename": "",
"body": {
"data": [
68,
114,
97,
102,
116,
32,
98,
111,
100,
121,
13,
10
],
"size": 12
},
"mimeType": "text/plain"
},
{
"headers": [
{
"value": "text/html; charset=\"UTF-8\"",
"name": "Content-Type"
}
],
"partId": "1",
"body": {
"size": 33,
"data": [
60,
100,
105,
118,
32,
100,
105,
114,
61,
34,
108,
116,
114,
34,
62,
68,
114,
97,
102,
116,
32,
98,
111,
100,
121,
60,
47,
100,
105,
118,
62,
13,
10
]
},
"mimeType": "text/html",
"filename": ""
}
],
"body": {
"size": 0
},
"headers": [
{
"value": "1.0",
"name": "MIME-Version"
},
{
"value": "Fri, 8 Oct 2021 16:01:56 +0200",
"name": "Date"
},
{
"value": "<CADVhnimBt3Jdod1wBgGUgB_75yrsoJMwM68mtYKmX6cN39=CNQ@mail.gmail.com>",
"name": "Message-ID"
},
{
"name": "Subject",
"value": "Draft subject"
},
{
"name": "From",
"value": "\"KOSTYUK, Dmitry\" <user@domain.com>"
},
{
"value": "multipart/alternative; boundary=\"00000000000088918105cdd7d2e1\"",
"name": "Content-Type"
}
],
"mimeType": "multipart/alternative",
"partId": ""
},
"id": "17c6035e45454be8",
"threadId": "17c6035c50e83b2f"
},
"id": "r-8326849559354985208"
}
经过一番研究和学习RFC2822 MIMEText语法后,我对这个问题有了明确的答案。我分三部分回答:
- 什么没用
- 解决方案一:困难的方法
- 解决方案二:新的简便方法
什么没用
没有用的是像我在问题中所做的那样使用实际的 Message
object。不要问我为什么,它没有在任何地方记录,它只是不起作用。即使您通过 Gmail.Users.Drafts.get()
从另一条消息中复制 JSON object 的全部或现有部分,GAS 仍会抛出错误。
所以进入的是而不是返回的内容,即使文档另有说明。
因此唯一的解决方案是使用消息 object 的 raw
属性,它必须是 RFC2822 格式的 base-64 编码字符串。
解决方案一:困难的方法
结合 here and here 中的解决方案,创建了一个基本函数,可以生成带有表情符号的消息草稿:
function convert(toEmail, fromEmail, subject, body) {
body = Utilities.base64Encode(body, Utilities.Charset.UTF_8);
subject = Utilities.base64Encode(subject, Utilities.Charset.UTF_8);
const boundary = "boundaryboundary";
const mailData = [
"MIME-Version: 1.0",
"To: " + toEmail,
"From: " + fromEmail,
"Subject: =?utf-8?B?" + subject + "?=",
"Content-Type: multipart/alternative; boundary=" + boundary,
"",
"--" + boundary,
"Content-Type: text/plain; charset=UTF-8",
"",
body,
"",
"--" + boundary,
"Content-Type: text/html; charset=UTF-8",
"Content-Transfer-Encoding: base64",
"",
body,
"",
"--" + boundary,
].join("\r\n");
return mailData;
}
function makeApiDraft() {
const subject = "Hello MimeText World";
const body = 'This is a plain text message';
const me = Session.getActiveUser().getEmail();
const raw = convert('test@test.com', me, subject, body);
const b64 = Utilities.base64EncodeWebSafe(raw);
console.log(raw)
Gmail.Users.Drafts.create({ message: { raw: raw } }, me);
}
这个解决方案没有问题,它有效。然而,如果你想超越这个例子,比如添加多个收件人、有不同的纯文本和 html 正文、管理附件等,你将不得不手动编写所有代码,这需要理解 RFC2822 MIMEText格式。
因此输入新的更简单的解决方案。
解决方案二:新的简便方法
我偶然发现 this library 生成用 Node.js 编写的 MIMEText 电子邮件。所以我认为完美。我分叉了 repo 并修改了一些东西使其成为 GAS-compatible,特别是:
- 使用
Utilities.base64Encode()
和 Utilities.base64EncodeWebSafe()
完成 Base 64 编码
- 附加文件只需传递一个 GAS
DriveApp.File
object
- 我确保在需要的地方出现了正确的 MIMEText headers 和 base 64 编码。
虽然我的 pull request 未决,但我使用 Webpack 转译了整个内容(因为该库确实有依赖项)并将其作为 GAS 库发布在这个 ID 下:
1HzFRRghlhuCDl0FUnuE9uKAK39GfeuUJAE3oOsjv74Qjq1UW8YEboEit
这里有an example project大家可以用来测试一下,不过代码基本如下:
const testMimeText = () => {
const { message } = MimeText;
message.setSender({
name: 'Dmitry Kostyuk',
addr: 'dmitry.kostyuk@gmail.com',
});
const file = DriveApp.getFileById('1pdMwlGL1WZTbi-Q2-Fc7nBm-9NKphkKg');
const me = Session.getActiveUser().getEmail();
message.setRecipient('dmitry.kostyuk@gmail.com');
message.setSubject('Hello MimeText World!');
message.setMessage('This is a plain text message ' + getAllEmojis(), 'text/plain');
message.setMessage('<p>This is an html message</p><p>' + getAllEmojis() + '</p>\r\n\r\n', 'text/html');
message.setAttachments([file]);
const raw = message.asEncoded();
Gmail.Users.Drafts.create({ message: { raw: raw } }, me);
}
const getDriveAuth = () => DriveApp.getRootFolder();
我想我掉进了一个我从未想过的兔子洞,但我对结果很满意:)
我正在尝试 create a Gmail draft with the Gmail Advance Service. What I need is to have the body
contain data
in bytes format。我构建了以下函数:
const createDraftWithAdvancedService = () => {
Gmail.Users.Drafts.create({
message: {
payload: {
parts: [
{
body: {
data: [
42,
123,
123,
80,
114,
],
}
}
],
headers: [
{
"value": "This is a test subject",
"name": "Subject"
},
]
}
}
}, 'user@domain.com');
}
但是,当我 运行 它时,出现以下错误:
GoogleJsonResponseException: API call to gmail.users.drafts.create failed with error: Invalid JSON payload received. Unknown name "data" at 'draft.message.payload.parts[0].body': Proto field is not repeating, cannot start list.
错误看起来很奇怪,因为我正确地遵循了索引(或者我相信在检查了一百次之后)。
我在这里错过了什么?
更新
我将字节字符串格式化为数组的原因是,当您 阅读 草稿时,Gmail API return 就是这样。如果您有不同的 工作 代码,我洗耳恭听。而且我不能使用raw
,我需要设置字节串。
以下是如何检索消息对象及其格式的示例:
我的消息草稿:
检索此草稿消息的脚本:
const getMessage = () => {
const id = 'r-8326849559354985208';
const msg = Gmail.Users.Drafts.get('user@domain.com', id);
console.log(JSON.stringify(msg, null, 2));
}
脚本的输出:
{
"message": {
"internalDate": "1633701716000",
"snippet": "Draft body",
"labelIds": [
"DRAFT"
],
"historyId": "954861",
"sizeEstimate": 534,
"payload": {
"filename": "",
"parts": [
{
"partId": "0",
"headers": [
{
"value": "text/plain; charset=\"UTF-8\"",
"name": "Content-Type"
}
],
"filename": "",
"body": {
"data": [
68,
114,
97,
102,
116,
32,
98,
111,
100,
121,
13,
10
],
"size": 12
},
"mimeType": "text/plain"
},
{
"headers": [
{
"value": "text/html; charset=\"UTF-8\"",
"name": "Content-Type"
}
],
"partId": "1",
"body": {
"size": 33,
"data": [
60,
100,
105,
118,
32,
100,
105,
114,
61,
34,
108,
116,
114,
34,
62,
68,
114,
97,
102,
116,
32,
98,
111,
100,
121,
60,
47,
100,
105,
118,
62,
13,
10
]
},
"mimeType": "text/html",
"filename": ""
}
],
"body": {
"size": 0
},
"headers": [
{
"value": "1.0",
"name": "MIME-Version"
},
{
"value": "Fri, 8 Oct 2021 16:01:56 +0200",
"name": "Date"
},
{
"value": "<CADVhnimBt3Jdod1wBgGUgB_75yrsoJMwM68mtYKmX6cN39=CNQ@mail.gmail.com>",
"name": "Message-ID"
},
{
"name": "Subject",
"value": "Draft subject"
},
{
"name": "From",
"value": "\"KOSTYUK, Dmitry\" <user@domain.com>"
},
{
"value": "multipart/alternative; boundary=\"00000000000088918105cdd7d2e1\"",
"name": "Content-Type"
}
],
"mimeType": "multipart/alternative",
"partId": ""
},
"id": "17c6035e45454be8",
"threadId": "17c6035c50e83b2f"
},
"id": "r-8326849559354985208"
}
经过一番研究和学习RFC2822 MIMEText语法后,我对这个问题有了明确的答案。我分三部分回答:
- 什么没用
- 解决方案一:困难的方法
- 解决方案二:新的简便方法
什么没用
没有用的是像我在问题中所做的那样使用实际的 Message
object。不要问我为什么,它没有在任何地方记录,它只是不起作用。即使您通过 Gmail.Users.Drafts.get()
从另一条消息中复制 JSON object 的全部或现有部分,GAS 仍会抛出错误。
所以进入的是而不是返回的内容,即使文档另有说明。
因此唯一的解决方案是使用消息 object 的 raw
属性,它必须是 RFC2822 格式的 base-64 编码字符串。
解决方案一:困难的方法
结合 here and here 中的解决方案,创建了一个基本函数,可以生成带有表情符号的消息草稿:
function convert(toEmail, fromEmail, subject, body) {
body = Utilities.base64Encode(body, Utilities.Charset.UTF_8);
subject = Utilities.base64Encode(subject, Utilities.Charset.UTF_8);
const boundary = "boundaryboundary";
const mailData = [
"MIME-Version: 1.0",
"To: " + toEmail,
"From: " + fromEmail,
"Subject: =?utf-8?B?" + subject + "?=",
"Content-Type: multipart/alternative; boundary=" + boundary,
"",
"--" + boundary,
"Content-Type: text/plain; charset=UTF-8",
"",
body,
"",
"--" + boundary,
"Content-Type: text/html; charset=UTF-8",
"Content-Transfer-Encoding: base64",
"",
body,
"",
"--" + boundary,
].join("\r\n");
return mailData;
}
function makeApiDraft() {
const subject = "Hello MimeText World";
const body = 'This is a plain text message';
const me = Session.getActiveUser().getEmail();
const raw = convert('test@test.com', me, subject, body);
const b64 = Utilities.base64EncodeWebSafe(raw);
console.log(raw)
Gmail.Users.Drafts.create({ message: { raw: raw } }, me);
}
这个解决方案没有问题,它有效。然而,如果你想超越这个例子,比如添加多个收件人、有不同的纯文本和 html 正文、管理附件等,你将不得不手动编写所有代码,这需要理解 RFC2822 MIMEText格式。
因此输入新的更简单的解决方案。
解决方案二:新的简便方法
我偶然发现 this library 生成用 Node.js 编写的 MIMEText 电子邮件。所以我认为完美。我分叉了 repo 并修改了一些东西使其成为 GAS-compatible,特别是:
- 使用
Utilities.base64Encode()
和Utilities.base64EncodeWebSafe()
完成 Base 64 编码
- 附加文件只需传递一个 GAS
DriveApp.File
object - 我确保在需要的地方出现了正确的 MIMEText headers 和 base 64 编码。
虽然我的 pull request 未决,但我使用 Webpack 转译了整个内容(因为该库确实有依赖项)并将其作为 GAS 库发布在这个 ID 下:
1HzFRRghlhuCDl0FUnuE9uKAK39GfeuUJAE3oOsjv74Qjq1UW8YEboEit
这里有an example project大家可以用来测试一下,不过代码基本如下:
const testMimeText = () => {
const { message } = MimeText;
message.setSender({
name: 'Dmitry Kostyuk',
addr: 'dmitry.kostyuk@gmail.com',
});
const file = DriveApp.getFileById('1pdMwlGL1WZTbi-Q2-Fc7nBm-9NKphkKg');
const me = Session.getActiveUser().getEmail();
message.setRecipient('dmitry.kostyuk@gmail.com');
message.setSubject('Hello MimeText World!');
message.setMessage('This is a plain text message ' + getAllEmojis(), 'text/plain');
message.setMessage('<p>This is an html message</p><p>' + getAllEmojis() + '</p>\r\n\r\n', 'text/html');
message.setAttachments([file]);
const raw = message.asEncoded();
Gmail.Users.Drafts.create({ message: { raw: raw } }, me);
}
const getDriveAuth = () => DriveApp.getRootFolder();
我想我掉进了一个我从未想过的兔子洞,但我对结果很满意:)