组会分享——如何优雅地发送邮件

冰岩作坊 May 22, 2024

泥嚎呀,今天想聊一聊邮件的事。

我们平常可能会收到来自各种网站的邮件,以我订阅的 WakaTime 为例,它会给我推送一个周报:

💡 WakaTime 可以和主流 IDE 或者编辑器关联,统计 coding 的时长等数据。我这里用的邮箱客户端是 qq 邮箱网页版,我们可以很容易看出这里是一个 HTML 文本,可以用 devtools 查看一下内容区域的结构是什么样的:

   它的主体结构是下面这样的:

1
<div class="readmail_content">  <div class="readmail_content_html qmbox qmbox_Webid="readmail_content_html">    <style>style>    <div bgcolor="#F7F7F7">div>  div>div>

显然这就是邮件的主体结构,最外层的两个 div 元素是 qq 邮箱添加的,而我们看到的  html  文本则是被放入了里面。

💡 这个结构在不同邮箱网站中会不一样,请自行探索🕶。既然如此,我们能否用编程的方式,发送一个被华丽的 CSS 装饰的 HTML 文本给别人呢?当然可以😎,这就是接下来要尝试的。

nodemailer 启动!

用我熟悉的 Node 可以快速搭建一个发送邮件的服务,用到的库是 nodemailer

首先,我们需要添加 nodemailer 这个包:

1
npm install nodemailer

接下来在项目中导入:

1
// index.jsconst nodemailer = require('nodemailer');

既然要发送邮件,我们肯定需要一个账号,可以用 nodemailer.createTransport 定义一个发送邮件的对象,并指定账号和密码:

1
const transporter = nodemailer.createTransport(,});

我来解释一下关键参数的含义:

  1. host : host 是指邮件服务器的主机地址。对于 qq 邮箱,SMTP 服务器的主机地址是 smtp.qq.com。SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是用于发送电子邮件的互联网标准协议。每个电子邮件服务提供商都会有自己独特的 SMTP 服务器地址。例如,Gmail 的 SMTP 服务器地址是 smtp.gmail.com
    💡 可以在邮箱的帮助文档中查询到对应的信息1. port : 一般是 456 或者 587,  这两个都是加密端口
  2. auth : 这里的 pass 不是密码,而是用授权码,授权码可以从邮箱的客户端中申请获得。
    完成之后我们只需要调用 transporter.sendMail 发送邮件即可,这里我写了一个异步函数来完成这件事:
1
const content = `

`async function sendMail() )  console.log(“Message sent: %s”, info.messageId);}sendMail().catch(console.error);这里的 content 是一个加载动画,具体实现比较复杂,实现的方式不是重点 :>

这是在不同设备上面看到的效果:

content 被篡改

打开 devtools, 观察一下我们的 content 部分的 html:

1
<div class="readmail_content_html qmbox qmbox_Web" id="readmail_content_html">  <style>    .qmbox .loader_OZAT     .qmbox .loader::before     .qmbox .loader::after     @keyframes l10-0     .qmbox 50%,.qmbox 100%     }    @keyframes l10-1     }    @keyframes l10-2     .qmbox 80%,.qmbox 100%     }  style>  <div class="loader_OZAT">div>div>

可以观察到我们的 content 被 qq 邮箱篡改了!

具体来说:

  1. 每个选择器前面被加上了 .qmbox 这个类选择器。
  2. @keyframes 内部被错误地添加上了 .qmbox ,造成语法错误
  3. div 的 loader 类变成了 loader_OZAT
    第一条是比较好理解的,通过加上前缀,来防止类名污染。

但是 2, 3 条的机制我就不是很能理解了,尤其是 2 显然是一个 bug,这直接导致动画失效。

不过我们能推断出 qq 邮箱是会对发来的 html 做处理的,来保证网站和用户的安全。

复原之后我们的马里奥又回来辣!

## js 是否能插入到 content 里?

那既然我们能在 content 里面写 html 和 css,那么我们能否写 js 呢?

考虑到安全,qq 邮箱应该是会删掉 content 里面的 js 的,我们可以来验证一下。

把 content 替换为下面的内容:

1
2
3
const content = `                  计数器: 0

    +1