SMTP・API連携で高速メール配信するならブラストエンジン

Notionのデータベースから一斉メール配信する

2022年11月6日 Notion

Notionは最近注目度の高い情報共有サービスです。Wikiのように自由テキストを保存したり、データベースのように構造化されたデータを保存もできます。

今回はそんなNotion内にあるメールアドレスを使って、blastengineと組み合わせてみました。

機能について

  • データベースにある顧客リストのメールアドレス宛にメールを一括送信
  • エラーリストを使って次回以降送信しないようにNotionのデータを更新

前半となるこの記事ではデータベースにある顧客リストのメールアドレス宛にメールを一括送信について実装します。

ユーザ登録する

blastengineにユーザ登録します。管理画面に入るためのユーザID、パスワードが手に入るので、ログイン又は無料トライアルからアカウントを作成します(ユーザIDは後で使います)。

Untitled.png


送信元ドメインのSPFを設定する

送信元として利用するドメイン(自分で持っているもの)の設定をします。これは任意のドメイン管理サービスで設定できますが、TXTレコードに以下のSPFを追加します。

txt @ v=spf1 include:spf.besender.jp ~all

APIキーを取得する

ログイン後、管理画面の右上にある設定メニューに移動します。

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/47b2b2ac-2e41-77fe-d84f-0ae0b7541f36.jpeg

そして設定の中で、APIキーを取得します。

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2b5ca124-3020-1d39-84e4-911cd4db27e2.png

Notionと連携するNoway Formを使う

まずNotionのデータベースにメールアドレスを登録する仕組みを構築します。今回はNoway Formを利用しています。Noway Formはお問い合わせフォームを作成できるサービスで、そのデータをNotionのデータベースに保存できます。

Untitled 1.png

データベースを作成する

作成するデータベースでは、お問い合わせフォームに必要な項目を登録しておく必要があります。今回は以下の項目を作成しました。

項目名
会社名テキスト
名前テキスト
メールアドレスメールアドレス
お問い合わせ内容テキスト
送信不可チェックボックス

送信不可はデフォルトチェックなしなので、フォームから入力した段階では全データが送信対象になります。

Noway Formと連携する

Noway FormのWebサイトからNotionの連携と、フォーム設計を進めていきます。

Untitled.jpeg

完了したら一度テストで登録してみると良いでしょう。無事データベースにデータが追加できていれば完了です。

Untitled 1.jpeg

Notionインテグレーションを作る

Notionのインテグレーションページにて、新しいインテグレーションを作成します。今回は blastengine としています。名前やワークスペースを選択すればできあがりです。

Untitled 2.png

内部インテグレーショントークン というのが生成されるので、メモしておきます。

Untitled 3.png

ページを作る

メールの件名、本文に利用するNotionページを作成します。ページのタイトルがメールの件名、ページの内容がメール本文になります。

Untitled 4.png

インテグレーションを追加する

先ほど追加したデータベース、メール本文用のページに対してNotionインテグレーションを追加します。

右上にある三点リーダーよりコネクトの追加を選択し、作成したインテグレーション(今回は blastengine を追加します)。

Untitled 5.png


アプリを作る

今回はNotionのJavaScript SDKを使います。これはNode.js向けに提供されています。まず適当なフォルダを作成します。

mkdir notion-app
cd notion-app

Node.jsプロジェクトとして初期化します。Node.jsはインストール済みであることとします。

npm init -y

ライブラリをインストール

必要なライブラリをインストールします。

  • @notionhq/client
    Notion SDK
  • blastengine
    blastengine SDK
  • dotenv
    環境変数の追加

開発用ライブラリは以下の通りです。

  • TypeScript
    TypeScriptサポート用
  • ts-node
    TypeScript用ファイルを実行するのに必要
npm i @kintone/kintone-js-sdk
npm i blastengine
npm i dotenv
npm i typescript -D
npm i ts-node -D

ファイルの作成

今回は mail.ts というファイルを作成します。また、プロジェクト直下に .env ファイルを作成し、環境変数を記述します。以下は .env ファイルの内容です。

NOTION_SECRET=Notionの内部インテグレーショントークン
DB_ID=NotionのデータベースページのID(URLから取得)
CHECKBOX_ID=送信不可フラグのID
EMAIL_PROPERTY=メールアドレスの入っているフィールドの名前
NAME_PROPERTY=名前が入っているフィールドの名前
PAGE_ID=NotionのメールテンプレートページのID(URLから取得)

BLASTENGINE_USERID=blastengineのユーザー名
BLASTENGINE_APIKEY=blastengineのAPIキー
FROMNAME=送信元の名前
FROMADDRESS=送信元のメールアドレス

