如何使用SendGrid API发送电子邮件通讯

2020年12月30日11:00:27 发表评论 49 次浏览

本文概述

多年来, 昆西·拉尔森(Quincy Larson)通过notlogy的永久邮件由Amazon SES提供支持的平台。

他最近将此过程迁移到SendGrid。在本文中, 我将向你展示如何构建工具来完成此任务。

如何设置一个SendGrid帐户

第一步是注册SendGrid并设置你的帐户。就本教程而言, 免费套餐就足够了。

在扩展应用程序时, 可能需要通过平台增加可用的电子邮件限制。

如何在SendGrid上设置专用IP地址

默认情况下, SendGrid使用共享的IP地址发送电子邮件。对于较小规模的电子邮件应用程序, 这可能是可以接受的, 但是随着你提高发送速率, 你将需要设置专用的IP地址。

这是一个好主意, 因为你的"发件人信誉"(SendGrid用于评估你在电子邮件服务提供商中的地位的指标)不会受到共享相同IP的其他用户的行为的负面影响。

要设置自己的专用IP, 请从侧面导航菜单中选择"设置"选项, 然后选择" IP地址"。不过请注意:免费套餐不提供此选项。

根据你的付费计划, 你可能已经设置了一个专用IP地址。如果你没有, 或者选择添加更多, 可以选择"添加IP地址"按钮来配置新IP。

如何使用SendGrid API发送电子邮件通讯1

IP地址设置菜单

如何在SendGrid中授权电子邮件发件人

注意:如果你将自定义域用于电子邮件, 则可以跳过本节。

要从你的个人电子邮件地址发送电子邮件, 你将需要验证该电子邮件地址是否属于你。

在左侧菜单中, 选择"设置", 然后选择"发件人身份验证"。选择"验证单个发件人"以逐步完成添加你的一个电子邮件地址的流程。

如何使用SendGrid API发送电子邮件通讯2

单寄件人选项

如何在SendGrid中验证你的自定义域

注意:如果你没有为电子邮件使用自定义域, 则可以跳过本节。

为了从你的自定义邮件域发送电子邮件, 你将需要使用SendGrid对该域进行身份验证。要进入此屏幕, 请再次选择"设置"菜单, 然后选择"发件人身份验证"。

如何使用SendGrid API发送电子邮件通讯3

发件人验证的设置菜单

然后, 你应该会看到一个带有"域身份验证"选项的屏幕。选择"验证你的域"选项, SendGrid将引导你完成配置DNS记录的过程(基于DNS提供商的特定说明)。

如何使用SendGrid API发送电子邮件通讯4

发件人身份验证设置页面

如何在SendGrid中设置反向DNS

注意:如果你的电子邮件没有使用自定义域, 则可以跳过本节。

电子邮件提供商使用反向DNS(域名系统)来查找给定IP地址的所有者。进行此设置将使电子邮件提供商可以验证你发送电子邮件的IP地址是否已连接到你的自定义域。

在与上述相同的"发件人身份验证"屏幕中, 你将看到"反向DNS"部分。有一个选项可以为你帐户中的每个专用IP配置反向DNS-例如域身份验证, SendGrid的平台将引导你完成设置。

如何在SendGrid中设置电子邮件身份验证

注意:如果你的电子邮件没有使用自定义域, 则可以跳过本节。

主要的电子邮件提供商(例如Gmail, Yahoo和Outlook)使用几种方法来验证电子邮件的发件人:SPF, DKIM和DMARC。

  • 防晒指数(发件人策略框架)验证是否已授权从你的域发送邮件的IP地址这样做。
  • DKIM(DomainKeys Identified Mail)使用公钥字符串对电子邮件进行身份验证从地址准确无误。
  • DMARC(基于域的消息身份验证, 报告和一致性)是一组说明, 告诉电子邮件提供商在电子邮件未通过SPF或DKIM验证时如何做出反应。

SendGrid的身份验证流程将引导你完成SPF和DKIM的设置, 这是域身份验证过程的一部分, 但是你将需要手动配置DMARC。

访问你的DNS托管提供商, 然后访问DNS管理设置。从那里添加一个新的文本文件记录名称为_dmarc.yourdomain.com(取代yourdomain.com与你的自定义域)。

