tshohe's memo

おぼえたことをかく

Google Apps Script で定期作業の実施記録 & リマインド

定期作業のリマインドをしてくれるやつをGASで設定してみたのでそのメモ。

定期的にしなければいけないけどつい忘れてしまう作業って結構あると思います。例えば、

  • エアコンのフィルター掃除
  • 空気清浄機のフィルター掃除
  • 加湿器のフィルター掃除
  • 筋トレ ... etc

毎週、毎月の定例作業なんかはSlackのリマインドとかでやればいいと思うんですが、「前回やった日から何日後に通知したい」という場合はちょっと難しいかと思います。(リマインドされてから実施するまでにはラグがある作業とかですね)

第一段階: スプレッドシート管理

まず最初に始めたのがスプレッドシートで管理するというもの。まずは下記のような表(シート名"Option")を作成します。

  • 定期的にやるべきだろうなということを単純に列挙
  • 経過日数を =ROUNDDOWN(now()-<最終実施日>) で求めておく
  • Thresholdを入力

f:id:tshohe:20191021010135p:plain
定期作業をまとめた表

そして、日数が指定したものを超過したらSlackに通知する処理を日時で実行するようにしてました。

const WEBHOOK_URL = "<incomming webhook url>";
const KEY_ID = "<Spreadsheet ID>";

function notify() {
  const spreadsheet = SpreadsheetApp.openById(KEY_ID);
  const sheet = spreadsheet.getSheetByName('Option');
  Logger.log(sheet.getLastRow());
  var exceededTasks = []
  for(var i = 2; i <= sheet.getLastRow(); i++) {
    option = sheet.getRange(i, 1).getValue();
    elapsedDay = sheet.getRange(i, 4).getValue();
    thresholdDay = sheet.getRange(i, 6).getValue();
    if(thresholdDay != '' && elapsedDay > thresholdDay) {
      exceededTasks.push(option);
    }
  }
  if(exceededTasks.length > 0) {
    sendToSlack('<!channel> ' + exceededTasks.toString() + ' を最後に実施してからの日数が、指定日数を超過しました。', ':bell:');
  }
}

function sendToSlack(message, icon) {
  Logger.log("message: " + message + "icon: " + icon)
  const jsonData = 
  {
    "channel": "#info",
    "username": "Counter",
    "icon_emoji": icon,
    "text": message
  };
  const options =
  {
    "method": "post",
    "contentType": "application/json",
    "payload": JSON.stringify(jsonData)
  };
  UrlFetchApp.fetch(WEBHOOK_URL, options);
}

するとこんな感じで指定日数を超過したときに通知が来るようになります。ここまででもそこそこ便利。

f:id:tshohe:20191021010657p:plain
Slack通知

第二段階: スマホからの操作

スプレッドシートだけでもまあまあ使えるのですがいかんせん、iPhoneでの操作性が悪い! そこでショートカットから実施日更新の操作くらいはできるように設定しました。

追加した関数は下記の通り。

まずは選択肢を返す関数。これはGASで「ウェブアプリケーションとして導入」から、誰でもGETで取得できるようにしておきます。(見られて困るものでもないですが適当にパスワードは設定)

function doGet(e) {
  const api_key = e.parameter.api_key;
  if(api_key != '<適当な文字列>') {
    return;
  }
  var output = ContentService.createTextOutput();
  output.setContent(list().join('\n'));
  return output;
}

function list() {
  const spreadsheet = SpreadsheetApp.openById(KEY_ID);
  const sheet = spreadsheet.getSheetByName('Option');
  return sheet.getRange(2, 1, sheet.getLastRow()).getValues();
}

そしてPOSTで今日の日付が実施日に入るように設定。本当は過去の実施にも対応できるようにDatePicker的なのがあるともっと良かったんですが、見つからなかったのでこの形になっています。

別シートへの履歴保存(logging関数)とかは別に不要ですが、後から集計とかしたら面白そうかなと思って設定してみています。

function doPost(e) {
  const params = JSON.parse(e.postData.getDataAsString());
  const api_key = params.api_key;
  if(api_key != '<適当な文字列>') {
    return;
  }
  const option = params.option;
  var date;
  if(params.date == 'Today') {
    date = new Date();
  } else if(params.date == 'Yesterday') {
    date = new Date(new Date - 86400000);
  }
  
  count(option, Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd'));
  logging(option, Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy/MM/dd'));
  
  var output = ContentService.createTextOutput();
  output.setMimeType(ContentService.MimeType.JSON);
  output.setContent(JSON.stringify({ message: "success" }));
  return output;
}

function count(option, date) {
  const spreadsheet = SpreadsheetApp.openById(KEY_ID);
  const sheet = spreadsheet.getSheetByName('Option');
  for(i = 2; i <= sheet.getLastRow(); i++) {
    const row_option = sheet.getRange(i, 1).getValue();
    if(option == row_option) {
      sheet.getRange(i, 3).setValue(date);
      return;
    }
  }
}

function logging(option, date) {
  const spreadsheet = SpreadsheetApp.openById(KEY_ID);
  const sheet = spreadsheet.getSheetByName('History');
  const lastRow = sheet.getLastRow();
  sheet.getRange(lastRow + 1, 1).setValue(date);
  sheet.getRange(lastRow + 1, 2).setValue(option);
}

そして最後にiPhone側の設定。 下記のようにショートカットを設定します。

  • URL
    • GASの公開URLを指定
    • パラメータでパスワードを指定(?api_key=~)
  • Get Contents of URL
    • 上記URLにGETリクエス
  • Split Text
    • 上記レスポンスをNew Linesでスプリット
  • Choose from List
    • 上記リストから選択 -> 後述のoption
  • List
    • 確認画面兼日付選択画面
    • Today, Yesterdayとかよく選ぶ日付を列挙しておく
  • Choose from List
    • promptで "When did you do ${option} ?" とか表示しておく
    • 上記リストから選択 -> 後述のdate
  • URL
    • GASの公開URLを指定
  • Get Contents of URL
    • 上記URLにPOSTリクエス
    • api_key, option, dateをRequest Bodyに追加
  • Get Dictionary from Input
    • レスポンスのJSONを辞書型として受け取る
  • Speak Text
    • messageの値を喋らせる

ショートカットから実行するとこんな感じでリストが表示されて、簡単に実施日を変更できるようになります。結構便利。

f:id:tshohe:20191021012409p:plain
選択肢一覧

f:id:tshohe:20191021012431p:plain
日付選択画面(というか確認画面)