【GAS】【Notion API】Gmailに届くメルマガを自動でNotionへ出力・管理する方法

## はじめに


私は、毎日とあるメールマガジンをGmailで受信しているのですが、中身を丁寧に読み、重要な点をメモし、知的資産として蓄積していくことが難しいと感じていました。Gmail上では文字を書き込んだり、目立たせたりすることが限られているうえ、情報量が膨大なので、把握と整理に相当の労力が必要だからです。

そこで注目したのが、メールマガジンを **Notion** に保存できる **Chromeの拡張機能 「Save to Notion」** というツールです。Notionなら情報を自由に整理し直せるうえ、書き込みなどもできるため、知的資産としての活用に適していると考えたのです。しかしこのツールも、毎回１クリックとはいえ、手動での保存が必要で、すこし、面倒くさいなと感じていました。

そこで、 **Google Apps Script(GAS)** を使ってGmailに毎日届くメールマガジンをNotionに自動で保存することを実現してみようと思いました。

この記事では、 **Google Apps Script(GAS)を使用して、Gmailで新しいメールを受信した際に自動的にNotionに情報を転送する方法** をご紹介します。

## 必要なもの

- Googleアカウント（Gmailアクセス用）
- Notionアカウントとインテグレーショントークン
- Googleスプレッドシート


## Gmailにとどくメルマガを毎日自動でNotionへ出力する手順

### 1. スプレッドシートの準備
#### a. スプレッドシートの作成

Googleドライブにアクセスし、「新規」ボタンをクリックします。

ドロップダウンメニューから「Google スプレッドシート」を選択し、新しいスプレッドシートを開きます。

スプレッドシートに適切な名前を付けます（例：「メールデータ」）。

#### b. シートの設定

スプレッドシートを開いたら、下部にある「シート1」をクリックして選択します。

「シート1」の名前を「メールマガジン」という名前に変更します。これにより、スクリプトがこのシートを識別しやすくなります。

シートの最初の行に、データのカラム名を入力します。例えば「タイトル」、「本文」、「受信日」といった具体的なカラム名です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686020/f7b49393-daec-128a-2ae9-cd42509b179d.png)

### 2. Google Apps Scriptの設定
#### a. スクリプトエディタの開き方

スプレッドシートのメニューバーで「拡張機能」をクリックし、「Apps Script」を選択します。

新しいスクリプトエディタが開きます。

#### b. スクリプトの記述

スクリプトエディタでは、新しいGASプロジェクトが自動的に作成されます。

ここに必要なコード（getEmail、addDataToNotionなど）を記述していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686020/01e210ab-8971-96b7-ab4b-963976628051.png)


### 3. メールデータの取得
このステップでは、Gmailから特定の条件でメールを検索し、そのデータ（タイトル、本文、受信日）をスプレッドシートに転送します。

#### a. getEmail関数の作成

getEmail関数は、Gmailからのメールを取得し、それらをスプレッドシートに保存するための主要な機能を果たします。

##### i. Gmailからメールを検索

Gmailの「検索条件」を指定して、特定のメールを見つけ出します。この「検索条件」はプログラミングでは「クエリ」と呼ばれます。

例えば、特定の送信者からのメール、特定のキーワードを含むメール、特定の日付に受信したメールなど、様々な条件を設定できます。

GmailApp.search メソッドを使用して、これらの条件に一致するメールを検索します。

##### ii. スプレッドシートにデータを書き込む

検索された各メールから、タイトル（メールの件名）、本文（メールの内容）、受信日（メールが受信された日付）を取得します。

受信日は Utilities.formatDate メソッドを使用して、年月日の形式（例: 2023-01-01）に変換します。

##### iii. スプレッドシートへの書き込み

スプレッドシートを取得し、操作するシート（この例では「メールマガジン」）を特定します。

スプレッドシートに新しい行として、メールのデータを追加します。

**サンプルコード** 

~~~JavaScript
function getEmail() {
    const query = "特定の条件"; // 例: "from:someone@example.com" で特定の送信者からのメールを検索
    const threads = GmailApp.search(query, 0, 10); // 最初の10件のメールを取得
    const messageThreads = GmailApp.getMessagesForThreads(threads);
    const data = [['タイトル', '本文', '受信日']];

    for (let messages of messageThreads) {
        const message = messages[0];
        data.push([
            message.getSubject(),
            message.getPlainBody(),
            Utilities.formatDate(message.getDate(), Session.getScriptTimeZone(), "yyyy-MM-dd")
        ]);
    }

    // スプレッドシートの操作
    const spreadsheetId = 'あなたのスプレッドシートID'; 
    const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
    const sheet = spreadsheet.getSheetByName('メールマガジン');
    sheet.getRange(sheet.getLastRow() + 1, 1, data.length, data[0].length).setValues(data);
}
~~~
この関数は、指定された条件に一致するメールをGmailから検索し、それらのデータをスプレッドシートに保存します。
これにより、Notionに転送するためのデータの準備が整います。

