ソフトウェア

Notionの未完了デイリーをLINE通知し、毎日リセットするGASの作り方

ノイ

この記事は、ソシャゲのデイリー管理を Notion・LINE通知・Google Apps Script(GAS) で自動化する仕組みの一部です。
今回はその中でも、GASを使ってNotionの未完了デイリーをLINEへ通知し、毎日チェックをリセットするところまでをまとめます。

先に全体の流れを知りたい場合は親記事から、LINE公式アカウントやNotionの準備がまだなら、それぞれの個別記事から読むのがおすすめです。

親記事
[ソシャゲのデイリー忘れ対策。Notion×LINE通知×GASで管理を仕組み化した話]

LINE記事
[LINE公式アカウントを作成する方法|GASでLINE通知を送る前準備]

Notion記事
[Notionでソシャゲのデイリー管理表を作る方法|GAS通知の前準備]

Apps Scriptのページを開く

Google Apps Script(GAS)は、Googleが提供しているプログラム実行環境です。
ブラウザ上でコードを書けるので、今回のように Notionの情報を取得したり、LINEに通知を送ったりする処理 を作るのに使えます。

今回はここで、Notionの未完了デイリーをLINEに通知し、毎日チェックをリセットするプログラムを作っていきます。
まずは、Apps Script のホームを開きます。

新しいプロジェクトを作成する

「新しいプロジェクト」をクリックする

Apps Script のホームを開いたら、左上の 「新しいプロジェクト」 をクリックします。

プロジェクト名を変更する

プロジェクトが開いたら、左上の 「無題のプロジェクト」 をクリックして、名前を変更します。
今回は分かりやすさを優先して、Notion_LINE_デイリー通知 として進めます。

設定値をまとめる config.gs を作る

まず最初に、設定値だけをまとめる config.gs を作ります。
ここには、NotionやLINEのトークン、データベースID、通知時刻などをまとめて入れます。

コード.gs に設定コードを貼り付ける

新しいプロジェクトを作成した直後は、コード.gs にサンプルの myFunction() が入っています。
これは使わないので消して、以下の config.gs 用コードをコピーしてそのまま貼り付けます。

const CONFIG = {
  /**************************************************
   * Notion
   **************************************************/
  NOTION_TOKEN: 'ここにNotionのインテグレーショントークン',
  NOTION_DATABASE_ID: 'ここにNotionのデータベースID',
  NOTION_VERSION: '2022-06-28',

  /**************************************************
   * LINE
   **************************************************/
  LINE_CHANNEL_ACCESS_TOKEN: 'ここにLINEのチャンネルアクセストークン',
  LINE_USER_ID: 'ここにLINEのuserId',

  /**************************************************
   * Notionの列名
   **************************************************/
  TITLE_PROPERTY_NAME: '名前',
  CHECKBOX_PROPERTY_NAME: '完了',

  /**************************************************
   * 通知・リセット時刻
   *
   * Apps Script の時間トリガーは厳密な時刻指定ではありません。
   * nearMinute() で指定した分の前後15分の範囲で実行されます。
   *
   * 例:
   * NOTIFY_HOUR: 21
   * NOTIFY_MINUTE: 15
   * の場合、21:00〜21:30頃に実行される可能性があります。
   **************************************************/
  NOTIFY_HOUR: 21,
  NOTIFY_MINUTE: 15,

  RESET_HOUR: 5,
  RESET_MINUTE: 0
};
ファイル名を config.gs に変更して保存する

左側の コード.gs の3点メニューをクリックし、「名前を変更」 を選びます。
ファイル名を config.gs に変更したら、そのまま保存します。

トークンやIDを自分の値に書き換える

貼り付けたコードはそのままでは使えないので、トークンやIDは自分の環境に合わせて書き換えてください。
今回書き換えるのは次の4つです。

  • Notionのインテグレーショントークン
  • NotionのデータベースID
  • LINEのチャンネルアクセストークン
  • LINEのuserId

通知時刻とリセット時刻も、この config.gs で設定します。
ただし、この2つはあとで作成する時間トリガーとセットで考える必要があるため、ここではまず項目だけ確認しておけば大丈夫です。

どの時刻を入れるべきかは、後半のトリガー設定のところでまとめて説明します。

LINE通知テスト用の line_test.gs を作る

まずは、LINEへの通知が正常に送れるかを確認するための line_test.gs を作ります。
左上の プラスボタン をクリックし、「スクリプト」 を選択してください。
作成したファイル名は line_test.gs に変更します。

