Google Apps Script (GAS) で Slack アプリを作る

Slack GAS 2019年 6月 14日

作成のきっかけ

私が担任する 3300 クラスの Slack 運用の話を聞いて、5 年生も有志で Slack 運用を開始した。 すると、早速うちの卒研生が週番を表示するアプリを作成した。 さすが若い学生は頭が柔らかい。 面白い取り組みなので、やり方を教わって自分でも作ってみた。 学生の作ったコードは、少し面倒なやり方をしていたので、私なりにアレンジしてみた。 ちなみに、作成した学生に私の修正部分をフィードバックしたら、早速取り入れてバージョンアップしていた。さすがだね。

作成したアプリ

週番は1週間ごとに3人を割り当てている。 これまでは月曜日にその週の週番の番号を Slack の学級日誌チャンネルに投稿していた。 今回作成したアプリは、これを自動的に投稿するように作成する。 テストのために、以下のコマンドを追加で作成する。

  • /shuuban: 現在の週番を表示する。
  • /shuuban next: 次の週番に切り替えて表示する。
  • /shuuban prev: 前の週番に戻して表示する。
  • /shuuban start: 自動更新を開始する。
  • /shuuban stop: 自動更新を止める(長期休業中など)
  • /shuuban help: これらのコマンドの説明を表示する。

アプリの作成

以下、アプリ作成手順を示す。

Slack 投稿の設定

  1. テスト用のチャンネル「テストスペース」を用意する。
  2. slack api にアクセスし、「Create a Slack App」ボタンをクリックする。
  3. アプリ名「Shuuban」を記載し、利用するワークスペースを選択し、「Create App」をクリックする。 Create a Slack App
  4. アプリが作成できたら、Features の Incoming Webhooks を On にする。 投稿するチャンネルは先ほど作成した「テストスペース」とする。 Incoming Webhooks
  5. 設定が正しくできたかどうかは、「Sample curl request to post to a channel:」に書かれたコマンドを実行してみれば確認できる。curl コマンドで指定された URL に POST することで、「Hello, World!」とチャンネルに投稿できれば成功である。
    curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello, World!"}' 「URL」