请注意, 某些提供商(例如GoDaddy)会自动将你的域添加到记录中-在这种情况下, 名称应为_dmarc.

的值该记录的结构应类似于:

"v=DMARC1; p=none; pct=100; rua=mailto:dmarc@yourdomain.com"
  • v = DMARC指示要使用的DMARC规则的版本(当前只有版本1可用)。
  • p =无表示当电子邮件未通过DKIM或SPF时, 电子邮件提供商应采取的措施。此设置应从None, 以避免影响电子邮件的可传递性。确认正确配置了DKIM和SPF之后, 你可以将该值更新为隔离让提供商自动将失败的电子邮件路由到垃圾邮件文件夹, 或者拒绝让提供商拒绝/退回失败的电子邮件。
  • pct = 100指示应将操作应用于失败电子邮件的百分比。
  • rua = mailto:dmarc@yourdomain.com是向其发送汇总报告的电子邮件地址。这些报告包含给定提供商收到的来自你IP的所有电子邮件的信息。更换dmarc@yourdomain.com与你要接收这些报告的电子邮件地址。

如何在SendGrid中创建动态模板

我们今天将要构建的工具使用SendGrid的动态模板功能来设置电子邮件的主题和正文。要进行设置, 请在侧面导航菜单中选择"电子邮件API"选项, 然后选择"动态模板"。

如何使用SendGrid API发送电子邮件通讯5

动态模板的设置菜单

你将看到一个屏幕, 提示你创建第一个动态模板。选择"创建动态模板"选项。

给你的新模板起一个名字:" notlogy SendGrid Tutorial"。 SendGrid将此模板添加到可用模板列表中。选择模板以查看范本编号(记下这一点, 因为稍后该工具将需要它), 然后单击"添加版本"按钮。

如何使用SendGrid API发送电子邮件通讯6

预览新添加的模板

在出现的屏幕上选择"空白模板", 然后选择"代码编辑器"。现在, 你应该看到编辑器视图。 SendGrid的编辑器使用HTML来构建电子邮件正文-但是, 当我们构建工具时, 我们将发送纯文本版本。

现在, 用以下代码替换编辑器的内容:

<p>This is a test email used with the notlogy SendGrid tutorial</p>
<p>Unsubscribe: {{{unsubscribeId}}}</p>

我们的工具将发送纯文本电子邮件, 因此不需要额外的HTML样板。

你会注意到我们已经添加了{{{unsubscribeId}}}。 SendGrid的模板使用把手来动态替换值-构建工具时, 我们将利用此功能。

现在, 从左上方选择设置选项-你可以选择给模板版本起一个名称, 但是"主题"字段是我们要修改的名称。将此值设置为{{{学科}}}从我们的工具动态加载主题值。

要测试动态模板, 请从顶部菜单中选择"测试数据"选项。将此JSON数据插入编辑器中:

{
    "unsubscribeId": "1", "subject": "Testing emails!"
}

请记住, JSON需要将密钥用引号引起来!

现在, 你应该看到屏幕右侧的预览反映了模板中的这些值。记得打保存按钮来保存你的更改!

如何使用SendGrid API发送电子邮件通讯7

编辑器和预览屏幕显示了模板值的动态加载

如何在SendGrid中生成API密钥

配置SendGrid帐户的最后一步是生成API密钥供我们的工具使用。

单击左上方的后退箭头以返回到SendGrid主页。然后选择"设置"和" API密钥"。选择"创建API密钥"以生成一个新密钥。你可以选择对密钥授予"完全访问"权限, 但是出于本教程的目的, 你仅需要"邮件发送"访问权限。

请确保为你的键指定一个描述性名称, 以便再次访问此屏幕时会记住其用途。配置权限后, 选择"创建并查看"以生成密钥-将其保存在安全的地方, 因为你将无法再次查看它.

如何使用SendGrid API发送电子邮件通讯8

启用了"邮件发送"权限的"创建API"屏幕

如何构建电子邮件工具

现在是时候编写代码以实际发送一些电子邮件了。你可以查看实时应用程序的代码, 但出于本教程的目的, 我们将构建一个略减版本主要专注于使用SendGrid API。

自定义电子邮件活动脚本所需的软件