### 4. Notionへのデータ送信
このステップでは、スプレッドシートに保存されたメールデータをNotionに転送します。Notion APIへの認証とデータ送信のための手順が必要です。

 #### a. addDataToNotion関数の作成

addDataToNotion関数は、スプレッドシートからデータを取得し、それをNotionに送信する役割を担います。

 ##### i. Notion APIの認証情報を安全に管理

Notion APIと通信するためには、APIキー（トークン）とデータベースIDが必要です。この記事がわかりやすいです。[NotionAPIを使うための事前準備](https://note.com/kohaku935/n/n1ee543bb2cd0)

(これらの情報はセキュリティ上の理由から、スクリプト内に直接記述するのではなく、Google Apps Scriptの「スクリプト プロパティ」に安全に保存することが推奨されます。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686020/8ba8850a-2801-7f73-a844-4c5d46a12432.png)



忘れずに、作成したIntegrationを、データベースにコネクトさせましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686020/d73cb97c-e18f-1ac8-fd8d-154e702d5a5a.png)

##### ii. スプレッドシートからデータを読み込み

スプレッドシートにアクセスし、メールのデータ（タイトル、本文、受信日など）を取得します。

##### iii. ページオブジェクトの生成

Notionに送信するために、データを特定の形式（ページオブジェクト）に整形します。

##### iv. データの送信

Notion APIにページオブジェクトを送信し、新しいページを作成します。

**サンプルコード**
~~~JavaScript
function addDataToNotion() {
    // スクリプト プロパティから認証情報を取得
    const props = PropertiesService.getScriptProperties();
    const dbId = props.getProperty('NOTION_DB_ID');
    const token = props.getProperty('NOTION_TOKEN');
    const apiUrl = 'https://api.notion.com/v1/pages';

    const pageObject = generateNotionPageObject(dbId);
    if (pageObject) {
        postToNotion(apiUrl, token, pageObject);
    } else {
        console.log("ページオブジェクトの生成に失敗しました。");
    }
}
~~~

### 5. トリガーの設定
トリガーは、特定の関数を自動的に定期的に実行させるために設定します。この機能を使うことで、手動でスクリプトを実行する手間を省き、自動化を実現できます。

#### a. getEmail関数用のトリガー設定

getEmail関数は、新しいメールを定期的にチェックしてスプレッドシートに保存するためのものです。

トリガー設定の手順
 1.Google Apps Scriptのエディタで、画面の右上にある時計のアイコン（「トリガー」）をクリックします。

Google Apps Scriptのエディタで、画面の右上にある時計のアイコン（「トリガー」）をクリックします。

 ２．画面下部にある「トリガーを追加」ボタンをクリックします。

３．「実行する関数を選択」ドロップダウンメニューから getEmail を選択します。

４．「イベントのソースを選択」では「時間主導型」を選びます。

 ５．「時間ベースのトリガータイプを選択」で実行頻度（例えば「1時間ごと」や「1日ごと」）を選択します。

 ６．必要に応じて、他のオプションを調整し、「保存」をクリックします。

 これにより、getEmail関数は定期的に自動的に実行され、新しいメールがあるかどうかをチェックします。

 #### b. addDataToNotion関数用のトリガー設定

addDataToNotion関数は、スプレッドシートに保存されたメールデータをNotionに転送します。この関数も自動で定期的に実行されるようにトリガーを設定しますが、getEmail関数が実行された後に実行されるようにタイミングを調整することが重要です。

トリガー設定の手順

1.同じくGoogle Apps Scriptのエディタのトリガー設定画面にアクセスします。

2.「トリガーを追加」を再度クリックします。

3.今回は「実行する関数を選択」で addDataToNotion を選択します。

4.「イベントのソース」では「時間主導型」を選択し、getEmailより少し遅れる時間を設定します。

5.その他の設定を行い、「保存」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3686020/023c7303-e8aa-bd60-505b-9f4548320fb4.png)

この設定により、getEmail関数が新しいメールを検出し、スプレッドシートに保存した後に、addDataToNotion関数がそれらのデータをNotionに転送する流れが自動で行われます。


## スクリプトの全公開
このブログ記事を通じて、GmailからNotionへのメール自動転送の基本的な概念とステップをご紹介しました。これらの手順を実際に動かすために必要な、全スクリプトを以下に記録しておきます。

このスクリプトを利用することで、自動化と効率化を実現できます。

