我已经阅读了一些很棒的在线资源,例如http://www.jwz.org/doc/threading.html,似乎任何电子邮件都带有Message-ID
标题,然后对它的任何回复都包括In-Reply-To
命名该 ID 以及Refences
可以命名父消息 ID 的列表,电子邮件客户端在查看线程视图中的电子邮件列表时使用此信息来构建线程。
我的问题是:可以将一系列电子邮件发送给带有伪造标题的收件人,以使它们出现在线程中而收件人不回复它们吗?如果是这样,为什么我在下面的尝试不起作用?
我们有一个系统可以发送多封与我们系统中的特定实体相关的电子邮件。假设我们销售小部件并多次向用户发送有关每个小部件的电子邮件。我们希望特定小部件 ID 的所有电子邮件在我们用户的电子邮件客户端中显示为电子邮件线程。
这里的旅行似乎是通常发送电子邮件,然后回复。我们的系统只是想发送几封电子邮件,并伪造 In-Reply-To 和 References 标头,以欺骗电子邮件客户端将它们显示在树中。
我使用的 Message-ID 格式是:'foobar' + widgetId + sequence
- widgetId = 每个小部件唯一的数字,例如 1234
- 序列 = 每次我们发送电子邮件时递增的序列号
第一封电子邮件:
- 消息 ID
<foobar-1234-0@server.com>
- 回复:未提供
- 参考资料:未提供
第二封电子邮件:
- 消息 ID
<foobar-1234-1@server.com>
- 回复:
<foobar-1234-0@server.com>
- 参考:
<foobar-1234-0@server.com>
第三封电子邮件:
- 消息 ID
<foobar-1234-2@server.com>
- 回复:
<foobar-1234-1@server.com>
- 参考:
<foobar-1234-0@server.com> <foobar-1234-1@server.com>
(顺便说一句,包含@server.com
消息 ID 的部分似乎很重要。没有它,使用例如foobar-123-0
,我们的 SMTP 服务器简单地忽略它并使用它自己的自动生成的消息 ID)
电子邮件在 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
似乎没有触发并且脚本没有干净地退出,尽管正确发送了所有电子邮件:)