送信不可フラグのID

これは検索時に利用するのですが、これはフィルターを実行してみた結果から取得しました(開発者ツールにて)。データベースのプロパティを調べて取得するようにすると、もう少し簡単かも知れません。筆者の例で言えば、Kcrxになります。

Untitled 6.png


名前やメールアドレスのフィールドの名前は日本語のものでOKです。

ライブラリ読み込み

ここからは mail.ts への記述です。まず最初に必要なライブラリを読み込みます。

// blastEngine SDKとBulkを読み込む
import { BlastEngine, Bulk } from 'blastengine';
import { Client } from '@notionhq/client';

// dotenvを読み込む
import * as dotenv from 'dotenv';
import { PageObjectResponse } from '@notionhq/client/build/src/api-endpoints';
dotenv.config();

Notionクライアントの初期化への接続情報を作成する

.env ファイルに定義したシークレットを使って、Notionクライアントを作成します。

// Notion APIの認証を設定
const notion = new Client({ auth: process.env.NOTION_SECRET });

blastengineの初期化

blastengine SDKを初期化します。こちらも .env ファイルの内容を使います。

// blastEngineを初期化する
new BlastEngine(process.env.BLASTENGINE_USERID, process.env.BLASTENGINE_APIKEY);

無名関数の定義

ここからはネットワーク処理があるので、非同期処理用にasync/awaitを定義します。

// メイン処理をasync関数で囲む
(async () => {
  // ここからはこの中に書いていきます
})();

Notionからレコードを取得

Notionを検索します。クエリーとして、メールアドレスが入力されていることと、配信対象として設定されていることを定義しています。

const ary = await fetchAll(process.env.DB_ID!);