~~~javascript 
// ----- 関数：メールデータの取得とスプレッドシートへの書き込み -----
function getEmail() {
    const query = "これはメールマガジンのテストです。";
    const threads = GmailApp.search(query, 0, 1); // Start and Max values are inlined
    const messageThreads = GmailApp.getMessagesForThreads(threads);
    const data = [['タイトル', '本文', '受信日']];

    messageThreads.forEach(messages => {
        const message = messages[0];
        data.push([
            message.getSubject(),
            message.getPlainBody(),
            formatDate(message.getDate())
        ]);
    });

    const spreadsheetId = '*********************';
    writeToSpreadsheet(spreadsheetId, 'メールマガジン', data);
}

function formatDate(date) {
    return Utilities.formatDate(date, Session.getScriptTimeZone(), "yyyy-MM-dd");
}

function writeToSpreadsheet(spreadsheetId, sheetName, data) {
    const spreadsheet = SpreadsheetApp.openById(spreadsheetId);
    const sheet = spreadsheet.getSheetByName(sheetName) || createSheet(spreadsheet, sheetName);
    sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}

function createSheet(spreadsheet, sheetName) {
    console.log(`'${sheetName}' シートが見つかりません。新しいシートを作成します。`);
    return spreadsheet.insertSheet(sheetName);
}

// ----- Notionへのデータ追加 -----
function addDataToNotion() {
    const props = PropertiesService.getScriptProperties();
    const pageObject = generateNotionPageObject(props.getProperty('NOTION_DB_ID'));
    postToNotion('https://api.notion.com/v1/pages', props.getProperty('NOTION_TOKEN'), pageObject);
}

function generateNotionPageObject(dbId) {
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('メールマガジン');
    if (!sheet) {
        console.log("'メールマガジン' シートが見つかりません。");
        return null;
    }

    const title = sheet.getRange(2, 1).getValue();
    const content = sheet.getRange(2, 2).getValue();
    const rawDate = sheet.getRange(2, 3).getValue();
    const formattedDate = Utilities.formatDate(new Date(rawDate), "Asia/Tokyo", "yyyy-MM-dd");

    const blocks = splitTextIntoBlocks(content, 2000);

    return {
        parent: { database_id: dbId },
        properties: {
            "受信日": { "date": { "start": formattedDate, "end": null }},
            "タイトル": { "title": [{ "text": { "content": title }}]}
        },
        children: blocks.map(block => ({
            type: 'paragraph',
            paragraph: { rich_text: [{ type: 'text', text: { content: block } }] }
        }))
    };
}

function splitTextIntoBlocks(text, maxLength) {
    var blocks = [];
    for (let i = 0; i < text.length; i += maxLength) {
        blocks.push(text.substring(i, Math.min(i + maxLength, text.length)));
    }
    return blocks;
}

function postToNotion(apiUrl, token, pageObject) {
    if (!pageObject) {
        console.log('ページオブジェクトが存在しません。Notionへの投稿を中止します。');
        return;
    }

    const options = {
        method: "POST",
        headers: {
            "Content-type": "application/json",
            "Authorization": `Bearer ${token}`,
            "Notion-Version": '2022-06-28',
        },
        payload: JSON.stringify(pageObject),
        muteHttpExceptions: true
    };

    const response = UrlFetchApp.fetch(apiUrl, options);
    Logger.log(response.getContentText());
}
~~~
このまま貼り付けて大丈夫ですが、
このスクリプトの中でご自身で設定が必要なのは以下の項目です。

### １．保存したいメールのキーワード（または送信者）の設定

~~~Javascript
const query = "これはメールマガジンのテストです。";
~~~

「これはメールマガジンのテストです。」のかわりに、Notionに保存したいメールの送信者やキーワードを記入してください。

### ２．スプレッドシートIDの設定
~~~Javascript
 const spreadsheetId = '*********************';
~~~


ここにご自身のスプレッドシートIDを記入してください。「スプレッドシートID」というのは、
URL:  https://docs.google.com/spreadsheets/d/スプレッドシートID/edit#gid=シートID　の、スプレッドシートIDの部分です。

### ３．NOTION_DB_IDとNOTION_TOKEN の設定
~~~Javascript
    const pageObject = generateNotionPageObject(props.getProperty('NOTION_DB_ID'));
    postToNotion('https://api.notion.com/v1/pages', props.getProperty('NOTION_TOKEN'), pageObject);
~~~


NOTION_DB_ID　と　NOTION_TOKEN をスクリプトプロパティに忘れずに記入してください。（ここに直接書き込んでも大丈夫ですが、セキュリティ上の理由から、スクリプトプロパティに書いたほうが安全です。）