Notionあれやこれや

情報一元化ツールNotion遊泳のTips

Notionデータベースで設定した日付から任意のタイミングでLINE Notifyに通知できたよ

Notionで防災用の備蓄品リストを作り、賞味期限が1ヶ月前・1週間前・期限切れになった日にLINEで通知してくれる仕組みづくりにトライしました。
Notion内でもリマインド機能はありますが、Notionアプリに通知が届くのでうっかり忘れがち&設定タイミングの最長は1週間前・・消費したいので1か月前が嬉しい・・ということで、もう少し細かい設定ができる方法を探してみました。

📛 今回はピンポイント時刻の設定ではありません。午前9時~10時の間というような時間帯指定です。ピンポイント(例:9時半)にしたい場合はもう少し処理が必要ですが、今回は未対応です。

そもそもNotionって何?

ワークフローやデータを一元管理できるツール「Notion」。本や映画の鑑賞リストや、タスク管理、単語帳など活用方法は様々。PCのブラウザ、PCアプリ、スマホアプリもあり、個人なら無料プランで十分使い倒せる勢いです。
2018年リリース、2022年日本語版対応と新しいサービスですが、世界3,000万人以上のユーザーがおり、日本でも三菱重工サントリーといった大企業も活用しているとのこと。操作性・拡張性が高く生産効率を高めるツールなんですね~。

ちなみに私の活用方法の一例はブックリスト。(内容はスルー願います🥳)

ブックリスト例:表示レイアウトはギャラリー/ボード/一覧など選べるよ

❓ Notionとは

ドキュメントの作成、ナレッジの整理、プロジェクトの管理を、ひとつのワークスペースで行うことができます。 数多くのワークフローを「All-in-one workspace」として、ひとつの場所にまとめました。タスクリスト、製品ロードマップ、デザインリポジトリーなど、必要なものすべてを集約させたのです。レゴブロックのように、パーツを組み合わせて自分だけのワークスペースを構築することもできます。

www.notion.so

デモ画面

Notionの備蓄品リスト

以下の画像を見ていただくと、テーブル一行目の「テスト」の「賞味/消費期限」が現時点で1か月を切っています。
※補足:「消費目安」列の中身は関数を入れています。本通知には関係ありませんが一覧表示した際に分かり易くするためのもの。

Notionで作った備蓄品リスト(仮)

LINE Notifyに届いた通知内容

私個人に通知されるようにしていますが、設定で対象LINEグループに送ることも可能です。家族間グループとかでもいいかもしれませんね。通知内容も任意で変更可能です。

(左・中)LINE Notifyの通知内容 (右)Notionアカウントのメルアド宛にも受信あり

Notionの備品リスト内のステータス変更

本通知は、毎日1回処理を実行する仕組みとしています。各通知を連日送る必要はありませんので、送るか否かの判断条件として「通知ステータス」という項目(プロパティ)を用意しました。通知後はこのステータスを変更するようになっています。

ステータスは「未通知」「1か月前」「1週間前」「期限切れ」の4つにしました

作り方

できるだけ分かかり易い言葉を使って説明したいと思いますので、厳密にいえば正しい単語ではない場合もあります。ニュアンスでご理解ください。

使うサービス(全部無料)

🔰 前提:Notionのアカウント登録は済んでいるものとします。

手順① [Notion API] 後続の手順②で作成するNotionテーブルのデータを参照するためのインテグレーションを作成

※インテグレーション:異なるシステムや要素を組み合わせること

  1. 以下のリンク先で新しいインテグレーションを作成
  2. 「内部インテグレーションシークレット」の値を手元のメモ帳などに一旦コピペしておく【コピーA】

1. 新しいインテグレーションを作成

2.「インテグレーションシークレット」の値をコピーしておく

手順② [Notion] 自分のワークスペースでテーブル(データベース)を作る&コネクト追加する&データベースIDを得る

  1. 新規ページで「テーブル」を作成。必要最低限のプロパティは以下の3つ

    • プロパティ名 種類 オプション 備考
      名前 タイトル - -
      賞味期限 日付 - -
      通知ステータス セレクト 未通知/1か月前/1週間前/期限切れ 初期値は「未通知」
  2. 作成したテーブル(データベース)に、手順①で作成したインテグレーションをコネクト追加する

  3. ブラウザ版でURLを確認し、以下の{database_id}にあたる値を手元のメモ帳などにコピペしておく【コピーB】

1. テーブル(データベース)を作成・必要なプロパティ(項目)を追加

2. 手順①で作成したインテグレーションをコネクト追加

3. データベースIDをコピペしておく

手順③ [LINE Notify] 対象トークルームへ通知するキーを得る

  1. 以下のリンク先にてLINEアカウントでログインする
  2. マイページからアクセストークンを発行する
    1. 通知用のタイトルを入力
    2. 通知先のトークルームを選択(個人なら1:1、グループトークも可)
    3. 発行されたトークンを手元のメモ帳などにコピペしておく【コピーC】

1. ログインします

2. 「マイページ」へ遷移して「トークンを発行する」ボタンをクリック

3. トークンのタイトル(通知名)と通知先を設定して発行ボタンをクリック

4. トークンの値をコピーしておく

5. トークン発行できたらこんな感じで表示される

手順④ [Google Apps Script] プログラム処理とトリガーを設定

  1. 以下リンク先のGoogle Apps Script(GAS)で「新しいプロジェクト」を作成
  2. 「コード.gs」のファイルに後述のソースコードをコピペ
  3. 「実行」でLINE通知&Notionテーブルの通知ステータス更新を確認
  4. 実行タイミングを決めるトリガーを設定
    • 毎日1回、設定した時間帯に処理が実行されます
      • 通知の必要が無ければ通知はされません
      • 今回はピンポイント時刻の設定ではありません。午前9時~10時の間というような時間帯指定です。ピンポイント(例:9時半)にしたい場合はもう少し処理が必要ですが、今回は未対応です。

