Как сделать так, чтобы отправленные электронные письма отображались в виде потока в представлении получателя GMail с помощью идентификатора сообщения, In-Reply-To и ссылок
Я прочитал несколько замечательных интернет-ресурсов, таких как http://www.jwz.org/doc/threading.html, и кажется, что любое электронное письмо отправляется с Message-ID
заголовок, затем любые ответы на него включают In-Reply-To
называя этот идентификатор и Refences
который может называть список идентификаторов родительского сообщения, и почтовые клиенты используют эту информацию для создания потоков при просмотре списка электронных писем в многопоточном представлении.
У меня вопрос: можно ли отправлять серию электронных писем получателю с поддельными заголовками, чтобы они появлялись в цепочке без ответа получателя? Если так, то почему моя попытка ниже не работает?
У нас есть система, которая отправляет несколько электронных писем, относящихся к конкретному объекту в нашей системе. Допустим, мы продаем виджеты и по электронной почте пользователям несколько раз о каждом виджете. Мы хотим, чтобы все электронные письма с определенным идентификатором виджета отображались как цепочка сообщений в почтовых клиентах нашего пользователя.
Похоже, что в этом случае обычные письма отправляются, а затем отправляются ответы. Наша система просто хочет отправить несколько электронных писем и подделать заголовки In-Reply-To и References, чтобы заставить почтовых клиентов отображать их в дереве.
Формат идентификатора сообщения, который я использую: "foobar" + widgetId + sequence
- widgetId = число, уникальное для каждого виджета, например, 1234
- sequence = порядковый номер, увеличиваемый каждый раз, когда мы отправляем письмо
Первое электронное письмо:
- Message-ID
<foobar-1234-0@server.com>
- In-Reply-To: не предоставляется
- Рекомендации: не предоставляются
Второе письмо:
- Message-ID
<foobar-1234-1@server.com>
- В ответ на:
<foobar-1234-0@server.com>
- Рекомендации:
<foobar-1234-0@server.com>
Третье письмо:
- Message-ID
<foobar-1234-2@server.com>
- В ответ на:
<foobar-1234-1@server.com>
- Рекомендации:
<foobar-1234-0@server.com> <foobar-1234-1@server.com>
(кстати, включая @server.com
часть идентификатора сообщения представляется жизненно важной. Без этого, используя, например, foobar-123-0
наш SMTP-сервер просто проигнорировал его и использовал свой собственный автоматически сгенерированный идентификатор сообщения)
Электронные письма правильно отображаются в Thunderbird в виде дерева, но не в Gmail, они просто перечисляются один за другим в папке "Входящие", в то время как другие разговоры правильно располагаются рядом с ними. Я не уверен, что я ошибаюсь, и Thunderbird делает все возможное с плохими данными, или если Gmail нужен дополнительный нестандартный сахар, который я не предоставляю.
Вот мой тестовый скрипт для node.js:
/*jshint dojo:true */
/*global console:true */
'use strict';
var Q = require('q'),
nconf = require('nconf'),
optimist = require('optimist'),
nodemailer = require('nodemailer');
console.log('Started to run.');
var argv = optimist.argv,
config = nconf.argv().env().file('conf.json'),
smtpConfig = config.get('smtp'),
smtpTransport = nodemailer.createTransport('SMTP', {
service: smtpConfig.service, // 'Gmail',
auth: {
user: smtpConfig.user, //'blah@gmail.com',
pass: smtpConfig.pass //'xyz'
}
}),
rand = Math.floor(Math.random() * 5000), // a random enough unique id
messageIdPrefix = 'foobar-' + rand + '-';
var promises = [],
references = '';
for (var i = 0 ; i < 3 ; i ++) {
// Prepare email content
var subject = 'This is test email ' + i,
htmlMessage = '<h1>Am I threaded? Email ' + i + '</h1><p>???</p>',
textMessage = 'Am I threaded? Email ' + i + '\n\n???';
var recipients = 'recipient@server.com';
// Each email in this sequence has a common prefix
// In Reply To should be the single immediate parent message id
// References should list all parents, top most first
var messageId = messageIdPrefix + i + '@server.com',
inReplyTo = (i > 0) ? ('<' + (messageIdPrefix + (i-1)) + '@server.com>') : false;
// setup e-mail data with unicode symbols
var mailOptions = {
from: config.get('ourEmail'),
to: recipients,
subject: subject,
text: textMessage,
html: htmlMessage,
messageId: messageId,
inReplyTo: inReplyTo,
references: references,
headers: {
// 'in-Reply-To': inReplyTo
}
};
// send mail with defined transport object
var q = Q.defer();
promises.push(q.promise);
smtpTransport.sendMail(mailOptions, function (error, response) {
if (error) {
console.error(error);
q.reject('error');
} else {
console.log('Message sent: ' + response.message);
q.resolve('yay!');
}
});
// next time round loop, if any, includes this id in the references list
references = (references ? (references + ' ') : '') + messageId;
}
Q.all(promises).then(function (results) {
console.log('All done, closing mail connection: ', results);
smtpTransport.close(); // shut down the connection pool, no more messages
});
Требуется файл conf вроде:
{
"ourEmail": "me@server.com",
"smtp": {
"service": "Gmail",
"user": "me@server.com",
"pass": "ilikecheese"
}
}
Для получения бонусных баллов, пожалуйста, укажите, почему моя попытка использовать Q.all
похоже, не запускается, и скрипт не завершается корректно, несмотря на правильную отправку всех писем:)
1 ответ
Ответ на то, почему они не врезаны в Gmail, заключается в том, что потоки Gmail выполняются в соответствии с темой сообщений (это не основано на поле "in-reply-to" или "reference" в заголовке).
См. Ответы на этот вопрос о stackexchange для получения дополнительной информации о том, как Gmail выполняет потоки: https://webapps.stackexchange.com/questions/965/how-does-gmail-decide-to-thread-email-messages..
В вашем случае темы "Это тестовое электронное письмо 1", "Это тестовое электронное письмо 2" и "Это тестовое электронное письмо 3", которые не будут вызывать многопоточность по правилам использования Gmail.