Notionのタスク管理を元にLINEにリマインドを送信するツールを開発した話

GAS

俺の特性に過集中がある。
基本俺はこれに助けられていることが多いけど、重要な予定をすっぽかすリスクが常に付きまとう。

そんな自分をうまくコントロールするためにタスク管理をNotionでしているんだけど、それをLINEに飛ばすことによって、過集中による予定忘れの防止とプログラミングスキルの向上を狙っていきたい。

システムの概要

使用技術一覧

  • Google Apps Script (GAS):スケジュール管理や自動化処理の実行。
  • Notion API:Notionデータベースからスケジュール情報を取得。
  • LINE Messaging API:LINE公式アカウントを使用してメッセージを送信。

やっていること

元々Notionでスケジュール管理をしていて、そのdateプロパティに登録されている時間を元に、開始10分前にLINE公式アカウントに通知を送る。

Notionはこんな感じでタスク管理をしている。イーロンマスクもやっている(らしい)タイムボクシング方式。効果は未知数。
LINEに通知が届く。Apple Watchとかを使っていれば、そこにも通知がいく。

簡単な流れはこんな感じ。

  1. 未着手かつ日付が設定された予定をNotion APIで取得。
  2. 現在時刻と予定開始時刻を比較し、通知対象を判定。
  3. LINEで通知メッセージを送信し、通知済みの予定IDを記録。
  4. 古い通知記録を定期的に削除してデータを管理。

実際にコードを見てみる

今回のシステム概要。
関数を一個一個見て行こうと思う

LINEのブロードキャストメッセージを送信する関数

  • アクセストークン取得: スクリプトプロパティからLINE APIのアクセストークンを取得。
  • リクエスト作成: メッセージペイロードをJSON形式で作成。
  • API呼び出し: LINE APIのブロードキャストエンドポイントにPOSTリクエストを送信。
  • エラーハンドリング: API呼び出しの結果をログに記録。
function sendBroadcastMessage(message) {
  const lineToken = PropertiesService.getScriptProperties().getProperty("YOUR_LINE_ACCESS_TOKEN");

  const url = 'https://api.line.me/v2/bot/message/broadcast';
  const payload = {
    messages: [{
      type: 'text',
      text: message
    }]
  };

  const options = {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + lineToken
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    Logger.log("LINE送信結果: " + response.getContentText());
  } catch (error) {
    Logger.log("LINE送信エラー: " + error.message);
  }
}

Notionデータベースからスケジュール情報を取得する関数

  • アクセストークンとデータベースID取得: スクリプトプロパティから取得。
  • リクエスト作成: フィルター条件(例: 未着手かつ日付が設定されている)を指定。
  • API呼び出し: Notion APIのqueryエンドポイントにPOSTリクエストを送信。
  • レスポンス処理: レスポンスをパースし、データベースの結果を返す。エラー時はログを出力。
function fetchNotionSchedules() {
  const notionToken = PropertiesService.getScriptProperties().getProperty("YOUR_NOTION_API_TOKEN");
  const databaseId = PropertiesService.getScriptProperties().getProperty("YOUR_DATABASE_ID");

  const url = `https://api.notion.com/v1/databases/${databaseId}/query`;
  const filter = {
    filter: {
      and: [
        {
          property: "日付",
          date: {
            is_not_empty: true
          }
        },
        {
          property: "ステータス",
          status: {
            equals: "未着手" // 実際に設定されているステータスの値を指定
          }
        }
      ]
    }
  };

  const options = {
    method: 'post',
    headers: {
      'Authorization': `Bearer ${notionToken}`,
      'Content-Type': 'application/json',
      'Notion-Version': '2022-06-28'
    },
    payload: JSON.stringify(filter),
    muteHttpExceptions: true
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    const responseCode = response.getResponseCode();
    const responseText = response.getContentText();

    if (responseCode !== 200) {
      Logger.log(`Notion APIエラー: ステータスコード ${responseCode}`);
      Logger.log(`レスポンス内容: ${responseText}`);
      return [];
    }

    const data = JSON.parse(responseText);
    Logger.log("取得データ: " + JSON.stringify(data.results, null, 2));
    return data.results;
  } catch (error) {
    Logger.log("Notion APIリクエストエラー: " + error.message);
    return [];
  }
}

LINE通知を送る関数

最後に上記の2関数を使いながらスケジュールを確認し、LINE通知を送る関数。(checkAndNotifySchedules関数)

  • スケジュール取得: fetchNotionSchedulesを呼び出してデータを取得。
  • 現在時刻と比較: スケジュール開始まで10分以内かどうかを判定。
  • 通知済み確認: getNotifiedIdsで過去の通知済みIDをチェック。
  • 通知送信: 条件を満たすスケジュールについてLINEに通知を送信。
  • 通知ID管理: 通知済みIDを記録し、古いIDを削除。
