Ответ на электронное письмо в gmail с помощью appscript с измененными получателями попадает в новую цепочку

В моем почтовом ящике есть электронное письмо, и я хочу, чтобы программа AppScript ответила на него, указав в качестве получателей только меня и специальную группу Google. Целью этого является коммуникация программы со мной, поскольку программа отвечает на сообщение после его обработки с необходимыми деталями обработки в теле ответа. Кроме меня в исходном сообщении могут быть и другие получатели, и я не хочу, чтобы программа отправляла им ответ.

Поэтому мне нужно ответить с измененным набором получателей. Когда я делаю это в графическом интерфейсе Gmail, он работает нормально, я нажимаю «Ответить», меняю получателей, отправляю сообщение, и ответ попадает в исходную цепочку. Однако, когда я делаю это в сценарии, ответ всегда попадает в новую цепочку. Первоначально я думал, что Gmail принимает решение на основе темы письма, но, похоже, это еще не все (возможно, это недавно изменилось, поскольку я думаю, что раньше так работало).

Я пробовал множество немного разных подходов, один из которых:

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

Чтобы поддерживать состояние потока, вы также должны сохранить определенные заголовки электронной почты. Дополнительную информацию можно найти в документации по API Gmail.

tehhowch 10.08.2018 14:38
3
1
583
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для этого вам нужно сделать несколько забавных вещей, но вы можете сделать это без особых проблем. Вам обязательно стоит ознакомиться с руководством по Черновики.

Согласно спецификации API:

In order to be part of a thread, a message or draft must meet the following criteria:

  1. The requested threadId must be specified on the Message or Draft.Message you supply with your request.
  2. The References and In-Reply-To headers must be set in compliance with the RFC 2822 standard.
  3. The Subject headers must match.

Для начала вам нужно получить ссылку на черновик, который вы хотите обновить. Это, вероятно, проще всего с помощью GmailApp:

const thread = /** get the thread somehow */;
const newBody = /** your plaintext here */;
const reply = thread.createDraftReply(newBody);

Основная проблема Gmail и Drafts заключается в том, что Draft - это неизменяемое сообщение для ресурсов сервера. Если вы измените что-то из этого, вы измените все. Таким образом, чтобы изменить значение заголовка, например адрес получателя, необходимо полностью перестроить сообщение. Вот почему при использовании GmailApp методы обновления черновика не удается сохранить информацию о существующем потоке - вы не можете указать его как одну из дополнительных опций для создания нового сообщения. Таким образом, вы должны использовать Gmail REST API для этой задачи:

const rawMsg = Gmail.Users.Drafts.get("me", reply.getId(), {format: "raw"}).message;

Чтобы обновить черновик, вам необходимо предоставить сообщение в формате RFC 2822, закодированное в base64. Если вам удобно преобразовывать части сообщения расширенного формата в такую ​​допустимую строку, непременно работайте с необработанным форматом, так как у вас есть прямой доступ к заголовкам в message.payload.

Чтобы работать с необработанным сообщением, знайте, что скрипт приложений преобразует описанную строку в кодировке base64 в массив байтов в приведенном выше вызове. Тогда переход состоит в том, чтобы рассматривать этот массив байтов как строковые байты, в частности, charCodes:

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

Получив сообщение в виде строки, вы можете использовать регулярные выражения для обновления желаемых заголовков:

const pattern = /^To: .+$/m;
var new_msg_string = msg_string.replace(pattern, "To: <....>");
// new_msg_string += ....

Поскольку конечная точка API Gmail для update в Draft ожидает строку, безопасную для веб-кодирования base64, вы можете вычислить это:

const encoded_msg = Utilities.base64EncodeWebSafe(new_msg_string);

И единственный оставшийся бит - выполнить вызов (и / или send обновленный черновик).

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

Я не утверждаю, что обработка массива Byte, возвращенного из свойства Message.raw, на 100% верна, только то, что она кажется правильной и не привела к каким-либо ошибкам в отправленном мною тестовом сообщении. Также может быть более простой подход, так как служба Apps Script имеет конечную точку Drafts.update, которая принимает входные данные Blob, и я не исследовал, как это можно использовать.

Какая поездка! Спасибо большое, работает. Для справки в будущем - для того, чтобы это работало, необходимо включить расширенные службы Google.

David Apltauer 18.08.2018 19:20

Другие вопросы по теме