1. GASにログインして「新しいプロジェクト」をクリック

2. 「コード.gs」ファイルに以下のソースコードをコピペ
※初期値で記述されているmyFunction等は消す※
そのあと、さらに必要な3つの値(const ~の箇所)を置き換える。
※値を囲っている'(シングルクォート)は消さないように※

《コード.gsへのコピペソースコード

const NOTION_API_KEY = 'コピーAの値に置き換えてください'; // 【コピーA】
const DATABASE_ID = 'コピーBの値に置き換えてください'; // 【コピーB】
const LINE_NOTIFY_API_TOKEN = 'コピーCの値に置き換えてください'; // 【コピーC】

/**
 * メイン処理
 */
function sendNotification() {
  // 通知対象アイテム取得
  const itemsToNotify = getItemsToNotify();

  itemsToNotify.forEach(item => {
    // 日付の整形
    const formattedExpiry = formatDate(item.expiry);
    // LINE通知
    sendLineMessage(item.name, formattedExpiry, item.afterNoticeStatus);
    // 通知を送ったらNotionテーブルの通知ステータスを更新
    updateNotificationStatus(item.id, item.afterNoticeStatus);
  });
}

/**
 * 通知対象アイテムをすべて取得
 */
function getItemsToNotify() {
  const today = new Date();
  const url = `https://api.notion.com/v1/databases/${DATABASE_ID}/query`;
  const options = {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${NOTION_API_KEY}`,
      'Content-Type': 'application/json',
      "Notion-Version": "2022-06-28",
    },
    payload: JSON.stringify({
      filter: {
        property: '賞味期限',
        date: {
          on_or_after: today, // 実行日(含む)より未来の日付
        },
      },
    }),
  };

  // 「賞味期限」が期限切れ前(当日含む)をいったんすべて取得
  const response = UrlFetchApp.fetch(url, options);
  const data = JSON.parse(response.getContentText());
  const items = data.results.map(item => {
    const expiryDate = new Date(item.properties['賞味期限'].date.start);
    // 変更後の通知ステータスを設定
    const afterNoticeStatus = setAfterNotificationStatus(expiryDate, today);
    
    return {
      id: item.id,
      name: item.properties['名前'].title[0].text.content,
      expiry: expiryDate.toISOString().split('T')[0],
      // 現時点の通知ステータス
      currentNoticeStatus: item.properties['通知ステータス'].select.name,
      afterNoticeStatus: afterNoticeStatus,
    };
  });

  // 実行日時点で既に該当通知済みのアイテムは除く
  return items.filter(item => item.afterNoticeStatus !== item.currentNoticeStatus);
}

/**
 * 実行日時点で更新されるべき「通知ステータス」を設定
 */
function setAfterNotificationStatus(expiryDate, today) {
  // 期限:1か月前を設定
  const oneMonthAgo = new Date(today);
  oneMonthAgo.setMonth(oneMonthAgo.getMonth() + 1);

  // 期限:1週間前を設定
  const oneWeekAgo = new Date(today);
  oneWeekAgo.setDate(oneWeekAgo.getDate() + 7);

  // アイテムの期限ごとに通知ステータスを判断
  if (expiryDate <= today) {
    return '期限切れ';
  } else if (expiryDate <= oneWeekAgo) {
    return '1週間前';
  } else if (expiryDate <= oneMonthAgo) {
    return '1か月前';
  } else {
    return '未通知';
  }
}

/**
 * LINE通知内容に記述する「賞味期限」の日付整形
 * 例: "2024/01/22"
 */
function formatDate(dateString) {
  const date = new Date(dateString);
  return date.toLocaleDateString('ja-JP');
}

/**
 * LINE通知
 */
function sendLineMessage(name, expiry, afterStatus) {
  // 通知内容の整形
  const message = `\n\n【買い替え時:${afterStatus}\n- アイテム: ${name}\n- 賞味期限: ${expiry}`;
  const notifyUrl = 'https://notify-api.line.me/api/notify';
  const options = {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${LINE_NOTIFY_API_TOKEN}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    payload: {
      message: message,
    },
  };

  UrlFetchApp.fetch(notifyUrl, options);
}

/**
 * Notionデータベースの「通知ステータス」を更新
 */
function updateNotificationStatus(itemId, afterStatus) {
  const url = `https://api.notion.com/v1/pages/${itemId}`;
  const options = {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${NOTION_API_KEY}`,
      'Content-Type': 'application/json',
      "Notion-Version": "2022-06-28",
    },
    payload: JSON.stringify({
      properties: {
        '通知ステータス': {
          select: {
            name: afterStatus,
          },
        },
      },
    }),
  };

  UrlFetchApp.fetch(url, options);
}

3. 実行ボタンをクリックして、実行ログにエラーが無いか・LINE通知が来たか・Notion内データのステータス更新を確認

4. 左側のメニューから「トリガー」を選択し、画像のように設定
※「時刻を選択」の部分は希望の時間帯を設定※

おわりに

以上です。
杞憂かもしれませんが、備蓄品の賞味期限が切れる前にNotionやLINEやGASの仕様が変わったり、最悪サービス終了にならないかだけ心配ですね🙃
ざーっと作ったのでソースコードに誤りがあればこっそり教えてください。

🙇‍♀️参考にさせていただいたサイト note.com