如何在GMail收件人的视图中使用Message-ID、In-Reply-To和References使已发送邮件呈现成线程形式

10

我已经阅读了一些很棒的在线资源,比如http://www.jwz.org/doc/threading.html,似乎任何电子邮件都会带有一个Message-ID头部字段,然后任何回复都会包括一个In-Reply-To命名该ID和References,它可以命名一系列父消息ID,当在线查看电子邮件列表时,电子邮件客户端使用这些信息来构建线程。

我的问题是:是否可以发送一系列带有伪造标题的电子邮件给收件人,以使它们出现在线程中,而不需要收件人回复它们?如果可以,那么为什么下面的尝试不起作用?

我们的系统会发送几封与我们系统中的特定实体相关的电子邮件。例如,我们销售小部件,并向用户发送有关每个小部件的多封电子邮件。我们希望特定小部件ID的所有电子邮件都显示为用户电子邮件客户端中的电子邮件线程。

关键在于通常情况下会发送电子邮件,然后进行回复。我们的系统只需发送若干封电子邮件,然后伪造In-Reply-ToReferences标题,以欺骗电子邮件客户端将其显示为树形结构。

我使用的Message-ID格式是:'foobar' + widgetId + sequence

  • widgetId = 每个小部件唯一的数字,例如1234
  • sequence = 每次发送电子邮件时递增的顺序号

第一封电子邮件:

  • Message-ID <foobar-1234-0@server.com>
  • In-Reply-To: 未提供
  • References: 未提供

第二封电子邮件:

  • Message-ID <foobar-1234-1@server.com>
  • In-Reply-To: <foobar-1234-0@server.com>
  • References: <foobar-1234-0@server.com>

第三封电子邮件:

  • Message-ID <foobar-1234-2@server.com>
  • In-Reply-To: <foobar-1234-1@server.com>
  • References: <foobar-1234-0@server.com> <foobar-1234-1@server.com>

(顺便说一下,包括消息ID的@server.com部分似乎是至关重要的。如果没有这个,例如使用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,仍然无法使脚本干净地退出,尽管所有电子邮件都已成功发送。请给出提示以获取额外的分数:)

1个回答

7
在Gmail中为什么不使用线程,是因为Gmail的线程是根据邮件主题完成的(而不是基于头部中的“回复”或“参考”字段)。有关Gmail如何处理线程的更多详细信息,请参见stackexchange上这个问题的答案:https://webapps.stackexchange.com/questions/965/how-does-gmail-decide-to-thread-email-messages。在您的情况下,主题是“This is test email 1”,“This is test email 2”和“This is test email 3”,这些主题不符合Gmail使用的规则,因此不会引起线程。

谢谢。我们注意到了这个问题,当我们将所有发出的电子邮件从我们的测试平台导向一个电子邮件帐户时,就会出现这种情况。其中几个是相同的操作“X did Y”,但通常会发送到许多不同的个人帐户。当在同一个Gmail收件箱中查看时,它们都被线程化了。因此,我想主题都必须足够相似,即“Re:”和“Fwd:”等垃圾邮件将被忽略,但基本上在Gmail中使用主题字符串匹配?那么我们的计划就失败了 :) - Neek
3
链接到的另一个问题中被接受的答案确实表明“in-reply-to”标题也会被考虑在内,我可以通过查看今天看到的一些邮件列表线程来确认这一点。其中一位人员通过在回复中省略“in-reply-to”标题来多次分割该线程。所有消息都有相同的主题,但在他修复客户端以包括“in-reply-to”头之前没有正确地线程化。 - TwoD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接