Google Spreadsheet の設定

  1. Google Spreadsheet に以下のようにデータを入力する。
    1. Spreadsheet の名前は Shuuban とする。
    2. A1 には自動、B1 には 1 を入力する。B1 が 1 の時は月曜日に週番が更新される。
    3. A2 から下に学生番号、B2 から下に学生の氏名を入力する。A2 から B4 までが現在の週番である。この部分のデータをローテーションすることで週番の交代を行う(図の例では 3329 から 3331 までが今週の週番)。
    Spreadsheet
  2. この Google Spreadsheet に Google Apps Script を連携させる。まず、「ツール」メニューの「スクリプトエディタ」を開く。 scriptEditor
  3. コード.js に以下のようにスクリプトを書いた。各関数の内容は以下の通り。
    postSlack(text)
    text を指定したチャンネルに投稿する。Post URL は上で作成した URL。
    doPost(e)
    Slack のコマンドから呼び出される。ここでコマンドの引数を調べ、対応する関数を呼び出す。help 以外は結果をチャンネルに投稿する。help は個人の画面のみにテキストを表示するようにした。
    getSheet()
    スプレッドシートのアクティブシートを得るためのコンビニエンス関数。他の関数から呼び出される。
    getMembers(text)
    テキストとともに現在の週番を表示する
    nowShuuban()
    現在の週番を表示する
    nextShuuban()
    現在の週番を一番下に移動し、抜けた3行を削除する。その後、新しい週番を表示する。
    prevShuuban()
    nextShuuban で下に移動してしまった週番を上に戻し、その後、元の週番を表示する。
    changeAuto(newValue)
    自動更新のオン・オフを newValue で設定する。
    startAuto()
    自動更新をオンにする。
    stopAuto()
    自動更新をオフにする。
    autoNextShuuban()
    自動更新がオンの時に、週番を更新する。月曜日に定期的に呼び出される。
    function postSlack(text){
      var url = "Slack の Post URL";//WebhookURL
      var options = {
        "method" : "POST",
        "headers": {"Content-type": "application/json"},
        "payload" : '{"text":"' + text + '"}'
      };
      UrlFetchApp.fetch(url, options);
    }
    
    function doPost(e) {
      var command = e.parameter.text;
    
      if (/next/i.test(command)) {
        nextShuuban();
      } else if (/prev/i.test(command)) {
        prevShuuban();
      } else if (/gomi/i.test(command)) {
        postSlack("ゴミが溜まっています。処理をお願いします");
      } else if (/stop/i.test(command)) {
        stopAuto();
      } else if (/start/i.test(command)) {
        startAuto();
      } else if (/help/i.test(command)) {
        var message = ["/shuuban next: 強制的に次の週番に変更します。",
                       "/shuuban prev: 強制的に前の週番に変更します。",
                       "/shuuban gomi: ゴミが溜まっていることを週番に報告します。",
                       "/shuuban start: 週番の自動更新を開始します。",
                       "/shuuban stop: 週番の自動更新を終了します(長期休業中など)。"
                      ].join("\n");
        return ContentService.createTextOutput(JSON.stringify({text: message})).setMimeType(ContentService.MimeType.JSON);
      } else{
        nowShuuban();
      }
      return ContentService.createTextOutput("");
    }
    
    function getSheet() {
      return SpreadsheetApp.getActiveSheet();
    }
    
    function getMembers(text) {
      var sheet = getSheet();
      var values = sheet.getSheetValues(2, 1, 3, 2);
      postSlack(text + "\n" + (values.map(function( value ) {
        return value.join(' ');
      }).join("\n")));
    }
    
    function nowShuuban() {
      getMembers("現在の週番を表示します。");
    }
    
    function nextShuuban() {
      var sheet = getSheet();
      var lastrow = sheet.getLastRow();
      sheet.getRange("A2:B4").moveTo(sheet.getRange("A"+(lastrow+1)));
      sheet.deleteRows(2, 3);
      getMembers("週番を交代します。");
    }
    
    function prevShuuban() {
      var sheet = getSheet();
      var lastrow = sheet.getLastRow();
      sheet.insertRowsAfter(1, 3);
      sheet.getRange("A"+(lastrow+1)+":B"+(lastrow+3)).moveTo(sheet.getRange("A2"));
      getMembers("週番を元に戻します。");
    }
    
    function changeAuto(newValue) {
      var sheet = getSheet();
      var cell = sheet.getRange("B1");
      var autoFlag = cell.getValue();
      if (autoFlag == newValue) {
        postSlack(["すでに児童更新は止まっています。", "すでに自動更新は動いています。"][autoFlag]);
      } else {
        cell.setValue(newValue);
        postSlack(["自動更新を止めます。", "自動更新を開始します。"][newValue]);
      }
    }
    
    function startAuto() {
      return changeAuto(1);
    }
    
    function stopAuto() {
      return changeAuto(0);
    }
    
    function autoNextShuuban() {
      var sheet = getSheet();
      var autoFlag = sheet.getRange("B1").getValue();
      if (autoFlag == 1) {
        nextShuuban();
      }
    }
  4. 作成した関数がうまく動くかを確認するために、実行したい関数を選択して再生ボタンを押してみる。以下の関数は直接テストできる。
    nowShuuban()
    現在の週番がポストされるかを確認
    nextShuuban()
    次の週番に変更になり、さらにそのことがポストされるかを確認
    prevShuuban()
    前の週番に戻され、さらにそのことがポストされるかを確認
    startAuto()
    B1 のセルが 0 の時には 1 に変更され、自動更新オンがポストされるかを確認。すでに 1 の時にはその旨が表示されるかを確認
    stopAuto()
    B1 のセルが 1 の時には 0 に変更され、自動更新オフがポストされるかを確認。すでに 0 の時にはその旨が表示されるかを確認
    autoNextShuuban()
    自動更新がオンの時に、週番が更新されるかを確認。オフの時に何もしないことを確認
    exec Functions
  5. 作成したスクリプトをウェブアプリケーションとして導入する。ここで、スクリプトを修正したら必ず「New」として新しくデプロイし直すこと。更新しても Web アプリケーションの URL は変更とならない。この後表示される ウェブアプリケーションの URL は別途記録しておく。
  6. webApplication
  7. また、Slack との連携前に GAS 側で定時トリガーを設定してしまう。編集メニューにおいて、現在のプロジェクトのトリガーを表示する。設定した値は以下の通り。
    • 実行する関数を選択: autoNextShuuban
    • デプロイ時に実行: Head
    • イベントのソースを選択: 時間主導型
    • 時間ベースのトリガーのタイプを選択: 週ベースのタイマー
    • 曜日を選択: 毎週月曜日
    • 時刻を選択: 午前 7 時から 8 時
  8. trigger
  9. 以上で GAS 側は終了

Slack のコマンド設定

  1. slack api にアクセスし、Shuuban アプリを表示後に左から「Slack Commands」を選択する。
  2. 「Create New command」をクリックし、下図のように設定する(ただし、以下の画面はその後の編集画面である)。ここで、Request URL は先ほど GAS で記録しておいた URL である。
  3. SlashCommand
  4. 保存をすると、/shuuban コマンドが実行できるようになっているはずである。テストスペースで確認してみる。

学級日誌チャンネルへの変更

このままでは週番の交代はテストスペースに常に表示されてしまう。このため、学級日誌のページにこれらを表示するように変更する必要がある。以下の手順でポストさきを変更した。

  1. Slack api の Incoming Webhooks で「Add New Webhook to Workspace」をクリックし、学級日誌チャンネルを選ぶ。
  2. GAS の PostSlack にある URL をここで作成された Webhook URL に差し替える。これで投稿先は、変更された。
  3. GAS を更新するために、再度ウェブアプリケーションとして導入を実行し、新規にデプロイする。

おわりに

これらの作業により、週番の更新が自動化された。 後は長期休業前に「/shuuban stop」、後期が始まったら「/shuuban start」するだけでよい。 また、週番がゴミ捨てをしていないようだったら、「/shuuban gomi」とするようになっている。 ただ、学級日誌を Slack にしてから、清掃報告はかなりちゃんと行われていて、このコマンドを使わなければならない時がほとんどないようだ。

Google Spreadsheet と Slack の連携はかなり面白そうだ。もっと面白いものが作れないか学生と相談してみよう。