fetchAll関数は以下のようになります。関数は (async () => { 外で問題ありません。

// Notionデータベースから未送信の情報を取得する関数
const fetchAll = async (databaseId: string): Promise<{email: string, name: string}[]> => {
	const res = await notion.databases.query({
    database_id: databaseId,
		filter: {
			and: [{
				property: process.env.CHECKBOX_ID!,
				checkbox: {
					equals: false,
				}
			}]
		}
	});
	return (res.results as PageObjectResponse[]).map(row => {
		const properties = row.properties as {[key: string]: any};
		const name = (properties[process.env.NAME_PROPERTY!].title[0].plain_text);
		const email = properties[process.env.EMAIL_PROPERTY!].email;
		return {
			name, email,
		}
	});
};

メール本文の読み込み

メール本文、件名を取得します。

const { title, body } = await getTemplate(process.env.PAGE_ID!);

getTemplate 関数は以下のように定義しています。Notionではページタイトルと本文は別途作成するので、以下のように getTitle と getBody を別で呼び出しています。これらの関数は (async () => { 外で問題ありません。

// Notionページからテンプレートのタイトルと本文を取得する関数
const getTemplate = async (pageId: string): Promise<{title: string, body: string}> => {
	const title = await getTitle(pageId);
	const body = await getBody(pageId);
	return { title, body };
}

// Notionページからタイトルを取得する関数
const getTitle = async (pageId: string): Promise<string> => {
	const res = await notion.pages.retrieve({ page_id: pageId }) as any;
	return res.properties.title.title[0].plain_text;
};

// Notionページから本文を取得する関数
const getBody = async (pageId: string): Promise<string> => {
	const { results } = await notion.blocks.children.list({
		block_id: pageId,
		page_size: 200,
	});
	const ary = (results as any[])
		.map(row => 
			row.paragraph.rich_text && row.paragraph.rich_text[0]
				? row.paragraph.rich_text[0].plain_text
				: ''
		);
	return ary.join("\n");
};

メールの内容は以下のようにしています。 __name__ は配信先毎に置き換えられる文字列です。

__name__さん、こんにちは

これはメールの本文です。

バルクメールオブジェクトの作成

blastengineのバルクメールオブジェクトを作成します。これは一括配信用のオブジェクトです。

// Bulkオブジェクトを作成する
const bulk = new Bulk();

作成したバルクメールオブジェクトに、件名や送信元情報などを適用します。

// Bulkオブジェクトにメールの情報を設定する
await bulk
	.setFrom(process.env.FROMADDRESS, process.env.FROMNAME)   // 送信元のメールアドレスと名前
	.setSubject(process.env.SUBJECT)   // 件名
	.setText(text)   // 本文
	.register();   // 送信するメールの情報をblastEngineに登録する

Notionのレコード情報を配信先として設定

先ほど取得したNotionのレコードから、メールアドレスと名前を使って配信先として登録します。1つ目の引数がメールアドレス、2つ目の引数は置き換え文字列(メール本文で指定した __name__ と置き換えられます)になります。

// レコードを処理し、Bulkオブジェクトに宛先を追加する
ary.map(({email, name }) => {
	bulk.addTo(email, { name });
});

宛先情報を反映

登録した宛先情報をblastengineに反映します。

// blastEngineに登録したメールの情報を更新する
await bulk.update();

送信する

後はメールを送信するだけです。引数として、配信日時も指定できます。

// blastEngineを使ってメールを送信する
await bulk.send();

配信が完了すれば、配信ID(デリバリーID)が付与されています。

// 送信が完了したことをログに出力する
console.log(`送信しました ${bulk.delivery_id}`);

スクリプトの全体像

mail.ts の内容は以下の通りです。

// blastEngine SDKとBulkを読み込む
import { BlastEngine, Bulk } from 'blastengine';
import { Client } from '@notionhq/client';

// dotenvを読み込む
import * as dotenv from 'dotenv';
import { PageObjectResponse } from '@notionhq/client/build/src/api-endpoints';
dotenv.config();

// Notion APIの認証を設定
const notion = new Client({ auth: process.env.NOTION_SECRET });

// blastEngineを初期化する
new BlastEngine(process.env.BLASTENGINE_USERID!, process.env.BLASTENGINE_APIKEY!);

// Notionデータベースから未送信の情報を取得する関数
const fetchAll = async (databaseId: string): Promise<{email: string, name: string}[]> => {
	const res = await notion.databases.query({
    database_id: databaseId,
		filter: {
			and: [{
				property: process.env.CHECKBOX_ID!,
				checkbox: {
					equals: false,
				}
			}]
		}
	});
	return (res.results as PageObjectResponse[]).map(row => {
		const properties = row.properties as {[key: string]: any};
		const name = (properties[process.env.NAME_PROPERTY!].title[0].plain_text);
		const email = properties[process.env.EMAIL_PROPERTY!].email;
		return {
			name, email,
		}
	});
};

// Notionページからテンプレートのタイトルと本文を取得する関数
const getTemplate = async (pageId: string): Promise<{title: string, body: string}> => {
	const title = await getTitle(pageId);
	const body = await getBody(pageId);
	return { title, body };
}

// Notionページからタイトルを取得する関数
const getTitle = async (pageId: string): Promise<string> => {
	const res = await notion.pages.retrieve({ page_id: pageId }) as any;
	return res.properties.title.title[0].plain_text;
};

// Notionページから本文を取得する関数
const getBody = async (pageId: string): Promise<string> => {
	const { results } = await notion.blocks.children.list({
		block_id: pageId,
		page_size: 200,
	});
	const ary = (results as any[])
		.map(row => 
			row.paragraph.rich_text && row.paragraph.rich_text[0]
				? row.paragraph.rich_text[0].plain_text
				: ''
		);
	return ary.join("\n");
};

(async () => {
	const ary = await fetchAll(process.env.DB_ID!);
	const { title, body } = await getTemplate(process.env.PAGE_ID!);
	const bulk = new Bulk();

	// Bulkオブジェクトにメールの情報を設定する
	await bulk
		.setFrom(process.env.FROMADDRESS!, process.env.FROMNAME) // 送信元のメールアドレスと名前
		.setSubject(title)   // 件名
		.setText(body)   // 本文
		.register();   // 送信するメールの情報をblastEngineに登録する
	// レコードを処理し、Bulkオブジェクトに宛先を追加する
	ary.map(({email, name }) => {
		bulk.addTo(email, { name });
	});
	
	// Bulkオブジェクトを更新する
	await bulk.update();
	
	// Bulkオブジェクトでメールを送信する
	await bulk.send();
	
	// 送信完了メッセージを表示する
	console.log(`送信しました ${bulk.delivery_id}`);
})();

実行する

以下のようにしてコマンドプロンプトやターミナルで実行します。

npx ts-node mail.ts

以下のようなメッセージが出れば配信されているはずです。

送信しました 105

まとめ

今回はNotionのデータベースからメールアドレス情報を取得して、blastengineで配信するまでの流れを紹介しました。次回は配信結果をNotionに反映する流れを解説します。

エンジニア向けメール配信システム「ブラストエンジン(blastengine)」

blastengine(プラストエンジン)ロゴ

エンジニアを面倒なメールに関するトラブルから解放するために作られたブラストエンジン
まずは無料トライアルで、その使いやすさを実感してみませんか?

\メールアドレス入力のみ/

無料トライアル