Notionのデータベースから一斉メール配信する
Notionは最近注目度の高い情報共有サービスです。Wikiのように自由テキストを保存したり、データベースのように構造化されたデータを保存もできます。
今回はそんなNotion内にあるメールアドレスを使って、blastengineと組み合わせてみました。
機能について
- データベースにある顧客リストのメールアドレス宛にメールを一括送信
- エラーリストを使って次回以降送信しないようにNotionのデータを更新
前半となるこの記事ではデータベースにある顧客リストのメールアドレス宛にメールを一括送信について実装します。
ユーザ登録する
blastengineにユーザ登録します。管理画面に入るためのユーザID、パスワードが手に入るので、ログイン又は無料トライアルからアカウントを作成します(ユーザIDは後で使います)。
送信元ドメインのSPFを設定する
送信元として利用するドメイン(自分で持っているもの)の設定をします。これは任意のドメイン管理サービスで設定できますが、TXTレコードに以下のSPFを追加します。
txt @ v=spf1 include:spf.besender.jp ~all
APIキーを取得する
ログイン後、管理画面の右上にある設定メニューに移動します。
そして設定の中で、APIキーを取得します。
Notionと連携するNoway Formを使う
まずNotionのデータベースにメールアドレスを登録する仕組みを構築します。今回はNoway Formを利用しています。Noway Formはお問い合わせフォームを作成できるサービスで、そのデータをNotionのデータベースに保存できます。
データベースを作成する
作成するデータベースでは、お問い合わせフォームに必要な項目を登録しておく必要があります。今回は以下の項目を作成しました。
項目名 | 型 |
---|---|
会社名 | テキスト |
名前 | テキスト |
メールアドレス | メールアドレス |
お問い合わせ内容 | テキスト |
送信不可 | チェックボックス |
送信不可はデフォルトチェックなしなので、フォームから入力した段階では全データが送信対象になります。
Noway Formと連携する
Noway FormのWebサイトからNotionの連携と、フォーム設計を進めていきます。
完了したら一度テストで登録してみると良いでしょう。無事データベースにデータが追加できていれば完了です。
Notionインテグレーションを作る
Notionのインテグレーションページにて、新しいインテグレーションを作成します。今回は blastengine
としています。名前やワークスペースを選択すればできあがりです。
内部インテグレーショントークン
というのが生成されるので、メモしておきます。
ページを作る
メールの件名、本文に利用するNotionページを作成します。ページのタイトルがメールの件名、ページの内容がメール本文になります。
インテグレーションを追加する
先ほど追加したデータベース、メール本文用のページに対してNotionインテグレーションを追加します。
右上にある三点リーダーよりコネクトの追加を選択し、作成したインテグレーション(今回は blastengine
を追加します)。
アプリを作る
今回は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になります。
名前やメールアドレスのフィールドの名前は日本語のもので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に反映する流れを解説します。