GoogleカレンダーのイベントをHangouts Chatに通知するbotを作った話


概要

フューチャーAdventCalendar2019 5日目です。そしてQiita初投稿です。

・今年の6月ごろ、GoogleAppsScriptを使って、Google Hangouts Chat(G suiteで提供されている企業向けチャット)にGoogleカレンダーのイベント通知してくれるbotを作ったので、備忘もかねてまとめます。

・上の文章みてわかる通りGoogleサービスに依存しまくってます。Google大好きです。

実際に作ったもの

 弊社には、競技プログラミング部があるのですが、部のHangouts Chatのチャットルームでは各々コンテスト情報はじめ情報交換をしています。
 そのチャットルームに毎週、AtCoderのコンテスト情報を通知できたら便利かなあと思い作ってみました。もっと言うとこまめにコンテスト情報をチャットルームに発信されていた方(以下Hさん)が退職されてしまったため、「Hさんを実装する!」をモチベーションに作成しました。

 GASをつかってAtCoder社が公開しているGoogleカレンダーのイベント(コンテスト予定)を取得して、Hangouts ChatにPostします。

1. Webhookを取得する

  1. GASからHangouts ChatにPostするため、Webhookを発行します。
    Hangouts Chatのチャットルームの「Webhookを設定」するを押下し、webhookのURLを取得します。

  2. 着信Webhookの設定としてbotの名前、アイコンのURL(任意)を設定します。

  3. 保存後、コピーマークのボタンを押すと、WebhookのURLをクリップボードにコピーできます。

2. カレンダーのIDを取得する

Hangouts Chatに投稿する取得もとのカレンダーのIDを取得します。
取得元のカレンダーのGoogleカレンダーのページから、「設定」、「カレンダーの統合」の項目でカレンダーIDを確認できます。

3. GASで実装する

GAS上で、カレンダーのイベントを取得し、chatにポストするように実装します。
今回はコンテスト通知botですので、カレンダーに登録されたイベントを、当日から1ヶ月分取得し、イベント名、イベントの開始日時をchatに投稿します。
マラソン形式といった複数日にまたがるイベントもあるので、すでに開始している場合は、イベントの終了日時を取得することとします。

getEvent.gs
function getEvent() {
  var webhookUrl = 'https://XXXXXXXX'; // 取得したWebhookのURLを記載する
  var atCoderCal = CalendarApp.getCalendarById('XXXXXXXXXXXXX'); //取得するGoogleカレンダーのIDを指定する。
  var today = new Date();
  var startDate = new Date(today); //取得開始日は今日から
  var endDate = new Date(today);
  endDate.setMonth(endDate.getMonth()+1); //取得終了日は1か月後。
  var hello = 'こんにちは。\n 直近のAtCoderコンテストをお知らせします。\n ------------------------------- ';
  var noContest = 'コンテストは予定されていません。\n もしくはchokudaiさんがカレンダーに登録していません。';
  var atCoderEvents = atCoderCal.getEvents(startDate,endDate); //カレンダーのイベントを取得

  // イベントの数だけ繰り返してメッセージに入れ込む。 
  var maxRow = 1;
  var msg = hello;
  for each(var evt in atCoderEvents){ 
      if(evt.getStartTime() < today){
        // 開催中のイベントは終了日時を取得
        msg = msg +'\n '+ 'No.' + maxRow+'|'+ evt.getTitle()+'|開催中 ~'+ Utilities.formatDate(evt.getEndTime(),'GMT+0900','MM/dd (E) HH:mm');
      }else{
        // 開催前のイベントは開始日時を取得
      msg = msg +'\n '+ 'No.' + maxRow+'|'+ evt.getTitle()+'|'+ Utilities.formatDate(evt.getStartTime(),'GMT+0900','MM/dd (E) HH:mm');
      }
    maxRow++;
  }
    if (maxRow == 1){
      // コンテストがない場合
      msg = msg +'\n '+ noContest;
    }

  // ボットでchatに投稿する
  var botMessage = { 'text' : msg}
  var options = {
    'method': 'POST',
    'headers' : {
      'Content-Type': 'application/json; charset=UTF-8'
    },
    'payload':JSON.stringify(botMessage)   
  };
  var result = UrlFetchApp.fetch(webhookUrl, options);
  Logger.log(result);
}

4. 実行のトリガーを設定する。

実行トリガーを設定することで、毎日X時に実行、などという実行タイミングの設定ができます。

GASのAppsScriptダッシュボードから、時計マークを押下し、トリガーページへ遷移します。

「トリガーの追加」から、トリガーの設定を行います。
今回は、毎週金曜日の夕方に1回実行することとしました。

5. 動かしてみる


直近あるはずのABCがカレンダーに登録されていませんでしたw

atCoder社のけんしょうさんにカレンダー登録していただいた後に動かしたのが下記です。

(ソースでは時刻をフォーマットしていますが、動かした当時は開始日時をフォーマットしていませんでした。)

6. 学び

・カレンダーに予定を登録していないと通知してくれません。(あたりまえ)
・業務上JavaScriptを触ることがあまりなかったので、JavaScript(ES6以前ではありますが)の文法を再学習するきっかけになりました。
・とても簡単なものでも、あったらちょっと便利かなーってものを作ってお披露目すると予想以上に喜ばれました。競プロ部に生息する社内のつよつよエンジニアから反応もらえたのはうれしかったです。
・初めのモチベーション通り「Hさんの実装に成功」という言葉もいただきましたw

7.その後

・AtCoder公式カレンダーのメンテナンスが手動で追いついていないこともあり、AtCoderのサイトの「予定されたコンテスト」をスクレイピングして作成された非公式カレンダー(ibuki2003さん作成)を参照することにしました。

「AtCoderのコンテストカレンダー」(ibuki2003さん)
https://fuwa.dev/log/atcoder_calendar

・最近ではTopCoderやCodeforcesのカレンダーも読み込んで通知もしてくれるようにアップデートしました。

参考記事

GASからhangout chatにPostする
https://qiita.com/mistolteen/items/a67b131b31d5c5246a6c