你需要安装以下工具才能与此项目一起使用:

  • Node.js-建议使用LTS版本
  • 一个IDE, 例如VSCodeor原子

你可能还需要吉特用于版本控制。

我们的实时工具使用MongoDB Atlas集群, 但是我们的教程示例不会。如果你不熟悉MongoDB, 则notlogy的课程包括关于设置和使用MongoDB的精彩部分.

如何初始化项目

创建一个用于处理此项目的目录(文件夹)。然后使用你的编辑器和所选终端打开该文件夹。

首先, 我们需要将其设置为Node项目。最快的方法是npm初始化在你的终端中。这将引导你完成创建package.json这是Node应用程序的核心文件。

默认值将对我们的应用程序正常工作, 但我们将需要修改剧本部分:

"scripts": {
    "build": "tsc", "send": "node ./prod/send.js"
  }, 

npm初始化

将创建一个

测试

脚本-可以为我们的项目删除。

的建立脚本将用于将我们的TypeScript编译为JavaScript, 并且发送脚本将运行我们的应用程序。

接下来, 我们将安装并设置TypeScript。如果你不熟悉TypeScript, 它实际上是JavaScript的超集, 具有更强的类型定义和编译时错误检查。

要在项目上安装TypeScript, 请运行npm install --save-dev打字稿在你的终端中。 (--save-dev标记将其保存为开发依赖项-运行时不需要TypeScript, 因此可以在生产环境中清除。

TypeScript需要其自己的配置文件来设置生成JavaScript文件时应遵循的规则。在项目的根目录中创建一个名为tsconfig.json并插入以下内容:

{
    "compilerOptions": {
      "target": "es5", "module": "commonjs", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "outDir": "./prod", "rootDir": "./src"
    }
  }

如果你查看教程资料库, 则会看到一个额外的内容

排除

属性。该值特定于教程文件结构, 你将不需要它。

为简洁起见, 我们将不涉及这些配置设置。如果你需要其他信息, TypeScript具有非常深入的文档.

如果你正在使用吉特为了进行版本控制并将其上传到存储库(例如GitHub), 你将需要创建一个.gitignore项目根目录中的文件。该文件应包含:

/node_modules/
.env
/prod/
  • / node_modules /将忽略已安装的软件包。在使用版本控制时, 这被认为是最佳实践。
  • .env将忽略我们的环境变量文件。这对你很重要决不想要将你的机密提交到存储库。
  • /产品/将忽略我们编译的JavaScript文件。我们还将此文件夹用于我们的电子邮件列表, 因此避免意外提交该私人可识别信息非常重要。

创建一个.env文件放在根项目目录中。我们将通过该文件加载以下环境变量:

SENDGRID_API_KEY=
SENDGRID_FROM=
SENDGRID_TEMPLATE_ID=

MAIL_SUBJECT=

填写值时, 请记住将其用双引号引起来。另外,

=

标志!

  • SENDGRID_API_KEY应该是你在之前的步骤中生成的API密钥。
  • SENDGRID_FROM应该是你的电子邮件地址(这是用于从领域)。
  • SENDGRID_TEMPLATE_ID应该是id你之前创建的动态模板的字符串。
  • MAIL_SUBJECT将是你发送的电子邮件的主题行。现在, 将其设置为" fCC教程电子邮件"。

最后, 创建一个src根项目目录中的文件夹, 并创建一个send.ts该文件夹中的文件。

如何安装依赖项

首先, 我们需要安装sendgridNode.js包。该程序包用作SendGrid API的包装, 将简化我们进行API调用以发送电子邮件的过程。跑npm安装@ sendgrid / mail安装此软件包。

然后, 我们需要几个开发依赖项。跑npm install --save-dev dotenv @ types / node.

  • Dotenv将允许我们从.env本地文件。
  • @类型/节点提供Node.js的类型定义-TypeScript依靠这些定义来了解内置方法和函数的结构。

如何编写逻辑

现在我们将在我们的/src/send.ts文件-这是我们构建大量应用逻辑的地方。我们将从从包中导入所需的值开始。

首先, 我们要加载Dotenv打包并解析我们的环境变量。

import dotenv from "dotenv";
dotenv.config();

Dotenv

仅用于本地开发-大多数在线主机(如Heroku和Repl.it)可以本地处理环境变量

的dotenv.config()呼叫读取我们的.env文件并将值加载到process.env节点对象。

接下来, 我们从SendGrid包中导入所需的模块:

import sgMail, { MailDataRequired } from "@sendgrid/mail";

TypeScript可以自动导入内容, 但是最好进行手动导入。

邮件是主要的API包装器, 并且MailDataRequired是我们需要的类型定义。

最后, 我们导入一些内置的Node功能来处理文件:

import path from "path";
import { createWriteStream, readFile } from "fs";

{语法}用于从包中导入特定模块。

  • 路径将用于查找带有相对路径的电子邮件列表文件
  • fs将用于读取和写入这些文件

是时候开始构建逻辑了!我们的应用程序依赖于在.env文件, 因此我们需要首先验证这些变量的设置是否正确。如果缺少任何内容, 我们希望我们的应用程序尽早退出, 以避免在发送电子邮件时引发错误。

// Here we check for a valid API key
const apiKey = process.env.SENDGRID_API_KEY;
if (!apiKey) {
  console.error("Missing SendGrid Key");
  process.exit(1);
}

// Here we check for a valid from address
const fromAddress = process.env.SENDGRID_FROM;
if (!fromAddress) {
  console.error("Missing sender email address!");
  process.exit(1);
}

// Here we check for a dynamic template ID
const sgTemplate = process.env.SENDGRID_TEMPLATE_ID;
if (!sgTemplate) {
  console.error("Missing SendGrid Template ID");
  process.exit(1);
}

// Here we check for the mail subject, but if it is missing
// we do not need to exit. Instead we use a fallback value.
const subjectValue = process.env.MAIL_SUBJECT || "Fallback value - check your env!";

||

语法告诉代码, 如果

process.env.MAIL_SUBJECT

是未定义或错误的, 请改用字符串。

的process.exit(1)你在每个条件检查中看到的调用告诉Node以退出代码终止该进程(我们的应用程序)1。这表明由于这些检查之一失败, 我们的应用程序崩溃了。

SendGrid要求我们设置API密钥。在环境变量逻辑下, 添加函数调用以设置键。

// Here we set the SendGrid API key
sgMail.setApiKey(apiKey);

继续前进之前, 先跑步npm运行构建在你的终端中-这将创建一个产品包含我们编译的JavaScript的文件夹。现在, 你应该看到以下文件结构:

如何使用SendGrid API发送电子邮件通讯9

本教程中这一点的文件树

此时, 如果你正在使用吉特你想成为非常确定那产品文件夹将不会提交到你的存储库。

内产品文件夹, 创建一个validEmails.csv文件。我们的应用程序将使用此文件来阅读电子邮件列表。用以下内容初始化文件(替换your@email.com和你的实际电子邮件地址):

email, unsubscribeId
your@email.com, 1
iama@fake.email, 2

.csv

文件在逗号周围没有空格。

现在我们可以编写代码以将其解析为电子邮件列表!在你的src / send.ts文件, 添加以下代码:

// Here we concatenate our file path for the valid email file
const filePath = path.join(__dirname + "/../validEmails.csv");

// This is where we start reading the file!
readFile(filePath, "utf8", (err, data) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log(data)
});

