ari23の研究ノート

メーカ勤務エンジニアの技術ブログです

ラジオ体操を毎日通知するbotをGASで開発して、運動不足を解消する!

新型コロナウイルス1ほんとに怖いですよね。

私も緊急事態宣言に伴い、ここ最近は在宅勤務ですが、お陰様で運動不足に拍車がかかっておりますw

そこで今回は、運動不足をテクノロジーで解決したする方法を記事にします🐜

運動不足を解消するソリューション

室内で効果的にできる運動といえばラジオ体操だろうという安易な考えにより、「毎日決まった時間にラジオ体操を促す通知を送る」を提案します!

ラジオ体操って、子供の頃に体育の授業でダラダラとやっていたイメージがありましたが、本気でやると結構良い運動になるんですよ!

特に筋トレは面倒くさいし、あんまり時間を取りたくないっていう方にオススメです2

開発環境

下記のような設計をしました。

GAS-Slack-LINEの構成
GAS-Slack-LINEの構成

GASのトリガーを毎日9時~10時と15時~16時に設定し、1日2回通知が飛ぶように設定します。

GASとは

こういったアプリ開発のとき、必ず問題となるのはサーバです。レンタルサーバの使用も考えたのですが、私自身サーバの知識が乏しいため、できるだけ時間をかけず、かつ無料でやりたいと思い、今回はGASを利用しました。

GASとは、Google Apps Scriptのことで、簡単に言うと「クラウドで使えるExcelのVBA」です。つまり、プログラムが書けてしまいます。

しかも、GASで使ったデータはExcelのようにセルに書き込んでしまえばいいので、面倒なデータベースの構築も不要です。

また、GASで書いたプログラムを定期実行することができるので、いわゆるサーバーレスでやりたいことが実現できてしまいます。

プログラミング言語は、JavaScriptとほぼ同じで、ちょこちょこVBAっぽい記法です。
JavaScript自体は難しい言語ではないので、修得は比較的容易です。

通知先

私は普段連絡手段として、業務ではSlack、プライベートではLINEをよく使っていて、ここに通知がくれば、だいたい目を通しています。
これらのSNSでアプリを開発すれば、会社の同僚や友人に簡単に使ってもらえますので、今回はSlackLINEを対象とします。

通知アプリとしてSlackの場合はIncoming Webhooksを、LINEの場合はLINE Notifyを使用します。

LINEには通知手段として、LINE BOTとLINE Notifyの2つがありますが、LINE Botはpost数の制限があるため、使用しませんでした3

通知情報

通知情報はラジオ体操で、せっかくなのでYoutube動画を通知することにしました。

また、毎回同じものを流すのは飽きてしまうので、事前にいくつかURLを控えておき、その中からランダムで通知するような設計にしました。

ソースコード

私が作成したソースコードは以下の通りです。

function postRadioExersise() {
  // ------- post youtube ------- //
  // --- 設定値 --- //
  // スプレッドシート
  var ssCovid19 = SpreadsheetApp.getActive();
  var shRadioExer = ssCovid19.getSheetByName('radioexersise');
  var numFirstRow = 3;  // 速度を考慮しベタ打ち
  var numLastRow = shRadioExer.getLastRow();
  var numColMsg = 3;  // 速度を考慮しベタ打ち
  var numColUrl = 4;  // 速度を考慮しベタ打ち
  var numRow = getRandomInt(numFirstRow, numLastRow);  // 連続で同じものが出ちゃう可能性あり
  var msg = null;
  var url = null;


  // --- 情報準備 --- //
  // スプレッドシートから取得
  var msg = shRadioExer.getRange(numRow, numColMsg).getValue();
  var url = shRadioExer.getRange(numRow, numColUrl).getValue();

  // 送信するテキスト作成
  msg = '\n' + msg;
  msg += '\n(再生できない場合はURLをクリックorタップして下さい。)\n';
  msg += url;


  // --- post --- //
  postSlackAll(username, msg);
  postLINEAll(msg);
}

function getRandomInt(numMin, numMax){
  num = numMin + Math.floor(Math.random() * (numMax - numMin + 1));
  return num;
}

function postSlackAll(username, message){  
  var whurlSlack1 = PropertiesService.getScriptProperties().getProperty('whurlSlack1');
  var whurlSlack2 = PropertiesService.getScriptProperties().getProperty('whurlSlack2');
  var whurlSlack3 = PropertiesService.getScriptProperties().getProperty('whurlSlack3');

  var payload = ({'username': username, 'text':message});

  var request1 = {
      'url': whurlSlack1,
      'method': 'post',
      'contentType': 'application/json',
      'payload': JSON.stringify(payload)
  };

  var request2 = {
      'url': whurlSlack2,
      'method': 'post',
      'contentType': 'application/json',
      'payload': JSON.stringify(payload)
  };

  var request3 = {
      'url': whurlSlack3,
      'method': 'post',
      'contentType': 'application/json',
      'payload': JSON.stringify(payload)
  };

  UrlFetchApp.fetchAll([request1, request2, request3]);
}

function postLINEAll(message){
  var urlLINE = 'https://notify-api.line.me/api/notify';
  var tokenLINE1 = PropertiesService.getScriptProperties().getProperty('tokenLINE1')
  var tokenLINE2 = PropertiesService.getScriptProperties().getProperty('tokenLINE2')
  var tokenLINE3 = PropertiesService.getScriptProperties().getProperty('tokenLINE3')

  var request1 = {
      'url': urlLINE,
      'method': 'post',
      'payload': {'message': message},
      'headers': {'Authorization': 'Bearer ' + tokenLINE1}
  };

  var request2 = {
      'url': urlLINE,
      'method': 'post',
      'payload': {'message': message},
      'headers': {'Authorization': 'Bearer ' + tokenLINE2}
  };

  var request3 = {
      'url': urlLINE,
      'method': 'post',
      'payload': {'message': message},
      'headers': {'Authorization': 'Bearer ' + tokenLINE3}
  };

  UrlFetchApp.fetchAll([request1, request2, request3
}

解説

ネットにたくさんの関連情報があるので、ここでは比較的情報が少ない点を中心に解説します。

スプレッドシートから情報取得

  // --- 設定値 --- //
  // スプレッドシート
  var ssCovid19 = SpreadsheetApp.getActive();
  var shRadioExer = ssCovid19.getSheetByName('radioexersise');
  var numFirstRow = 3;  // 速度を考慮しベタ打ち
  var numLastRow = shRadioExer.getLastRow();
  var numColMsg = 3;  // 速度を考慮しベタ打ち
  var numColUrl = 4;  // 速度を考慮しベタ打ち
  var numRow = getRandomInt(numFirstRow, numLastRow);  // 連続で同じものが出ちゃう可能性あり
  var msg = null;
  var url = null;


  // --- 情報準備 --- //
  // スプレッドシートから取得
  var msg = shRadioExer.getRange(numRow, numColMsg).getValue();
  var url = shRadioExer.getRange(numRow, numColUrl).getValue();

GoogleスプレッドシートはExcelと同じように、GASからスプレッドシートへの操作がものすごく遅いです。また、GASでは動作時間が6分間と制限が設けられているため、できるだけ高速に動くよう配慮しなければなりません。

本当は「message」や「url」など列見出し名からセルの位置を取りに行きたいところですが、位置を決めてしまえば今後大きく変わらないだろうと判断し、

  • データの最初の行番号
  • 通知するメッセージの列番号
  • 通知するURLの列番号

はベタ打ちすることにしました。

ちなみに、スプレッドシートのデータ構造は以下のようにしています。

スプレッドシードのデータ構造
スプレッドシードのデータ構造

UrlFetchApp.fetchAll

  UrlFetchApp.fetchAll([request1, request2, request3]);

Slack、LINEともに複数の宛先に同時に通知を送るので、GASの動作時間に制限があることを考慮して、通知は並列処理すべきです。

私は普段あまり並列処理を書かないので苦手意識がありましたが、UrlFetchApp.fetchを非同期処理できるUrlFetchApp.fetchAllがGASに実装されました。
これを使って、GASの高速化を図っています。

使い方の参考として、UrlFetchApp.fetchとUrlFetchApp.fetchAllを比較したソースコードを以下に記載します。

function myFunction(){
  // --- 設定値 --- //
  // 通知用URL
  var urlLINE = 'https://notify-api.line.me/api/notify';
  // LINE Notifyから通知するグループごとに発行されるトークン
  var token1 = 'qwertyuiop';  // ←発行されたトークンを文字列として格納
  var token2 = 'asdfghjkl';  // ←発行されたトークンを文字列として格納
  // 通知するテキスト
  var message = 'test message';

  // --- UrlFetchApp.fetchの場合 --- //
  var options1 = {
      'method': 'post',
      'payload': {'message': message},
      'headers': {'Authorization': 'Bearer ' + token1}
  };
  var options2 = {
      'method': 'post',
      'payload': {'message': message},
      'headers': {'Authorization': 'Bearer ' + token2}
  };

  UrlFetchApp.fetch(urlLINE, options1);
  UrlFetchApp.fetch(urlLINE, options2);

  // --- UrlFetchApp.fetchAllの場合 --- //
  var request1 = {
      'url': urlLINE,   // <- これを追加するだけでOK
      'method': 'post',
      'payload': {'message': message},
      'headers': {'Authorization': 'Bearer ' + token1}
  };

  var request2 = {
      'url': urlLINE,   // <- これを追加するだけでOK
      'method': 'post',
      'payload': {'message': message},
      'headers': {'Authorization': 'Bearer ' + token2}
  };

  UrlFetchApp.fetchAll([request1, request2]);
}

効果測定

工事中

しばらく使ってもらったあとに、同僚や友人からざっくばらんに聞いてみる予定です。

おわりに

在宅勤務を始めてしばらくした頃に身体が固くなっているのが実感としてありましたが、なかなか運動する気にはなれずにいました。

しかし、今はこのアプリで妻アリ🐜と毎日必ず2回体操しています。運動だけでなく良い気分転換にもなっていますね。
「今日はどんなラジオ体操が出るかな?」と意外と楽しみにしてますw

参考になれば幸いです(^^)

参考文献

私が参考にした文献を以下に記載します。

リンク


  1. 余談ですが、『新型コロナウイルス(COVID-19)を倒したかを知る方法』はメディアの説明よりもすごく納得感もあるのでオススメです。英語ですが字幕付きなので全然大丈夫。

  2. つまり、それは私です。

  3. LINE Botは応答用、LINE Notifyは通知用みたいですね。