Google Apps Script Google Workspace (G Suite) Javascript プログラミング

LINE Notify + GASで作る、しつこい定時リマインダー

1日1回の点眼もリマインド&記録したい

私は眼圧が高いので毎日の点眼が欠かせません。

1日3回の点眼が必要な薬については、LINE Nofity、LINE Messanger APIとESP8266マイコンを使ったリマインドシステムで忘れずに点眼できています。

もうひとつ1日1回の点眼薬についてもリマインドと記録が欲しくなり、新しいリマインダーを作りました。

点眼を忘れることはないのですが、点眼したかどうか記憶が怪しいことがあるのです。

点眼用に作りましたが、もちろん他の用途にも使えます。

LINE NofityのメッセージにGAS WebアプリのURLを載せる

欲しい仕様は、以下のようなものです。

  • 定時にLINEで点眼を催促してくれる
  • 点眼したことを簡単に報告できる
  • 報告が来るまでは催促し続ける。

定時送信はGoogle Apps Scriptで簡単に実現できます。GASからLINEのAPIを叩けばメッセージが送れます。

以前作ったものはLINE Messanger APIを使っていましたが、無料での送信回数制限が厳しくなったので、LINE Nofityを使います。

LINE Notifyは一方通行のメッセージなので、ユーザーからの返信に答えることかできません。

そこでLINEで送られてきたリマインドに対して実行したことを知らせるためにGASのWebアプリへ直接アクセスすることにしました。

以前は、LINE Messageに返信するとLINEからWeb hookでGASのWebアプリにアクセスして点眼を記録していました。

今回は、LINEから送るメッセージにWebアプリのURLを載せて、クリック(タップ)するとWebアプリにアクセス。これをもって点眼の実行を知らせます。

LINEはメッセージ内にURLがあると自動的にリンクに変換してくれるのでこういうことができます。

1日3回のリマインダーでは、薬ケースにマイコンを内蔵されて自動記録させましたが、今回は人間がURLをタップします。1日一回なら手動でも面倒ではないと思いました。

使い方

Gogole Apps ScriptとLINE Notifyを組み合わせたシンプルなコードです。

Google スプレッドシートのバインドスクリプトです。

スプレッドシートの項目名は起動時にfunction onOpen()で自動設定されます。

スプレッドシートにLINE NotifyのTOKENなどの設定値を書き込んで使います。

図の黄色い欄か必須項目です。

送信メッセージや、定時送信の時刻、催促の間隔(分)などを設定します。

Sticker Package IDやSticker IDは開発中のコードの名残で使用していません。

コードを入力し、シートに数値を書き込んだらWebアプリとしてデプロイします。

デプロイ時に得られるWebアプリのURLをスプレッドシート上のNotify Message欄に入力しておきます。

コードエディタ上でcreateDailyTrigger関数を実行します。これで毎日の定時リマインダーがセットされます。

あとは定時のリマインダーがきたら点眼してメッセージ上のリンクをクリックします。すぐに確認のメッセージが送られてきます。

リンクをクリックしないでいると、設定した間隔で再度メッセージが届きます。リンクを開くか翌日の定時送信が来るまで続きます。

定時送信用と、繰り返し送信用で、中身は同じで名前の違う関数を用意しています。これはトリガーを消去するときに消去対象のトリガーを設定された関数名で検索するためです。

動作ログはスプレッドシートに追記されます。appenRaw()で書き込んでいるので、適当なセルを「Log」などの文字で埋めておくと、そこから下にログが記録されます。

LINEのメッセージ上でエラーが表示されるが問題ない

このスクリプトを実行するとLINEに届いたメッセージ内にエラーが表示されます。

どうやらこれは、添付したURLのプレビューを作るためにLINEがアクセスするためのようです。

LINEのプレビューアクセスは、GASのWebアプリが行うリダイレクトに対応していないらしく、エラーが表示されているようです。

URLのないメッセージではエラーがでないので、たぶんそういうことだろうと思います。

ただ、もしリダイレクトに対応されていたら、それは点眼を報告したことになってしまいます。