在此过程中, 我们一直使用ES6箭头函数语法。欢迎你使用

函数

如果愿意, 可以使用声明代替。

现在, 如果你运行npm运行构建和npm运行发送你应该看到我们的内容validEmail.csv文件在终端中。如果你愿意, 可以查看目前为止的最新进展.

大!现在, 我们需要将该字符串解析为对象数组, 以便我们可以对其进行迭代并生成电子邮件。更新我们的回调函数:

// This is where we start reading the file!
readFile(filePath, "utf8", (err, data) => {
    if (err) {
        console.error(err);
        return;
    }
    
  // Here we parse the data into an object array
  const emailList = data
    .split("\n")
    .slice(1)
    .map((el) => {
      const [email, unsubscribeId] = el.split(", ");
      return { email, unsubscribeId };
    });
});

[电子邮件, unsubscribeId]

应用解构将拆分数组的值分配给这两个变量。

  • .split(" \ n")通过换行符分割字符串。注意:如果你使用的是Windows, 则可能需要更改你的行尾设置validEmails.csv从CRLFtoLF(Windows会插入多余的换行符, 这会影响我们的数据处理)
  • .slice(1)删除该数组的第一个元素(我们的电子邮件, 取消订阅ID线)。
  • 我们的地图函数将转换每个电子邮件, 取消订阅ID串成一个{电子邮件, unsubscribeId}目的。

