今日の日記を書いてないと怒られるLINE Botを作ってみる【Notion API/GAS】

## 概要

本記事では以下の方法を説明します

- Notion APIを使って、Notionの記事を取得する
- GASとMessaging APIで、定期的にLINEのメッセージを送信する

## 背景

最近の習慣として、Notionに日記を書いています。

https://www.notion.so/ja-jp/product?utm_source=google&utm_campaign=14877655427&utm_medium=125922091617&utm_content=552465064290&utm_term=notion&targetid=aud-840164194060:kwd-312974742&gclid=CjwKCAiAlrSPBhBaEiwAuLSDULMxVffd8_I7ufh7_qcgo80PA-pb05wDP949alGNg6rDC2VMgovTBhoCnbgQAvD_BwE

ただ、日記に限ったことではないですが、どうしても習慣が継続できない日も出てきてしまいます。
私は、自分の継続しよう！という気力を信用できません。
そこで、LINEを使って日記を書いていないときに通知してくれる仕組みを作成しました。

## つくったもの

22:00の時点で当日の日記を書いていないと、Botから怒りのLINEが来ます。
無視して日記を書かないでいると、10分毎にさらにLINEが続けてきます。

## 使用技術
### Notion API 

Notionでは、ページやデータベース、ユーザー、ブロックに対する操作が可能です。
基本的にCRUD全てできますが、データベースの削除だけはできないため、「ページからデータベースだけを消して空ページにする」といった操作は難しそうです。

詳しくは、ドキュメントを見ていただければと思います。

https://developers.notion.com/reference/intro

※ Notion APIを使用するためには、まず、インテグレーションを作成する必要があります。
`Getting started`の手順に従って進めるのが良いと思います。

今回必要な情報は、日記データベースの中に当日の日付のデータがあるかどうかです。
そのため、データベースのデータを取得し、`「日付プロパティ」 = 「当日」`という条件で絞り込むAPIを叩いています。

https://developers.notion.com/reference/post-database-query

この後GASのコードも紹介しますが、参考までに、curlで叩く時のコマンドも記載します。


```shell
# インテグレーションの作成時に発行したトークン
$ NOTION_API_KEY=XXX
# ページURLに含まれているデータベースID
$ DATABASE_ID=XXX

# "property": "日付"の部分は、Notion側のカラム名に合わせて調整                
$ curl -X POST "https://api.notion.com/v1/databases/$DATABASE_ID/query" \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H 'Notion-Version: 2021-08-16' \
  -H "Content-Type: application/json" \
	--data '{
	  "filter": {
	        "property": "日付",
            "date":{
              "equals": "2022-01-24"
            }
	      }
	}'
```

### Messaging API
LINEの通知には、Messaging APIを利用します。
https://developers.line.biz/ja/services/messaging-api/

LINEが提供している機能は様々ありますが、今回はシンプルにBotから自分に向けてメッセージを送れれば良いので、ブロードキャストメッセージしか使っていません。
ブロードキャストメッセージは、友だちになっているすべてのユーザーに、同じメッセージを送信するものです。
自分からBotにメッセージを送信したときに返事をする応答メッセージや、特定のユーザーに対してメッセージを送信するプッシュメッセージなどと比べ、簡単にメッセージの送信が可能です。

Messaging APIを使うには、チャネルアクセストークンが必要になります。
ドキュメントの手順に従って、チャネルを作成し、チャネルアクセストークンを発行します。
https://developers.line.biz/ja/docs/messaging-api/getting-started/

### GAS
今回、定期実行にはGASを使っています。
最近触る機会が多かったのでGASを選びましたが、スプシなど他ツールとの連携をしている訳ではないので、
CloudFunctionなど別の選択肢を選んでも良いと思います。

GASではおこなっている処理は、以下のような流れになります。

- 自分自身のトリガーを削除する (トリガーが溜まらないように、都度削除しています)
- Notionのデータベースに当日の日記データがあるかを取得する
- データがない場合
    - LINEのメッセージを送信して、10分後に同じ判定を行うためのトリガーをセットする
