Twilio SendGridとNode.jsで登録者にメール通知を送信

December 12, 2021
執筆者
レビュー担当者

Send Email Notifications to Subscribers with SendGrid and Node.js Header JP

この記事はDhruv Patelこちらで公開した記事(英語)を日本語化したものです。

はじめに

一日中端末に向かい、商品の値下げを待つのは面倒な作業です。メール通知システムなら、一番欲しい商品を逃すことはありません。お探しのものが話題の靴であれ、リビングルームのソファーであれ、すべて自動化することで、テクノロジーをご自身のために活用いただけます。

本稿では、メールによる価格通知システムを構築する方法を学びます。Sneaks APIを使用してスニーカーの価格を追跡し、特定のスニーカーの価格が下がるとTwilio SendGridのメールアラートを配信するシステムです。

必要条件

本稿の内容を理解いただくために、以下が必要となります。

環境設定を行う

このセクションでは、まずTwilio SendGridアカウントを設定し、メール通知システムのベースを構築することで、環境設定を行います。

Twilio SendGridのAPIキーを作成

まず、Twilio SendGridのアカウントにログインし、管理コンソールの [API Keys] 画面に移動します。右上の [Create API Key](APIキーを作成)ボタンをクリックします。

TSG APIKey JP

APIキーの名前を指定し、[Create & View](作成と表示)ボタンをクリックします。ご自身のAPIキーをメモし、安全な場所に保存します。

APIキーのメモ&保存は大切なステップですので、忘れずに実施してください。

開発プロジェクトの構造を作成する

次に、Node.jsプロジェクトのベースを任意のディレクトリに構築します。ターミナルまたはコマンドプロンプトで、任意のディレクトリに移動して次のコマンドを実行します。

mkdir email-notifier
cd email-notifier

続いて、新しいNode.jsプロジェクトを開始し、このプロジェクトに必要な依存関係をインストールします。

npm init -y
npm install @sendgrid/mail dotenv sneaks-api fs node-schedule

Twilio SendGrid APIを使用してメールを送受信するにあたり、@sendgrid/mailパッケージが必要となります。dotenvは環境変数にアクセスするために使われるものであり、APIとやりとりするために必要なSendGrid APIキーを格納します。sneaks-apiパッケージはスニーカーの価格を追跡するために使われます。fsパッケージにより、登録者データベースとやり取りできるようになります。(登録者データベースは、JSONファイルでシミュレートして構成します。)最後のnode-scheduleパッケージは、価格チェッカーのロジックをスケジュール化するために使います。

本番アプリでは、実際のデータベースを使用して登録者データを管理することをお勧めします。ファイルにデータを保存すると、ファイルの破損、サイズの制限、ロックなどの問題が発生しがちです。今回の開発プロジェクトで実際のデータベースを使用して登録者データを管理したい場合、このチュートリアルをご確認ください。

Node.jsアプリの依存関係を初期化したら、次のステップとして任意のテキストエディタを開き、email-notifierディレクトリ内に3つの新しいファイル(index.js.envsubscribers.json)を作成します。

index.jsファイルは継続的に実行され、スニーカーが一定のしきい値を下回ると登録者に通知します。.envファイルには、環境変数がすべて安全に保存されます。subscribers.jsonには、すべての登録者とそのデータが格納されます。

先ほど作成したSendGrid APIキーは、.envファイルに安全に格納することができます。.envファイルを開き、以下をコピー&ペーストします。

SENDGRID_API_KEY=XXXXXXX

.envファイルにコピーした後、XXXXXXXの部分を実際のAPIキーに置き換えてください。

登録者のシミュレーションとスケジューラの構築