该解析函数的最终结果将是一个对象数组电子邮件和unsubscribeId属性-使用起来比字符串平滑得多。

如何使用SendGrid API发送电子邮件通讯10

解析功能的示例输出

现在该发送一些电子邮件了。低于解析功能(但仍位于readFile回调)为我们的迭代方法添加结构。因为我们要访问数组中的每个值, 所以我们将使用.for每个方法。

// Here we iterate through the emailList array
  emailList.forEach((user) => {});

用户

参数将是

{电子邮件, unsubscribeId}

目的。我们称它为

用户

因为它表示整个应用程序中的notlogy userdata对象。

在的回调中.for每个, 我们可以构造SendGrid API期望的消息对象。

// Here we iterate through the emailList array
  emailList.forEach((user) => {

    // This is the message object SendGrid needs
    const message: MailDataRequired = {
        to: user.email, from: fromAddress, subject: subjectValue, text: "This goes away!", templateId: sgTemplate, dynamicTemplateData: {
            subject: subjectValue, unsubscribeId: user.unsubscribeId
        }
    }
    
  });

在继续之前, 让我们仔细看看这个消息对象。的MailDataRequired我们之前导入的用作此处的类型定义, 因此, 如果我们恰巧错过了必需的属性, TypeScript可以提醒我们。幸运的是, 我们拥有所有必需的属性。但是它们是什么意思呢?

  • 至:发送消息的电子邮件地址。这将是电子邮件从我们的每一行validEmails.csv文件
  • 从:发送消息的电子邮件地址。这是在我们的.env更早(应该是你的电子邮件地址)。
  • 学科:该字段不是必填字段, 但是为我们提供了备用值, 以防动态模板无法正确解析主题。
  • 文本:该文本值被模板覆盖。但是, 使用仍然很重要。 SendGrid可以将电子邮件发送为纯文本orhtml-通过使用文本属性而不是html属性, 我们确保模板发送为纯文本。电子邮件提供商是更可能将HTML邮件标记为垃圾邮件, 因此这有助于提高我们的可传递率。
  • templateId:这是SendGrid应该在电子邮件中使用的动态模板的ID。
  • dynamicTemplateData:这些值与我们之前在动态模板中设置的Handlebars字符串相对应。

大!我们的下一步是获取此构造的消息并将其发送。在邮件对象下方(但仍在.for每个回调), 让我们添加发送调用:

// Here we send the message we just constructed!
    sgMail.send(message);

这会将消息发送到我们中的每个电子邮件中validEmails.csv。不幸的是, 我们的代码将以静默方式运行, 我们将不知道每次发送是否成功。让我们添加一些错误处理。

的。发送()通话会返回一个Promise, 因此我们可以使用.then()。catch()处理退货。

// Here we send the message we just constructed!
    sgMail.send(message)
        .then(() => {
            // Here we log successful send requests
            console.info(`Message send success: ${user.email}`)
        }).catch((err) => {
            // Here we log errored send requests
            console.error(err);
            console.error(`Message send failed: ${user.email}`)
        });

你也可以使用async / await, 但这是.then.catch更清晰的情况

现在, 如果你运行npm运行构建和npm运行发送你应该会在收件箱中看到一封精美的电子邮件!

至此, 你现在有了一个功能正常的电子邮件发送应用程序。恭喜你!你可以查看我们目前的进展如果你愿意。

请继续阅读以了解如何处理退回的电子邮件以及发送失败的其他逻辑, 这是我们接下来要讨论的内容。

如何在SendGrid中处理退回的电子邮件

你可能已经注意到iama@fake.email不是真正的电子邮件地址。 SendGrid将每天为你的活动在前一天生成退回报告。

每一封退回的电子邮件都会损害你的SendGrid声誉, 并可能导致电子邮件提供商将你的邮件标记为垃圾邮件。因此, 我们需要添加逻辑以防止发送到已知的退回地址。