ファイルを作成したら、以下のコードをコピーしてそのまま貼り付けます。

function sendLineTestMessage() {
  const url = 'https://api.line.me/v2/bot/message/push';

  const payload = {
    to: CONFIG.LINE_USER_ID,
    messages: [
      {
        type: 'text',
        text: '✅ LINE通知テストです'
      }
    ]
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: 'Bearer ' + CONFIG.LINE_CHANNEL_ACCESS_TOKEN
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  const response = UrlFetchApp.fetch(url, options);
  Logger.log('Response Code: ' + response.getResponseCode());
  Logger.log('Response Body: ' + response.getContentText());
}

初回実行時はGoogleの承認が必要です

次に line_test.gs を実行して、LINE通知のテストを行います。
ただし、初めて GAS を実行するときは、その前に Google の権限確認画面が表示されます。

これは、今回のスクリプトが LINE への通知送信に必要な権限を使うためです。
自分で作成した GAS プロジェクトを初回実行するときには自然な流れなので、表示されても慌てなくて大丈夫です。

途中で「このアプリはGoogleで確認されていません」と表示される場合がありますが、今回のスクリプトは自分で作成したものなので、内容を確認したうえでそのまま進めて問題ありません。

表示されたら、以下の順に進めてください。

権限を確認 をクリックします。
警告画面が表示されたら、左下の 詳細 をクリックします。

展開されたリンクの中から、Notion_LINE_デイリー通知(安全ではないページ)に移動 をクリックします。

権限内容の確認画面が表示されたら、下までスクロールします。

一番下まで進んだら、続行 をクリックします。

line_test.gs を実行する

承認が終わったら、上部の 実行 をクリックします。

正常に動作すると、実行ログに Response Code: 200 と表示されます。
この 200 は、LINEへの通知リクエストが正常に成功したことを表しています。

そのうえで、自分のLINEに 「✅ LINE通知テストです」 というメッセージが届いていれば、LINE通知テストは成功です。

Apps Script の編集画面で line_test.gs を実行し、実行ログに Response Code: 200 が表示されている画面
実行をクリックし、実行ログに Response Code: 200 と表示されれば、LINEへの通知送信は成功です
LINEのトーク画面に「✅ LINE通知テストです」というメッセージが届いている状態の画面
自分のLINEにテストメッセージが届けば、LINE通知の準備は完了です

考えられる失敗と対策

  • チャンネルアクセストークンが間違っている
    config.gs の設定値を確認する
  • userId が間違っている
    → 取得した userId を再確認する
  • Googleの承認が終わっていない
    → 権限確認を最後まで進める

Notion接続を確認する notion_test.gs を作る

次に、Notionのデータベースを正しく読み取れるか確認するための notion_test.gs を作ります。
左上の プラスボタン をクリックし、「スクリプト」 を選択してください。
作成したファイル名は notion_test.gs に変更します。

ファイルを作成したら、以下のコードをコピーしてそのまま貼り付けます。

function testFetchNotionDatabase() {
  const url = `https://api.notion.com/v1/databases/${CONFIG.NOTION_DATABASE_ID}/query`;

  const options = {
    method: 'post',
    headers: {
      Authorization: `Bearer ${CONFIG.NOTION_TOKEN}`,
      'Content-Type': 'application/json',
      'Notion-Version': CONFIG.NOTION_VERSION
    },
    muteHttpExceptions: true
  };

  const response = UrlFetchApp.fetch(url, options);
  const responseCode = response.getResponseCode();
  const data = JSON.parse(response.getContentText());

  Logger.log('Response Code: ' + responseCode);
  Logger.log('取得件数: ' + (data.results ? data.results.length : 0));
}
Apps Script の編集画面で notion_test.gs ファイルを作成し、Notionのデータベース接続確認用コードを入力した状態の画面
notion_test.gs を作成し、Notion接続確認用のコードを貼り付けます

notion_test.gs を実行する

notion_test.gs を作成したら、上部の 実行 をクリックして起動します。

正常に動作すると、実行ログに Response Code: 200 と表示されます。
この 200 は、Notionのデータベースに正常に接続できたことを表しています。

あわせて、取得件数 も表示されます。
今回の例では 4件 と表示されているため、Notionのデータベースから4件のデータを取得できていることが分かります。

このように、Response Code: 200 と取得件数が表示されれば、Notion接続テストは成功です。

Apps Script の notion_test.gs を実行し、実行ログに Response Code: 200 と 取得件数: 4 が表示されている画面
testFetchNotionDatabase() を実行し、Response Code: 200 と取得件数が表示されれば、Notionのデータベース接続は成功です。

考えられる失敗と対策

  • Notionトークンが間違っている
    config.gs の設定値を確認する
  • データベースIDが間違っている
    → NotionのURLから再確認する
  • Integration とデータベースが接続されていない
    → Notion側で対象データベースに接続権限を付ける

main.gs を作る

LINEテストとNotionテストが通ったら、次は本番用の main.gs を作ります。

main.gs では、主に次の処理をまとめます。

  • 未完了デイリーをLINEに通知する
  • 毎日決まった時間にチェックをリセットする
  • 時間トリガーで自動実行できるようにする

コード量は少し増えますが、やっていることはこの3つです。

作り方は line_test.gsnotion_test.gs と同じです。
左上の プラスボタン から 「スクリプト」 を選び、新しいファイルを作成して、ファイル名を main.gs に変更してください。

ファイルを作成したら、以下のコードをそのまま貼り付けます。

function notifyUncheckedNotionTasksToLine() {
  try {
    const pages = fetchAllNotionDatabaseItems_();

    const uncheckedTasks = pages.filter(page => {
      return page.properties[CONFIG.CHECKBOX_PROPERTY_NAME]?.checkbox === false;
    });

    if (uncheckedTasks.length === 0) {
      Logger.log('✅ 未完了タスクはありません。通知は送信しませんでした。');
      return;
    }

    const message = buildUncheckedTasksMessage_(uncheckedTasks);
    sendLinePushMessage_(message);

    Logger.log('✅ LINEに未完了タスクを通知しました。');
  } catch (error) {
    Logger.log('❌ notifyUncheckedNotionTasksToLine エラー: ' + error.message);
    throw error;
  }
}

function resetNotionCheckboxes() {
  try {
    const pages = fetchAllNotionDatabaseItems_();

    const checkedTasks = pages.filter(page => {
      return page.properties[CONFIG.CHECKBOX_PROPERTY_NAME]?.checkbox === true;
    });

    if (checkedTasks.length === 0) {
      Logger.log('✅ リセット対象はありません。');
      return;
    }

    checkedTasks.forEach(page => {
      updateNotionCheckbox_(page.id, false);
    });

    Logger.log(`✅ ${checkedTasks.length}件のチェックをリセットしました。`);
  } catch (error) {
    Logger.log('❌ resetNotionCheckboxes エラー: ' + error.message);
    throw error;
  }
}

function setupDailyTriggers() {
  deleteTriggersByFunctionName_('notifyUncheckedNotionTasksToLine');
  deleteTriggersByFunctionName_('resetNotionCheckboxes');

  ScriptApp.newTrigger('notifyUncheckedNotionTasksToLine')
    .timeBased()
    .everyDays(1)
    .atHour(CONFIG.NOTIFY_HOUR)
    .nearMinute(CONFIG.NOTIFY_MINUTE)
    .create();

  ScriptApp.newTrigger('resetNotionCheckboxes')
    .timeBased()
    .everyDays(1)
    .atHour(CONFIG.RESET_HOUR)
    .nearMinute(CONFIG.RESET_MINUTE)
    .create();

  Logger.log('✅ 毎日の通知トリガーとリセットトリガーを設定しました。');
}

function deleteTriggersByFunctionName_(functionName) {
  const triggers = ScriptApp.getProjectTriggers();

  triggers.forEach(trigger => {
    if (trigger.getHandlerFunction() === functionName) {
      ScriptApp.deleteTrigger(trigger);
    }
  });
}

function fetchAllNotionDatabaseItems_() {
  let allResults = [];
  let hasMore = true;
  let nextCursor = null;

  while (hasMore) {
    const url = `https://api.notion.com/v1/databases/${CONFIG.NOTION_DATABASE_ID}/query`;

    const payload = nextCursor ? { start_cursor: nextCursor } : {};

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

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

    Logger.log('Notion Response Code: ' + responseCode);

    if (responseCode !== 200) {
      Logger.log('Notion Response Body: ' + responseText);
      throw new Error('Notion APIの取得に失敗しました。');
    }

    const data = JSON.parse(responseText);

    allResults = allResults.concat(data.results || []);
    hasMore = data.has_more;
    nextCursor = data.next_cursor;
  }

  return allResults;
}

function sendLinePushMessage_(messageText) {
  const url = 'https://api.line.me/v2/bot/message/push';

  const payload = {
    to: CONFIG.LINE_USER_ID,
    messages: [
      {
        type: 'text',
        text: messageText
      }
    ]
  };

  const options = {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: 'Bearer ' + CONFIG.LINE_CHANNEL_ACCESS_TOKEN
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

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

  Logger.log('LINE Response Code: ' + responseCode);
  Logger.log('LINE Response Body: ' + responseText);

  if (responseCode !== 200) {
    throw new Error('LINE通知の送信に失敗しました。');
  }
}

function buildUncheckedTasksMessage_(uncheckedTasks) {
  const taskLines = uncheckedTasks.map(page => {
    const title = getNotionTitle_(page);
    return `・${title}`;
  });

  return [
    '🔔 未完了のデイリーがあります',
    '',
    ...taskLines
  ].join('\n');
}

function getNotionTitle_(page) {
  return page.properties[CONFIG.TITLE_PROPERTY_NAME]?.title?.[0]?.plain_text || '(名前なし)';
}

function updateNotionCheckbox_(pageId, checkboxValue) {
  const url = `https://api.notion.com/v1/pages/${pageId}`;

  const payload = {
    properties: {
      [CONFIG.CHECKBOX_PROPERTY_NAME]: {
        checkbox: checkboxValue
      }
    }
  };

  const options = {
    method: 'patch',
    headers: {
      Authorization: `Bearer ${CONFIG.NOTION_TOKEN}`,
      'Content-Type': 'application/json',
      'Notion-Version': CONFIG.NOTION_VERSION
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

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

  if (responseCode !== 200) {
    Logger.log('Notion PATCH Response Body: ' + responseText);
    throw new Error(`Notionのチェック更新に失敗しました。 pageId=${pageId}`);
  }
}
Apps Script の編集画面で main.gs ファイルを作成し、未完了通知やチェックのリセットなど本番用コードを入力した状態の画面
本番用の処理をまとめた main.gs を作成し、通知・リセット・トリガー設定に使うコードを貼り付けます

本番コードの確認手順

ここからは、本番に必要な各機能を順番に確認していきます。
main.gs には複数の関数が入っているため、Apps Script 上部の関数欄から、実行したい関数を切り替えて確認します。

Apps Script の main.gs 画面で、上部の関数選択欄から notifyUncheckedNotionTasksToLine() を選ぶ位置を示した画像
main.gs では、上部の関数欄から実行したい関数を切り替えて確認します

notifyUncheckedNotionTasksToLine() を実行する

notifyUncheckedNotionTasksToLine() は、Notionから未完了のデイリーを読み取り、その内容をLINEへ通知するための関数です。

上部の関数欄で notifyUncheckedNotionTasksToLine() を選び、実行 をクリックします。
実行後は、まず実行ログを確認します。NotionとLINEのレスポンスコードがどちらも 200 になっており、ログに 「LINEに未完了タスクを通知しました。」 と表示されていれば成功です。

そのうえで、自分のLINEに未完了デイリーの一覧が届いていれば、通知処理は正常に動作しています。

LINEのトーク画面に「未完了のデイリーがあります」というメッセージと、未完了デイリーの一覧が届いている状態の画面
未完了のデイリー一覧がLINEに届けば、通知処理は正しく動作しています

resetNotionCheckboxes() を実行する

resetNotionCheckboxes() は、Notion側で完了済みになっているデイリーのチェックを外し、次の日のためにリセットするための関数です。

確認の前に、Notion側でいくつかの項目にチェックを付けておきます。
その状態で resetNotionCheckboxes() を選んで 実行 をクリックしてください。

実行ログに 「○件のチェックをリセットしました。」 と表示され、実際にNotion上のチェックが外れていれば成功です。

今回は例として、2件のチェックがリセットされた状態を示しています。
実行後は、Notionに戻ってチェックが消えていることを確認してください。

毎日自動で動くように設定する

ここまで確認が終われば、あとは自動実行用のトリガーを設定すれば完了です。

実行の前に、まずは自動実行の時間設定について確認しておきます。

時間トリガーは前後15分ずれる

Apps Script の時間トリガーは、設定した時刻ぴったりに実行されるわけではありません。
指定した分の前後15分程度ずれることがあるため、通知時刻やリセット時刻は少し余裕を持って設定するのがおすすめです。

たとえば、ゲーム内の日付更新が5時ちょうどの場合は、リセット時刻を 5:00 ぴったりにするより、5:15 など少し後ろに寄せておくと安全です。
通知時刻についても、まだその日のデイリーが間に合う時間帯に設定しておくと使いやすくなります。

config.gs の設定例

通知時刻とリセット時刻は、config.gs の中で設定します。
Apps Script の時間トリガーは前後15分程度ずれることがあるため、今回の例では次のように少し余裕を持たせています。

NOTIFY_HOUR: 22,
NOTIFY_MINUTE: 45,

RESET_HOUR: 5,
RESET_MINUTE: 15

この設定にした場合、未完了のデイリーがあると 22:30〜23:00頃 にLINE通知が届きます。
未完了のデイリーがない場合は、通知は送られません。

また、チェックリストのリセットは 5:00〜5:30頃 に行われます。
日付更新が5時ちょうどのゲームでも、少し後ろに寄せた設定として使いやすい形です。

setupDailyTriggers() を実行する

時間設定を確認したら、setupDailyTriggers() を実行します。
この関数は、未完了通知用のトリガーと、完了チェックのリセット用トリガーをまとめて作成するためのものです。

まずは setupDailyTriggers() を選んで 実行 をクリックします。
正常に動作すると、実行ログに 「毎日の通知トリガーとリセットトリガーを設定しました。」 と表示されます。

そのあと、左メニューの 「トリガー」 を開いて、通知用とリセット用の2つが作成されているか確認します。
2つの時間ベーストリガーが表示されていれば設定完了です。
あとは、実際に設定した時間どおりに動作するかを確認してください。

Apps Script の左メニューから「トリガー」を開き、notifyUncheckedNotionTasksToLine と resetNotionCheckboxes の2つの時間ベーストリガーが表示されている画面
左メニューの「トリガー」を開き、通知用とリセット用の2つの時間ベーストリガーが作成されていれば設定完了です

よくある詰まりポイント

config.gs の値が未設定

トークンやIDが未設定のままだと、当然ながら正常に動作しません。
LINEに通知が届かない場合や、Notionのデータが取得できない場合は、まず config.gs の設定値を確認してください。

Notionの列名が違う

今回のコードでは、タイトル列を 名前、チェックボックス列を 完了 として読み取っています。
Notion側の列名が異なる場合は、config.gsTITLE_PROPERTY_NAMECHECKBOX_PROPERTY_NAME も合わせて変更する必要があります。

初回承認が終わっていない

NotionやLINEを使うコードは、最初にGoogleアカウント側の権限承認が必要です。
途中で止まってしまった場合は、承認が最後まで完了しているかを確認してください。

GASのタイムゾーンが違う

時間トリガーは、Apps Script プロジェクトのタイムゾーン基準で動きます。
日本時間で使う場合は、左メニューの 歯車アイコン(Project Settings) から Time zone を確認し、Asia/Tokyo になっているか見ておくと安心です。

まとめ

今回は、Google Apps Script を使って

  • Notionのデータベースを読み取る
  • 未完了のデイリーをLINEに通知する
  • 毎日チェックをリセットする
  • その処理を時間トリガーで自動実行する

ところまで作成しました。

大事なのは、いきなり本番コードに入るのではなく、

  • config.gs で設定値をまとめる
  • line_test.gs でLINE通知を確認する
  • notion_test.gs でNotion接続を確認する
  • main.gs で本番処理を動かす
  • 最後にトリガーを設定する

という順番で進めることです。

ここまで設定できれば、Notionで管理しているデイリーを毎日自動で通知・リセットできる状態になります。
あとは、自分のプレイ時間やゲーム内の更新時刻に合わせて、通知時刻やリセット時刻を調整しながら使ってみてください。

今回の仕組み全体の流れを見直したい場合は、親記事もあわせて読むと分かりやすいです。
[ソシャゲのデイリー忘れ対策。Notion×LINE通知×GASで管理を仕組み化した話]

LINE公式アカウントの作成や userId の取得方法を確認したい場合は、こちらを参照してください。
[LINE公式アカウントを作成する方法|GASでLINE通知を送る前準備]

Notionのデイリー管理表そのものをまだ作っていない場合は、先にこちらから進めるのがおすすめです。
[Notionでソシャゲのデイリー管理表を作る方法|GAS通知の前準備]

ABOUT ME
記事URLをコピーしました