今回の用途に限っては、このエラーのお陰で助かったと言えます。

将来的に対応されちゃうと嫌だなぁと思いますが、当面はこのままでいこうと思います。

コード

コードは以下の通りです。


//setup
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();

function doGet(e) { //点眼したことをWebアクセスで報告
  const message = sheet.getRange("B6").getValue();
  sendLineNotifyMsg(message);
  sheet.getRange("B7").setValue("yes");
  sheet.appendRow(['User Responded!']);
  deleteAllSendReNotifyTriggers();
  return ContentService.createTextOutput('OK');
}

function onOpen() { //シートの見出しを自動書き込み
  sheet.getRange("A1").setValue("LINE Notify TOKEN");
  sheet.getRange("A2").setValue("Message");
  sheet.getRange("A3").setValue("Sticker Package ID");
  sheet.getRange("A4").setValue("Sticker ID");
  sheet.getRange("A5").setValue("Response");
  sheet.getRange("A6").setValue("Confirmed Message");
  sheet.getRange("A7").setValue("is Confirmed");
  sheet.getRange("A8").setValue("Notify Time(hour)");
  sheet.getRange("A9").setValue("re Notify Delay (min)");
  sheet.getRange("A10").setValue("next re:Notify Time");
}

function sendNotify() { //定時送信用
  deleteAllSendReNotifyTriggers();
  const message = sheet.getRange("B2").getValue();
  sendLineNotifyMsg(message);
  createReNotifyTrigger();
}

function sendReNotify() { //繰り返し送信用
  deleteAllSendReNotifyTriggers();
  const message = sheet.getRange("B2").getValue();
  sendLineNotifyMsg(message);
  createReNotifyTrigger();
}


function sendLineNotifyMsg(message) {
  const token = sheet.getRange("B1").getValue();
  Logger.log(message);
  const options = {
    "method": "post",
    "headers": { "Authorization": "Bearer " + token },
    "payload": { "message": message }
  };
  try {
    const response = UrlFetchApp.fetch("https://notify-api.line.me/api/notify", options);
    Logger.log(response);
    sheet.getRange("B5").setValue(response);
    sheet.appendRow([new Date(), 'Notification sent', response]);
  }
  catch (error) {
    Logger.log(error);
    sheet.getRange("B5").setValue(error);
  }

}

function createReNotifyTrigger() {
  sheet.getRange("B7").setValue("waiting");
  // 現在の日付と時間を取得
  var now = new Date();
  // 再送信の時間を計算
  const delay = sheet.getRange("B9").getValue();
  var timeInFuture = new Date(now.getTime() + delay * 60 * 1000);
  sheet.getRange("B10").setValue(timeInFuture);
  // タイムドリブンのトリガーを作成
  ScriptApp.newTrigger('sendReNotify')
    .timeBased()
    .at(timeInFuture)
    .create();
}

function createDailyTrigger() {
  deleteAllTriggers()
  // B8セルから値(時間)を取得
  var hour = sheet.getRange("B8").getValue();

  // 毎日指定した時間にsendNotify関数を呼び出すトリガーを作成
  ScriptApp.newTrigger('sendNotify')
    .timeBased()
    .everyDays(1) // 毎日
    .atHour(hour) // 指定した時間に
    .create();
}

function deleteAllSendReNotifyTriggers() {
  // プロジェクト内のすべてのトリガーを取得
  var triggers = ScriptApp.getProjectTriggers();

  // 各トリガーについて調査
  for (var i = 0; i < triggers.length; i++) {
    // トリガーが sendReNotify 関数を起動する場合、そのトリガーを削除
    if (triggers[i].getHandlerFunction() === 'sendReNotify') {
      ScriptApp.deleteTrigger(triggers[i]);
    }
  }
}

function deleteAllTriggers() { //トリガーをすべて削除
  // プロジェクト内のすべてのトリガーを取得
  var triggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < triggers.length; i++) {
    ScriptApp.deleteTrigger(triggers[i]);
  }
}

-Google Apps Script, Google Workspace (G Suite), Javascript, プログラミング