function checkAndNotifySchedules() {
  const schedules = fetchNotionSchedules(); // 取得済みデータ
  const now = new Date();

  // 通知済みの予定IDを取得
  const notifiedIds = getNotifiedIds();

  schedules.forEach(schedule => {
    const properties = schedule.properties;
    const scheduleId = schedule.id; // ページID

    // 日付プロパティを取得
    const dateProperty = properties["日付"];
    if (dateProperty && dateProperty.date) {
      const scheduleStart = new Date(dateProperty.date.start); // 開始時間

      // 現在時刻との差分を計算(分単位)
      const timeDifference = (scheduleStart - now) / 60000; // ミリ秒から分へ変換

      if (timeDifference > 0 && timeDifference <= 10 && !notifiedIds[scheduleId]) {
        // 名前プロパティからタイトルを取得
        const nameProperty = properties["名前"];
        const scheduleName = nameProperty.title[0]?.plain_text || "不明な予定";

        // メッセージ作成
        const message = `「${scheduleName}」という予定の10分前です。`;

        // LINEメッセージを送信
        sendBroadcastMessage(message);

        // 通知済みIDとして記録
        addNotifiedId(scheduleId);
      }
    }
  });

  // 古い通知IDをクリア
  clearOldNotifiedIds();
}

通知済みの予定を再通知しないようにする

追加として、通知済みの予定を再通知しないようにするシステムも作成。というのも、初期装備だと10分間毎分同じ予定が通知されるようになってしまってノイローゼになりかけたため。

// 通知済みの予定IDを追加
function addNotifiedId(scheduleId) {
  const scriptProperties = PropertiesService.getScriptProperties();
  const notifiedIds = getNotifiedIds();

  // 現在時刻を記録
  notifiedIds[scheduleId] = new Date().toISOString();
  scriptProperties.setProperty("NOTIFIED_IDS", JSON.stringify(notifiedIds)); // JSON形式で保存
}

// 古い通知IDをクリア
function clearOldNotifiedIds() {
  const scriptProperties = PropertiesService.getScriptProperties();
  const notifiedIds = getNotifiedIds();
  const now = new Date();

  // 1週間前の日時を計算
  const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);

  // 古いエントリを削除
  Object.keys(notifiedIds).forEach(scheduleId => {
    const notifiedTime = new Date(notifiedIds[scheduleId]);
    if (notifiedTime < oneWeekAgo) {
      delete notifiedIds[scheduleId];
    }
  });

  // 更新された通知済みIDリストを保存
  scriptProperties.setProperty("NOTIFIED_IDS", JSON.stringify(notifiedIds));
}

つまずいたところ

主に以下のポイントでつまづいた。

  • 無限通知地獄(前述)
  • LINE ACCESS TOKENはシークレットトークンとは別
  • LINE Notifyはサービスが終了する

順に見ていく。

無限通知地獄(前述)

純粋に現在時刻とNotion データベースに登録している時刻を比較するというロジックであるために、再通知を防止するツールを作らないと10分間無限に通知される。ノイローゼ不可避。

LINE ACCESS TOKENはシークレットトークンとは別

Messaging APIのページに行くとこんな感じでめちゃくちゃ意味深な2行がある。
ずっとChannel secretの値がアクセストークンだと思ってた。

正解はLINE Developersにアクセスをして作成した公式ラインを選択してMessaging API設定を選択。
この一番下にある。

LINE Notifyはサービスが終了する

「LINE 通知」で検索するとLINE Notifyなるサービスがめっちゃヒットする。LINEと外部ツールを連携するための無料ツールらしい。

ただ、このツールは2025年3月で終了するらしい。
https://notify-bot.line.me/closing-announce

まとめ

今回はプログラミングの練習としてLINEとNotionの連携をした。
これでド忘れをなくしつつ、これを改善していくことで自分のプログラミング知識も拡張して行こうと思う。

今後やりたいこととして、Notion でタイムボクシング方式のタスク管理をしているんだけど、これを通知に落とし込んでいきたい。

東京在住の25歳。
後先は考えないタイプで、高校受験、東大受験(現役・浪人)ともに併願なしで挑んでました。
東京大学文科1類に運良く合格。法学部に進みましたが、一番面白かったのはICT系・芸術の授業でした。
興味が続かずに弁護士にはならず、ITベンチャー企業で働いています。
動画編集、プログラミング、最近はAIなど色々やってます。

yuta|生活を豊かにするAI・自動化をフォローする
GASプログラミング
シェア・ご感想などお待ちしております!
yuta|生活を豊かにするAI・自動化をフォローする
タイトルとURLをコピーしました