ある商品の価格変更を登録者にアラート通知する価格通知機能を作成しているため、アラートの送信先である登録者が必要となります。通常、商用アプリケーションではデータベースで登録者を管理しますが、今回は仮の登録者を何名か作成し、JSONファイルにオブジェクトとして保存します。以下のコードをコピーし、subscribers.jsonファイルに貼り付けてください。

 {
   "email": "you@example.com"

Subscribers(登録者)配列の各オブジェクトの“email”キーは、その登録者のメールアドレスを格納します。ここには、必ずご自身のメールアドレスを入力してください。“styleID”キーは追跡中のスニーカーのスタイルIDを、“lastResellPrice”キーは最後に追跡したスニーカーの価格を格納します。

メール通知システムは、登録者が追跡中しているスニーカーの価格が引き下げられたときに、アラートを送信する必要があります。先ほど作成したindex.jsファイルは、登録者のコレクションを調べ、追跡したスニーカーの価格があるしきい値よりも下であるかどうかをチェックします。もし価格が引き下げられていれば、ユーザーにアラートメールを送ります。

index.jsファイルの先頭に、次のコードを挿入してください。

require('dotenv').config()
const SneaksAPI = require('sneaks-api');
const fs = require("fs");
const sneaks = new SneaksAPI();
const schedule = require('node-schedule');
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const jsonString = fs.readFileSync("./subscribers.json");
const subscribers = JSON.parse(jsonString);

このコードはSendGrid APIをロードし、dotenvを使用して、.envファイルに格納されているAPIキーでAPIをロードします。そこには、sneaks-apiパッケージ、fsモジュール、subscribers.jsonファイルも含まれています。登録者ファイルにあるメールアドレスをご自身のメールアドレスに置き換えることで、価格アラートのテストやチェックが(他人の支援を必要とせず)行えるようになります。

次のコードスニペットがメインの関数です。すべての登録者をループし、現在のスニーカーの価格を取得し、価格が引き下げられた際にメールアラートを送信します。これをindex.jsファイルに追加してください。なお、ヘルパー関数(getSneakerMap())への参照に気づいたことでしょう。この関数は次のセクションで追加します。

(async function main() {
   const sneakerMap = await getSneakerMap(subscribers);
   for await (const subscriber of subscribers) {
       if (sneakerMap[subscriber.styleID].price < subscriber.lastResellPrice - 10) {
           notifySubscriber(sneakerMap[subscriber.styleID], subscriber.email);
           subscriber.lastResellPrice = sneakerMap[subscriber.styleID].price;
       }
   }
   fs.writeFileSync("./subscribers.json", JSON.stringify(subscribers));
})()

getSneakerMap()関数は、登録者コレクション内の全スニーカースタイルIDと現在の価格のマップを出力します。このマップが生成されると、コードは全登録者をループし、マップ内の現在のスニーカーをチェックし、スニーカーの価格が引き下げられたかどうかを確認します。

下げ幅が10ドル以上であれば、notifySubscriber()関数を実行してユーザーにアラート通知を送り、その登録者のスニーカーの直近再販価格をスニーカーマップからの値で更新し、次の登録者に移ります。このループが終了すると、subscribers.jsonファイルが新しい価格で更新されたことになります。

ヘルパー関数

メインの関数のすぐ下に、ヘルパー関数を配置します。これらのヘルパー関数は、コードをよりモジュール化し、読みやすくするために使用します。

getSneakerMap()関数は、登録者の一覧を入力パラメータとして受け取り、この一覧を反復処理し、sneaksApiFunctionWrapper()を使用してsneaks-apiから全スニーカーオブジェクトを取り込みます。これらのオブジェクトは、スタイルIDをキー値とするマップに配置されます。この関数を、index.jsmain()関数の後に追加してください。

async function getSneakerMap(subscribers) {
   var sneakerMap = new Object();
   for (const subscriber of subscribers) {
       if (sneakerMap[subscriber.styleID]) continue;
       const sneaker = await sneaksApiFunctionWrapper(subscriber.styleID);
       sneakerMap[subscriber.styleID] = sneaker;
   }
   return sneakerMap;
}

notifySubscriber()関数はスケジューラの中核となるもので、登録者に通知する必要がある場合に実行され、スニーカーとメールアドレスを入力パラメータとして取り込み、SendGrid APIを使用してアドレスにアラートメールを送信します。この関数は、SendGrid Node.jsヘルパーライブラリで先ほど初期化したsgMail変数を使用します。SendGrid APIは、プレーンテキストの通知メールを送信する代わりに、HTMLでスタイル化したメールを送信する方法を提供します。6行目では、createEmailInHTML()関数で生成されたメールオブジェクトに、スタイル化したHTMLドキュメントを差し込んでいます。

index.jsgetSneakerMapの直後に、notifySubscriberを追加してください。

function notifySubscriber(sneaker, email) {
   const msg = {
       to: email,
       from: SENDER_EMAIL, // Change to your verified sender
       subject: `Price Drop - ${sneaker.name}`,
       html: createEmailInHTML(sneaker),
   }
   sgMail.send(msg);
}

SendGridを使用する場合、単一送信元の検証またはドメイン認証を完了し、送信元IDを確認することをお勧めします。送信元IDの確認については、ブログ記事「Twilio SendGrid Emailのドメイン認証手順 (チュートリアル)」をご確認ください。

4行目のSENDER_EMAILの部分をご自身のメールアドレスに置き換えてください。

sneaksApiFunctionWrapper()関数は、sneaks-apiパッケージを利用して、メール本文を構成する情報をフォーマットして提供します。index.jsnotifySubscriberの後に、以下の関数を追加してください。

function sneaksApiFunctionWrapper(styleID) {
 return new Promise((resolve, reject) => {
   sneaks.getProductPrices(styleID, function (err, product) {
     const lowestResellSite = Object.keys(product.lowestResellPrice).reduce((a, b) => product.lowestResellPrice[a] > product.lowestResellPrice[b] ? a : b);
     const sneaker = {
       name: product.shoeName,
       image: product.thumbnail,
       site: lowestResellSite,
       price: product.lowestResellPrice[lowestResellSite],
       url: product.resellLinks[lowestResellSite],
       styleID: product.styleID
     };
     resolve(sneaker);
   }, (errorResponse) => {
     reject(errorResponse);
   });
 });
}

最後に、createEmailInHTML()がスニーカーを入力パラメータとして取り込み、スタイル化したドキュメントを生成します。このドキュメントには、価格の低下、スニーカーの画像、ユーザーを商品ページにリダイレクトさせるボタンが、表示要素として含まれます。

function createEmailInHTML(sneaker) {
   return `
   <h2>
     <img style="font-size:14px;display:block;margin-left:auto;margin-right:auto;width:200px; padding-bottom:15px"
       src="https://raw.githubusercontent.com/druv5319/Sneaks-API/master/Screenshots/Sneaks_Logo.png" />
   </h2>
   <div
     style=" margin-left: auto; margin-right: auto;box-sizing:border-box;width:45%; margin-bottom:30px;background:#fdfdfd;border:1px solid #f0f0f0">
     <h2 style="text-align: center; color: black; font-family: 'avenir';">Price Drop - ${sneaker.name}</h2>
     <p>
       <img style="display: block; margin-left: auto; margin-right: auto;" src=${sneaker.image} alt="" width="auto"
         height="250" />
     </p>
     <h2 style="text-align: center; color: black; font-family: 'avenir';">$${sneaker.price}</h2>
     <p>&nbsp;</p>
     <p style="font-size: 1.5em; color: black; font-family: avenir; text-align: center;">${sneaker.name} has dropped to
       <strong>$${sneaker.price}</strong> on <strong>${sneaker.site}</strong></p>
     <p style="text-align: center;">
       <a href="${sneaker.url}"
         style="box-sizing:border-box;border-color:#348eda;font-weight:400;text-decoration:none;display:inline-block;margin:0;color:#ffffff;background-color:#348eda;border:solid 1px #348eda;border-radius:2px;font-size:14px;padding:12px 45px; font-weight: 600;">Buy
         now on ${sneaker.site}</a>
     </p>
   </div>`;
}

通知機能をテストする

これでindex.jsファイルのビルドが完了しました。次はテストです!

index.jsファイルの全コードはこちらでご確認ください。読者の皆さんはindex.jsファイルを少しずつ実装していったので、コードが通しで正しいかのチェックにお使いいただけます。

ターミナルでプロジェクトのディレクトリに移動します。以下のコマンドを貼り付けてください。(まだ実行はしないでください。)

node index.js

このコマンドはindex.jsファイルを実行し、スニーカーが値下がりした際に、subscribers.jsonファイル内のメールアドレスに通知します。ただし、このコマンドを実行する前に、subscriber.jsonファイル内の既存の価格をより高い値へ、メールアドレスをご自身のメールアドレスへとそれぞれ変更し、通知機能が働いてメールがご自身に届くことを確認します。

では、ターミナルでコマンドを実行してください。数秒後に値下げに関するメールが受信ボックスに届きます。

TSG Sneaker JP

動作しましたか?メインロジックの完成です!

スケジューラを作成する

メインの関数は、登録者が追跡するスニーカーの価格が引き下げられたときにアラートを送信するように、スケジュールに基づいて自動的に実行される必要があります。このため、node-scheduleパッケージを使用して時間ベースのジョブをスケジュールします。これについて詳しくはブログ「SendGridを使用してNode.jsで定期的にメールを送信する方法(英語)」をご確認ください。

index.jsファイルのメインの関数を以下のように置き換えます。

const job = schedule.scheduleJob('*/10 * * * *', function () {
    (async function main() {
        const sneakerMap = await getSneakerMap(subscribers);
        for await (const subscriber of subscribers) {
            if (sneakerMap[subscriber.styleID].price < subscriber.lastResellPrice - 10) {
                notifySubscriber(sneakerMap[subscriber.styleID], subscriber.email);
                subscriber.lastResellPrice = sneakerMap[subscriber.styleID].price;
            }
        }
        fs.writeFileSync("./subscribers.json", JSON.stringify(subscribers));
    })()
});

上記コードスニペットでハイライトされた行は、ここでのメインの関数がscheduleJob()と呼ばれる別の関数でラップされていることを示します。この別の関数は入力パラメーターとしてcron式*/10 * * * *を取り込んでおり、10分ごとに実行するよう関数に指示しています。

ファイルを保存し、ターミナルでnode indexにより再実行すれば完了です!このスクリプトは継続的に実行され、10分ごとに登録者リストからスニーカーの価格が引き下げられたかどうかをチェックします。

まとめ

常に価格を注視し続けることは、「やることリスト」に新たな項目を増やすだけで、まったく効率的ではありません。メール通知機能を使えば、希望する価格に達し、手に入れることができるときに知らせることができます。Node.jsとTwilio SendGridなら、価格変動の一歩先を行くことができます。刻々と変化する市場において、メール通知は、あなたとあなたの最も切望するアイテムとの間をつなぐ架け橋となります。

また、通知機能の機能強化が色々と考えられます。実際のデータベースに接続して通知機能を拡張したり、Twilio SendGridの別プロダクト「Maketing Campaigns」を活用して、メールリストを使用したり、スニーカー情報メールの配信停止をユーザーが行う方法を提供したりできます。スケジューラをよりよく制御するには、PM2ForeverなどのNode.jsプロセスマネージャーを実装し、スクリプトをデーモンとして実行し、アプリケーションが24時間常時稼動するようにします。

Twilio SendGrid Email でコミュニケーションの未来を構築しましょう!

Dhruv Patel: TwilioのDeveloper Voicesチーム所属の開発者。お問い合わせは、コーヒーショップで水出しコーヒーを飲みながら仕事しているところを見つけるか、dhrpatel [at] twilio.comまたはLinkedInまで。