首先创建一个bouncedEmails.csv档案在产品文件夹(应该在你的文件夹旁边validEmails.csv)。我们不需要unsubscribeId值, 请使用以下命令对其进行初始化:

email
iama@fake.email

我们已添加了虚假电子邮件以进行演示

现在回到我们的send.ts文件。在第38行, 就在我们现有的下方文件路径声明, 配置新路径bouncedEmails.csv文件。

// Here we concatenate our file paths for the CSV files
const filePath = path.join(__dirname + "/validEmails.csv");
const bouncePath = path.join(__dirname + "/bouncedEmails.csv");

__dirname指向该文件的当前目录(在本例中,

send.ts

文件)。

大!现在我们需要读取该文件。在这些文件路径声明的正下方(在我们现有的之前readFile调用), 添加用于读取退回文件的逻辑。

// Read through the bounce list, parse into array
readFile(bouncePath, "utf8", (err, data) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  bounceList = data.split("\n").slice(1);

readFile是异步的-因此我们需要包装回调函数我们所有现有的发送逻辑。确保你的结帐})该回调将移至文件的末尾。

我们读了bouncedEmails.csv文件, 在新行中将其拆分(请记住, 你需要确保行尾LF), 然后删除电子邮件线。最后, 我们继续我们现有的发送逻辑。

回到我们的发送逻辑。在我们内.for每个功能, 添加逻辑以跳过被阻止的电子邮件(我们将在构造消息对象之前添加此内容, 以避免创建不必要的变量)。