- データがある場合
    - なにも行なわず、翌日の22時に同じ判定を行うためのトリガーをセットする

また、デフォルトではタイムゾーンがニューヨークになっているため、`appsscript.json`のTimeZoneを「Asia/Tokyo」に変更しておきます。
`appsscript.json`を開くには、画面左の「プロジェクトの設定」から、『「appsscript.json」マニフェスト ファイルをエディタで表示する』にチェックを入れます。

※ 1/27追記
一部のコードが間違っていたので、修正しました。
最初のコードをご覧いただいた方々にご迷惑をおかけしてしまい、申し訳ございません。


```javascript
const NOTION_DATABASE_ID = 'XXX';
const NOTION_URL = `https://api.notion.com/v1/databases/${NOTION_DATABASE_ID}/query`;
const NOTION_API_KEY = 'XXX';

const LINE_URL = 'https://api.line.me/v2/bot/message/broadcast';
const LINE_ACCESS_TOKEN = 'XXX';

const FUNCTION_NAME = 'notionCheck';

// Notionのデータを取得
function getNotionData() {
  const today = new Date();
  const formattedToday = Utilities.formatDate(today, 'JST', 'yyyy-MM-dd');
  const query = {
    filter: {
      property: '日付',
      date: {
        equals: formattedToday,
      },
    },
  };
  const headers = {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${NOTION_API_KEY}`,
    'Notion-Version': '2021-08-16',
  };
  const options = {
    method: 'POST',
    headers: headers,
    payload: JSON.stringify(query),
  };

  const response = UrlFetchApp.fetch(NOTION_URL, options);
  const json = JSON.parse(response.getContentText());
  const results = json.results;

  return results;
}

// LINEのメッセージを送信
function sendLineMessage(messageText) {
  const postData = {
    messages: [
      {
        type: 'text',
        text: messageText,
      },
    ],
  };
  const headers = {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${LINE_ACCESS_TOKEN}`,
  };
  const options = {
    method: 'POST',
    headers: headers,
    payload: JSON.stringify(postData),
  };

  UrlFetchApp.fetch(LINE_URL, options);
}

// 翌日のトリガーをセット
function setNextDayTrigger() {
  let date = new Date();
  date.setDate(date.getDate() + 1);
  date.setHours(22);
  date.setMinutes(0);
  ScriptApp.newTrigger(FUNCTION_NAME).timeBased().at(date).create();
}

// 10分後のトリガーをセット
function setAfterTenMinutesTrigger() {
  let date = new Date();
  date.setMinutes(date.getMinutes() + 10);
  ScriptApp.newTrigger(FUNCTION_NAME).timeBased().at(date).create();
}

// トリガーの削除
function delTrigger() {
  const triggers = ScriptApp.getProjectTriggers();
  for (const trigger of triggers) {
    if (trigger.getHandlerFunction() == FUNCTION_NAME) {
      ScriptApp.deleteTrigger(trigger);
    }
  }
}

// この関数を都度実行
function notionCheck() {
  delTrigger();
  const notionData = getNotionData();
  if (!notionData.length) {
    sendLineMessage(`今日の日記書いてないぞ😠\n https://www.notion.so/${NOTION_DATABASE_ID}`);
   setAfterTenMinutesTrigger();
  } else {
    setNextDayTrigger();
  }
}
```

## まとめ

GASやMessaging API部分の話が長くなってしまいましたが、個人的には、Notion APIを試している時が一番楽しかったです。
Changelogを見る限り、今後も機能の追加に期待できそうですね。

Notion自体、様々な使い方ができるツールなので、今後もまた何か活用法を思いついたら記事にしようと思います。

本記事が少しでも皆様の参考になれば幸いです。
最後まで読んでいただき、ありがとうございました。

## 参考文献
- https://zenn.dev/st43/articles/7982e6d371f8b8
- https://qiita.com/xeg_yn/items/d4b4efc02f997e261936
- https://qiita.com/nkjm/items/38808bbc97d6927837cd
- https://tonari-it.com/gas-trigger-set/