在 Ruby 中格式化电子邮件回复

Format Email Reply in Ruby

我正在 ruby 中制作我自己的电子邮件客户端,它目前可以 parse/read-in 消息。它还可以创建对消息的回复,设置 headers,并将消息发送给原始发件人。

如何将引用的原始消息添加到回复中?

我应该如何格式化回复中的原始消息?是否有最佳实践或格式? MIME/RFC?我知道 HTML 和文本应该有一个字符串。只是不确定如何制作这些字符串。

现在我的回复没有下面的原始消息,很难自行理解。

撰写电子邮件回复是一项相当大的挑战,尤其是在一开始您不知道从哪里开始的时候。

最近我不得不编写此类电子邮件并以编程方式发送。我首先做的是看看电子邮件客户端是如何做到这一点的,比如 Thunderbird。不过,这需要一些实验和耐心。

我使用的消息的整体结构很大程度上基于这个 Stack Overflow 答案:https://whosebug.com/a/23853079/1368043


1。 HTML部分

请注意,您没有几个选择:要么编写 HTML 片段(典型 <body> 标签的内容),要么编写整个 HTML 文档(使用 <html><head><body> 标签)。我看了一下 Thunderbird 是如何做到的。原来它创建了整个文档,大致是这样生成的:

  1. 创建 HTML 文档
  2. <head> 部分添加元信息 <meta content="text/html; charset=utf-8" http-equiv="Content-Type">(将字符集替换为您喜欢的字符集)
  3. <body> 部分添加您编写的 HTML 片段,添加引用的标题(例如:“<div>Few days ago, John Smith wrote: </div>”)并添加 <blockquote> 块紧随其后:<blockquote cite="mid:originalmessagemid@example.com" type="cite">。请注意,原始消息有一个消息 ID。

这是我不太喜欢 Thunderbird 的部分:

  1. 复制原始消息的 HTML 内容并将其粘贴到 <blockquote> 块中。

Thunderbird 并不真正检查复制的 HTML 是片段还是文档。但是,如果它是一个文档,它会去除 <html><head> 标签......同时保留它们的内容。结果,您可以看到原始邮件 <head> 部分中的 <style><title> 标记位于新邮件的 <body> 标记中。太乱了。

此外,Thunderbird 不处理全局样式。您可以轻松地使用全局样式而不是内联样式来撰写棘手的邮件,当邮件的收件人开始撰写回复时,样式会渗透到整个邮件中。


你也可以做同样的事情。它并没有真正伤害到任何人,它们只是典型邮件通常不会观察到的怪癖。另外这很容易。或者你可以更进一步清理这个烂摊子。

首先,您必须让自己拥有任何 HTML 解析器。我用的是Nokogiri,我的使用方法是这样的:

  1. 它会自动将任何片段转换为HTML文档,因此无需分别分析片段和文档
  2. 在文档中找到 <body> 标签并复制其内容
  3. 删除找到的任何 <style> 个标签
  4. 将结果复制到需要的地方

大致如下所示:

doc = Nokogiri::HTML.parse(strHTML)
body = doc.css('body')[0]
body.css('style').each { |node|
    node.unlink
}

puts body.inner_html

Nokogiri 还有一个好处 - 如果您在 HTML 消息中有任何内联图像,您可以轻松找到它们,将 URL 替换为 "cid:..." 方案并添加图像作为内联附件。


2。纯文本部分

对了,multipart/alternative 部分还有邮件的纯文本版本。这里最关键的过程是将任何 HTML 文本转换为纯文本版本的能力。这比编写 HTML 部分还要棘手。毕竟,您必须编写一个简单的渲染引擎(就像任何其他网络浏览器一样)。可能有 gems,不幸的是我当时找不到。

虽然有几个要点可以帮助您入门:

  • 所有换行符(\r\n 或 \n)应替换为单个 space
  • 所有多个 space 都应减少为一个(除非它们是 non-breaking)
  • 某些标签会保留内容而其他标签不会(例如 <style><script> 标签与 <b><div>
  • 某些标签需要在它们之后换行(<br><p><div> 等块标签就是示例)
  • 您必须正确格式化 table。您必须计算列的宽度,考虑 colspans 和 rowspans,用 spaces 填充单元格的内容以对齐它们等
  • 您必须为 <b><i>、... 标签找到替代标记(例如用星号或诸如此类的东西包围它们)
  • 您还可以格式化标题:<h1><h2>、...标签,方法是在 and/or 上方
  • 下面添加破折号或星号
  • 您必须正确格式化 <a> 标签,即将它们转换成以下格式:Stack Overflow site [http://whosebug.com]
  • 您必须丢弃 <img> 标签,并可能用替代文本替换它们(如果存在)
  • 您还必须解码 HTML 个实体(&gt; 等)。如果不是 Nokogiri,HTMLEntities gem 在这种情况下可能会有所帮助

这个列表可以继续下去。当然不用了

Internet 上有一些库和项目可以执行此操作,但它们不是为 Ruby 编写的and/or 他们缺少上面列出的一些功能。例如:


一旦你解决了这个问题,text/plain 部分的结构实际上与 HTML 部分相同。一开始就有你的回复。然后是引用标题,然后是引用的消息。它的格式通常是每行前面都有“>”字符。现在,问题是您究竟应该在其中粘贴什么内容。

第一个选项是转换原始消息的 HTML 部分(通过上述方法)并将其粘贴为引用的消息。第二种方法是使用原始消息的 text/plain 部分(如果存在)并在不进行任何转换的情况下粘贴它。后一种选择的好处是,长时间对话中的“>”字符将在时间后以树状方式累积。此外,它还保留了发件人可能手动组装的纯文本格式,以使其更加准确。


3。摘要

根据您的实际需要和想要达到的质量水平,撰写此类邮件的难度从 easy/tricky 到困难不等,特别是如果您必须自己编写所有代码.如果您碰巧发现任何 Ruby gem 至少可以帮助您完成其中的某些任务,请不要犹豫并使用它们。

编写 HTML 部分就像相互复制和粘贴 HTML 部分一样简单,最好事先剥离一些标签。编写纯文本部分就像完全删除几个标签一样简单(<head><script><style>、...),在保留内容的同时剥离所有标签并解码所有 HTML 个实体,按此顺序。

删除 HTML 标签可以使用正则表达式来完成,但它是 strongly discouraged 并且被认为是穷人工具箱中的工具。所以我建议为此目的使用 Nokogiri 或类似的东西。

虽然这不是问题的一部分,但我必须强调编写电子邮件客户端的一个方面。您应该始终记得清理您的HTML消息,尤其是您收到的消息。在收到的邮件中可疑地查看 iframe 或脚本没有什么好处,如果垃圾邮件过滤器没有立即 blocked/filtered,它们可能是 XSS 攻击的一部分。在这种情况下,Sanitize gem 可能有用。

干杯