// Here we iterate through the emailList array
  emailList.forEach((user) => {
    // Here we check if the email has been bounced
    if (bounceList.length && bounceList.includes(user.email)) {
        console.info(`Message send skipped: ${user.email}`);
        return;
    }

如果我们

bounceList.csv

文件为空, 调用

包括

会抛出一个错误。所以我们检查一个

。长度

价值第一。

通过早期利用返回声明, 我们就此结束.for每个迭代时bounceList包括该电子邮件。这样可以防止我们尝试将邮件发送到以前退回的电子邮件地址。现在, 如果你运行npm运行构建和npm运行开始, 你应该在终端中看到以下输出:

如何使用SendGrid API发送电子邮件通讯11

跳过的电子邮件和成功的电子邮件的示例控制台输出

查看到目前为止的进展.

如何在SendGrid中捕获失败的电子邮件

当前, 如果电子邮件发送失败, 我们的应用程序将记录一个错误。这可以用于较小的用例, 但是当你扩展应用程序时, 你会发现识别这些失败并尝试再次发送变得越来越困难。

但是, 相反, 我们可以使我们的应用程序将这些电子邮件保存到新文件中。

创建一个failedEmails.csv档案在产品夹。该文件可以为空。我们将编写代码以添加标题行。

回到我们的send.ts文件, 转到第38行的路径声明。让我们为新文件添加一个failedEmails.csv:

// Here we concatenate our file paths for the CSV files
const filePath = path.join(__dirname + "/validEmails.csv");
const bouncePath = path.join(__dirname + "/bouncedEmails.csv");
const failedPath = path.join(__dirname + "/failedEmails.csv");

与我们的其他路径不同, 此路径将用于写操作。因为我们要在处理电子邮件时连续写, 所以我们需要创建一个流来这样做。在这些路径声明的下面, 让我们创建该流并添加我们的初始标题行。

// Here we create our write stream for failed emails
const failedStream = createWriteStream(failedPath);

// Here we add the header row
failedStream.write("email, unsubscribeId\n")

是时候改进我们的错误处理逻辑以合并这个新流了。我们需要添加另一个写操作到我们的错误处理中发送呼叫。

// Here we send the message we just constructed!
    sgMail
      .send(message)
      .then(() => {
        // Here we log successful send requests
        console.info(`Message send success: ${user.email}`);
      })
      .catch((err) => {
        // Here we log errored send requests
        console.error(err);
        console.error(`Message send failed: ${user.email}`);
        // And here we add that email to the failedEmails.csv
        failedStream.write(`${user.email}, ${user.unsubscribeId}\n`)
      });

我们希望保留控制台语句, 以确保在脚本运行时看到反馈。

这将写电子邮件和unsubscribeId到我们的新failedEmails.csv以正确的格式-允许我们将数据复制到validEmails.csv再次尝试发送。

恭喜你!你现在已经构建了一个成功且功能齐全的工具来发送电子邮件爆炸。你可以查看完整的代码如果你想确认你的工作。但是请继续阅读一些可选的"不错"功能。

电子邮件工具的可选功能

因为我们的工具是基于CLI的(意味着它在命令行界面或终端中使用), 所以用户反馈很少。我们可以利用一些附加的控制台功能来提供有关脚本进度的更多信息。

让我们从添加一些"检查点"开始。在验证环境变量之前, 让我们打印一条消息, 表明脚本已启动并且正在检查变量:

console.info('Script started. Validating environment variables...')

console.info指示这是参考消息

然后, 在验证之后, 我们可以打印成功消息。

// Here we set the SendGrid API key
sgMail.setApiKey(apiKey);

console.info('Variables confirmed!')

在读取退回文件的功能内, 我们可以添加一些有关启动, 失败和成功的消息。

console.info('Reading bounced email list...')

// Read through the bounce list, parse into array
readFile(bouncePath, "utf8", (err, data) => {
  if (err) {
    console.error(err);
    console.error('Failed to read bounced emails!')
    process.exit(1);
  }
  bounceList = data.split("\n").slice(1);

console.info('Bounced emails read!')

对于我们的有效电子邮件列表也是如此:

console.info('Reading send list...')
// This is where we start reading the file!
readFile(filePath, "utf8", (err, data) => {
  if (err) {
    console.error(err);
    console.error('Failed to read send list!')
    return;
  }

现在, 当操作完成时打印一条消息将非常好。但是, 如果我们添加一个console.info在我们之后.for每个循环, 它实际上会打印之前电子邮件已发送完毕!

这是因为。发送方法创建网络调用并返回一个Promise, 并且在迭代完成之前该Promise可能尚未解析/拒绝。

因此, 我们可以建立一个计数器来跟踪已发送的电子邮件数与电子邮件总数。就在我们的面前.for每个循环, 添加以下变量:

// Here we create variables for counting
    const emailTotal = emailList.length;
    let emailCount = 0;

通过将.length分配给变量, 我们避免了每次迭代都必须读取它。

我们希望将退回的电子邮件计为已处理, 即使我们跳过它们也是如此。

// Here we iterate through the emailList array
  emailList.forEach((user) => {
    // Here we check if the email has been bounced
    if (bounceList.includes(user.email)) {
      console.info(`Message send skipped: ${user.email}`);
      emailCount++;
      if (emailCount === emailTotal) {
        console.info(
          `Sending complete! Sent ${emailTotal} emails. Have a nice day!`
        );
        return;
      }
    }

最后, 我们需要添加逻辑以查看我们发送的电子邮件是否为最后一封电子邮件。这个逻辑适用于send调用的成功和错误处理程序:

// Here we send the message we just constructed!
    sgMail
      .send(message)
      .then(() => {
        // Here we log successful send requests
        console.info(`Message send success: ${user.email}`);
        // Here we handle the email counts
        emailCount++;
        if (emailCount === emailTotal) {
          console.info(
            `Sending complete! Sent ${emailTotal} emails. Have a nice day!`
          );
        }
      })
      .catch((err) => {
        // Here we log errored send requests
        console.error(err);
        console.error(`Message send failed: ${user.email}`);
        // And here we add that email to the failedEmails.csv
        failedStream.write(`${user.email}, ${user.unsubscribeId}\n`);
        // Here we handle the email counts
        emailCount++;
        if (emailCount === emailTotal) {
          console.info(
            `Sending complete! Sent ${emailTotal} emails. Have a nice day!`
          );
        }
      });

至此, 我们的应用程序已完全完成!如果你运行npm运行构建和npm运行发送脚本, 你应该在终端中看到以下输出:

如何使用SendGrid API发送电子邮件通讯12

完整应用程序的示例控制台输出。

而且你应该已经收到了几封类似于以下内容的电子邮件:

如何使用SendGrid API发送电子邮件通讯13

测试电子邮件结果的样本图像

你可以在这里查看我们的最终代码, 或者你可以查看扩展版本为notlogy构建。

一盏木

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: