
随着电子邮件在监管环境中的使用增加,我决定是时候开始一个新项目,它将所有这些结合在一起,并提供有关如何存储电子邮件正文及其所有相关数据的代码示例。
Business in a box.
探索我们的解决方案。
与我们的销售团队交谈
大约一年前,我写了一篇博客,关于如何检索电子邮件副本用于存档和查看,但我并没有涉及实际存储电子邮件或相关数据的问题。最近,我又写了一篇博客,关于存储邮件的所有事件数据(例如,邮件发送时间,打开,点击,退回,退订等)以进行审计,但选择不创建任何支持代码。
随着电子邮件在监管环境中的使用增加,我决定开始一个新项目,将所有这些结合在一起,提供代码示例,展示如何存储电子邮件正文及其所有相关数据。在接下来的一年里,我将继续构建这个项目,目的在于创建一个存储和查看应用,用于存档电子邮件及SparkPost 产生的所有日志信息。SparkPost没有保存电子邮件正文的系统,但建立存档平台相对容易。
在本博客系列中,我将描述我为了将邮件正文存储到S3(亚马逊的简单存储服务)以及将所有相关日志数据存储在MySQL中以便于交叉引用而经历的过程。 最终,这就是构建一个应用程序的起点,能够便捷地搜索存档邮件,并显示那些邮件以及事件(日志)数据。该项目的代码可以在以下GitHub库中找到:https://github.com/jeff-goldstein/PHPArchivePlatform
本博客系列的第一篇文章将描述挑战,并为解决方案概述架构。其余的博客将详细介绍解决方案的部分内容以及代码示例。
我流程中的第一步是找出如何获取发送给原始收件人的邮件副本。为了获取邮件正文的副本,您需要:
在发送邮件之前捕获邮件正文
让邮件服务器保存一份副本
让邮件服务器创建一份供您存储的副本
如果邮件服务器正在添加诸如链接跟踪或打开跟踪等项目,则无法使用方法1,因为那样不会反映打开/点击跟踪的更改。
这意味着服务器要么存储邮件,或者以某种方式提供该邮件的副本以供您存储。因为SparkPost没有用于邮件正文的存储机制,但确实有一种创建邮件副本的方法,我们将让SparkPost发送邮件的副本给我们,并存储在S3上。
这通过使用SparkPost的存档功能完成。SparkPost的存档功能使发送者能够告知SparkPost将邮件的副本发送到一个或多个电子邮件地址,并使用与原始邮件相同的跟踪和打开链接。SparkPost文档如下定义其存档功能:
存档列表中的收件人将收到与发送给RCPT TO地址的邮件完全相同的副本。特别是,任何为RCPT TO收件人编码的链接将在存档邮件中是相同的。
与RCPT TO邮件唯一不同的是,部分头信息会有所不同,因为存档邮件的目标地址不同,但邮件正文将是完全相同的副本!
如果您希望了解更详细的解释,这里有一个创建邮件副本(或存档)的SparkPost文档链接。
顺便一提,SparkPost实际上允许您发送邮件到cc、bcc和存档电子邮件地址。对于这个解决方案,我们专注于存档地址。
现在我们知道如何获得原始邮件的副本,我们需要查看产生的日志数据以及该数据中的一些微妙差异。SparkPost追踪发生在其服务器上的所有事件,并以消息事件形式提供给您。那些事件在SparkPost上存储10天,并可以通过一个名为message-events的RESTful API从服务器上提取,或者您可以让SparkPost将那些事件推送到您希望的任何数量的采集应用中。 推送机制通过webhooks以实时方式完成。
目前,邮件上可能发生14种不同的事件。 这里是当前事件的列表:
Bounce
ClickDelay
Delivery
Generation Failure
Generation Rejection
Initial Open
InjectionLink Unsubscribe
List Unsubscribe
Open
Out of Band
Policy RejectionSpam Complaint
* 请按照此链接获取每个事件的描述及共享数据的最新参考指南。
每个事件都有很多字段与事件类型相匹配。 一些字段如transmission_id在每个事件中都会出现,但其他字段则可能更具事件特性;例如,只有打开和点击事件才有地理标记信息。
对于这个项目,一个非常重要的消息事件条目是transmission_id。原始邮件、存档邮件,以及任何cc和bcc地址的所有消息事件条目将共享相同的transmission_id。
还有一个叫做message_id的常见条目,原始邮件和存档邮件的每个条目将有相同的id。任何cc或bcc地址的message_id条目将有自己的id。
到目前为止,这听起来很好,坦率地说相当简单,但现在是一个有挑战性的部分。记住,为了获取存档邮件,我们让SparkPost将原始邮件的副本发送到另一个您有访问权限的收件箱对应的电子邮件地址。但为了自动化此解决方案并存储邮件正文,我将使用SparkPost的另一个功能,称为入境邮件中继。这项功能会接收所有发送到特定域的电子邮件并进行处理。通过处理,它会拆解邮件并创建一个JSON结构,然后通过webhook将其发送到应用程序。请参阅附录A查看JSON示例。
如果您仔细查看,您会注意到入境中继的JSON结构中缺少一个非常重要的字段:transmission_id。虽然所有的出站邮件都有相同的transmission_id条目,将原始邮件、存档、cc和bcc地址的所有数据绑定在一起;SparkPost无法知道通过入境过程捕捉的邮件与任一出站邮件相关联。入境过程只知道邮件被发送到一个特定域并进行解析。就是这样。它将以同样方式处理发送到该域的任何邮件,无论是来自客户的回复还是由SparkPost发送的存档邮件。
那么技巧是:如何将出站数据与刚抓取到存档版邮件的入境过程粘合在一起?我决定在邮件正文中隐藏一个唯一ID。这取决于您如何做到,但我只是创建了一个具有隐藏标签的输入字段。
我还将该字段添加到X-MSYS-API头的元数据块中,该头在注入SparkPost时传递。此隐藏UID将最终成为整个过程的粘合剂,是项目的主要组成部分,并将在接下来的博客文章中深入讨论。
现在我们拥有了将这个项目粘合在一起的UID,并理解了其必要性,我可以开始构建整体项目和相应博客文章的愿景。
捕获并存储存档邮件以及用于搜索/索引的数据库条目
捕获所有消息事件数据
创建一个应用程序以查看邮件及所有相关数据
这是该项目的简单图示:

第一个代码发布将涵盖存档过程及将邮件存储到S3,而第二个代码发布将涵盖将message-events中的所有日志数据存储到MySQL中。您可以在2019年初期待前两次代码发布和博客文章。 如果您有任何问题或建议,请随时提出。
祝您发送愉快。
– Jeff
附录A:
