ママはゆるキャリ!

ヲタママエンジニアが興味の向くままに書く雑記ブログ

【Slack+GAS】子供の保育園行事をSlackに自動通知させてみた

今通っている認可保育園では、大小様々なイベントが行われます。

親も参加する行事はGoogleカレンダーに予定を入れるのでわかりますが、子供たちだけで行う行事についてはほぼ把握していません。

お迎えの帰り道に「今日保育園でどんなことしたの?」と月並な質問をしてしまうのですが、質問が漠然としすぎて「あそんだー」となることもしばしば*1

ならばせめて、その日保育園でどんなイベントがあったかを知ったうえで話題を振ろうと思い、家族Slack*2に自動通知するようにしてみました。

やってみたこと

構成

f:id:tomo-sankaku:20180608013956p:plain:w500

GASを使えば仕組みは単純です。4月にもらう年間予定表の内容をデータ化し、そこから今日のイベント情報を参照、整形してSlackに通知します。

完成イメージ

▼イベントがある日の場合

f:id:tomo-sankaku:20180608005745p:plain:w300

▼イベントがない日の場合

f:id:tomo-sankaku:20180608005759p:plain:w300

※実際の運用では、娘の写真(イチオシ/変顔)を使ったカスタム絵文字で投稿されますw

手順

  1. 【スプレッドシート】イベント一覧作成
  2. 【Slack】IncomingWebHooks設定
  3. 【GAS】スクリプト作成
  4. 【GAS】定期実行させる

【スプレッドシート】イベント一覧作成

年間予定表の内容をひたすら転記します。こんな表にしてみました。

f:id:tomo-sankaku:20180608005712p:plain:w300

【Slack】IncomingWebHooks設定

Slackの設定画面から、IncomingWebHooksを設定します。

ここで得られたWebhookURLをコピーしておきます。

【GAS】スクリプト作成

先程の予定表スプレッドシートの「ツール」>「スクリプトエディタ」からスクリプトエディタを起動します。

処理の順序はこんな感じです。

  1. 今日の日付オブジェクトを取得
  2. スプレッドシートオブジェクトを取得
  3. 日付が一致するデータを検索
  4. 文言を整形する
  5. WebhookURLにデータを送信
/**
 * 今日の保育園イベントを通知する
*/
function postHoikuenEvent() {
  var prop = PropertiesService.getScriptProperties().getProperties();
  var postUrl = prop.webHookUrl;
  var botName = '保育園イベント通知bot';
  var botIcon = ':sleeping:';
  var message = '';
  var events = [];

  var date = Moment.moment().format("YYYY/M/D");
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var dataRow = findRow(sheet,date,1);

  if(dataRow.length > 0){
    for(var i=0;i<dataRow.length;i++){
        var eventName = sheet.getRange(dataRow[i], 2).getValue();
        var parentJoin = (sheet.getRange(dataRow[i], 3).getValue() == 1) ? " ★親参加★" : "";
    events.push("・" + eventName + parentJoin);
    }
    message = "本日" + date + "の保育園イベントは\n" + events.join("\n") + "\nです。";
    botIcon = ":kissing_smiling_eyes:";    //イベントありの場合はアイコンを変える
  }else{
    message = "本日" + date + "の保育園イベントはありません";
  }

  var jsonData =
  {
     "username" : botName,
     "icon_emoji": botIcon,
     "text" : message
  };
  var payload = JSON.stringify(jsonData);

  var options =
  {
    "method" : "post",
    "contentType" : "application/json",
    "payload" : payload
  };

  try{
    UrlFetchApp.fetch(postUrl, options);  //Slackに投稿
  }catch(e){
    Logger.log("Message:" + e.message + "\nFileName:" + e.fileName + "\nLineNumber:" + e.lineNumber)
  }
}

/**
 * スプレッドシートから特定の日付をもつデータの行数を取得
*/
function findRow(sheet,val,col){
  var dat = sheet.getDataRange().getValues();
  var result = []; 
  for(var i=1;i<dat.length;i++){
    if(Moment.moment(dat[i][col-1]).isSame(val,'day')){
      result.push(i+1);
    }
  }
  return result;
}

スプレッドシートオブジェクトから、条件に合うデータを検索しその行数を返すところは以下ページを参考にし、日付比較処理を追加したり複数件ヒットしても返せるようにアレンジしました。

tonari-it.com

また、絵文字やBot名はSlack側の設定そのままなら不要ですが、カスタム絵文字を指定したい場合やパターンによって変えたい場合はここで定義すればよしなに変えることができます。そのほか、POST内容のTipsは以下の記事に色々載っています。

qiita.com

【GAS】定期実行させる

GASならトリガー設定も簡単です。「編集」>「現在のプロジェクトのトリガー」を選択します。

例えば、仕事が終わってお迎えに行く途中で通知して欲しい場合は、このように設定します。

f:id:tomo-sankaku:20180608020628p:plain:w500

つまずいた点

日付比較

データ比較処理のところではじめは参考サイト通りにこう書いていましたが、あえなく失敗しました。

var date = new Date();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var dataRow = findRow(sheet,date.toLocaleDateString(),1)

(中略)

function findRow(sheet,val,col){
  var dat = sheet.getDataRange().getValues();
  for(var i=1;i<dat.length;i++){
    if(dat[i][col-1] === val){
      return i+1;
    }
  }
  return 0;
}

スプレッドシートに「2018/6/7」とあっても、実際にシートオブジェクトから取得したデータは形式が異なる*3ため、両者のデータ形式を一致させて比較しないといけません。

このあとシートから取得した日付カラムの値をフォーマット整形してちゃんと比較できるようになりましたが、もう少し調べてみるとDateよりも使いやすいMoment.jsなるものを知りました。

ライブラリキー:MHMchiX6c1bwSqGM1PZiW_PxhMjh3Sh48

これをライブラリに入れることで一気に扱いやすくなりました。

var date = Moment.moment().format("YYYY/M/D");
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var dataRow = findRow(sheet,date,1);

(中略)

function findRow(sheet,val,col){
  var dat = sheet.getDataRange().getValues();
  for(var i=1;i<dat.length;i++){
    if(Moment.moment(dat[i][col-1]).format("YYYY/M/D") === val){
      return i+1;
    }
  }
  return 0;
}

Dateで日付を取り扱うとすごく回りくどいコードになって苦手意識が強かったのですが、Moment.jsならそれが解消できて目からうろこでした。

こちらのサイトが参考になりました。ありがとうございます!

tonari-it.com

日付比較するならisSame()という便利な関数もあるので、最終的にこれに書き換えました。

tonari-it.com

課題

今回はここまでですが、他にも色々アレンジできそうだなぁと思いました。余裕があれば試してみようと思います。

  • 次週のイベント予告
  • Slackの発言をトリガーとして通知
  • attachmentを使った通知欄の装飾( "・" + がなんかダサいw)
  • 予定表スプレッドシートの古いデータ自動削除

*1:3歳児クラスになってからは今日の出来事の掲示があったり、娘自ら発言してくれることもたまにあるので、以前より状況は見えやすくなってはいます。

*2:家族用Slackについては過去記事参照。 tomo-sankaku.hateblo.jp

*3:実際に取得データを出力してみると「Thu Jun 07 00:00:00 GMT+09:00 2018」となっています