TAの仕事を楽にしたい！～GoogleフォームからSlackへの通知～

前置き長くなってます．．．飛ばす方は「できること」まで

# きっかけ
2022年10月，K研究室のロボ班に所属していた僕はTA業務に追われてました．
そう，「知能ロボット制御論」です．

簡単に説明しますと，前半５週でLegoでのライントレース，後半５週でKHRでのモーション作成をする，演習形式の講義です．どちらも最終回にはタイムや芸術性，得点を競う競技会をします．

ちょうど昨日，Legoの方の競技会が終わったところでした．今年は例年よりレベルが高くて面白かったなあ．

そんなことはさておき，「知能ロボット制御論」とは切っても切り離せない仕組みがあります．それは．．．「**時間外貸し出し**」です．

時間外貸し出しは，どう考えても５回（競技会除けば４回）という短い期間ではできない課題のために，我々TAが~~ボランティアで~~演習室を貸し出す仕組みです．

話は戻るのですが，2022年度は僕が初めてこの講義のTAになった年で，なぜか受講者数が前年度のほぼ**４倍**になっていました．

演習室は人であふれかえり，Lego本体も足りないし，あちこちで起きる不具合への対処．．．も～大変でした

第一回の講義を終えた僕はさらなる絶望を感じました．そう，時間外貸し出しです
というのも，前年度までは時間外貸し出しの予約を受講生からのメールで受け，それを整理して紙にシフトを書いてやりくりしていたというのを聞いていたので，この人数でそれやるの．．．と思ったのです

そこでI先輩に助言を求めたところ「Googleフォームならスプレッドシートと連携出来ていいよ」と教えてくださり，奮起した僕はもっともっとと最適化の旅に出かけたのでした．

# 出来ること
- Googleフォームの回答がスプレッドシートに保存される
- スプレッドシートの回答を並び替える（おまけ）
- Slackの特定のチャンネルに回答内容が通知される

# やること

簡単で
す！
まず初めにGoogleフォームを作ります．誰でもできますね．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/e2bc2e43-93df-5e7c-0921-2dfc6d2690d4.png)

そうしたら「回答」タブの「スプレッドシートにリンク」をクリックすれば，作成したシートに自動で回答をセルに書き込んでくれるようになります．一つ目終わり．

ここからSlackへの通知のための準備をしていきます．
流れとしては
1. SlackチャンネルのURLを取得
2. フォームのスクリプトエディタでSlackに通知する関数をコーディング
3. 関数を実行させるタイミングをトリガーで設定

こんなかんじです．

## 1. SlackチャンネルのURLを取得
実際に必要なのは「Incoming Webhook URL」です．どういうものかというと，通常のURLが「ショッピングモールの入り口」だとしたら，「荷物受け取り専用の受付口」になるそうです（ChatGPTくん調べ）．わかりにくいですね．

まず[Slackapiのページ↗](https://api.slack.com/apps)にアクセスします．

次にCreate New App → From scratchの順にクリックします．するとこんなポップアップが．

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/14c7d93b-0335-969e-ce04-3380feb6fad1.png">

App Nameに適当な名前をいれ（表示名は後からでも変えられます），２つ目でURLを取得したいワークスペースを選びます．

そしてできたアプリを開くとこんな画面に

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/42f486d3-eaf5-3b0c-b73d-9db7ad89b764.png)

ここでFeaturesタブの「Incoming Webhooks」で右上のスイッチでアクティベートさせます．

次に，アプリの表示名を設定します．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/001e129b-abac-007e-d9d5-cd6dee3d54ff.png)

FeaturesのApp HomeからApp Display NameをEditで変更できます．
（SettingのBasic Informationでアプリアイコンとかも変更できますよ！）

ようやく本題です．
Features → Incoming Webhooksで一番下にスクロールすると，「Add New Webhook to Workspace」とあるのでクリックします．すると以下の画面に．

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/58fd29cd-e4a7-66cd-afc4-89a104f2298a.png">

ここでチャンネルを選択して許可すれば，先ほど「Add New Webhook to Workspace」を押した上でURLがコピーできると思います．お疲れさまです．

ちなみに，今回紹介したのはSlackappを作成する方法ですが，もとからSlackの用意したアプリ（Slack marketplaceのIncoming Webhook）を使う方法もあります．ですが，公式から非推奨と言われてるみたいです．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/922fc228-d8b9-b2b5-e509-26820e839d55.png)

## 2. フォームのスクリプトエディタでSlackに通知する関数をコーディング
満を持してフォームに帰りましょう．設定の「スクリプトエディタ」を開きます．

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/2cdadff1-06fb-0f9f-17bf-88e4f9c246d8.png">

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/31bc6b15-a06a-58d0-109f-7ff86b298ff1.png)

いわゆるGAS（Google Apps Script）が立ち上がります．GASでは，コードに関数を書き，トリガーを設定することで好きなタイミングで関数を実行することができます．

とりあえず以下のコードをコピペしましょう．コードはほぼ他の方のコピーです．ありがとうございます．

```java:autoSlack
function autoSlack(e) {
  /* ステップ1: フォームのデータを取得する */
  //すべての質問と回答を取得する
  let itemResponses = e.response.getItemResponses();

  /* ステップ2: 必要なデータを抽出する */
  //個々の質問と回答を格納するための空配列を宣言する
  let questionAndAnswers = [];

  //for文(ループ)で変数itemResponsesから個々の質問と回答を取得する
  for(let i = 0; i < itemResponses.length; i++) {
    //質問のタイトルを取得する
    let questionTitle = itemResponses[i].getItem().getTitle();

    //回答を取得する
    let answer = itemResponses[i].getResponse();
    
    //未回答の質問かどうかで送信文章を調整する
    if(!answer) {
      questionAndAnswers.push(questionTitle + ": 未回答");
    } else {
      questionAndAnswers.push(questionTitle + "：" + answer);
    }
  }

  /* ステップ3: 宛先、本文を決める */
  //Slackの宛先 (チャンネル)
  //★★★SlackのIncoming Webhook URLを入力してください★★★
  let url = "入力してね！自分で！";

  //Slackの本文
  //★★★お好きな本文に変更ください★★★
  let body = "\nロボ演フォームの回答を受信しました。\n"
           + "\n"
             //一次元配列questionAndAnswersに対してjoinメソッドを使って文字列を作成する
             //区切り文字は改行"\n"
           + questionAndAnswers.join("\n");
  
  /* ステップ4: 指定したSlackチャンネルに通知を送信する */
  //Slackを送信する
  sendSlack(url, body);
}

function sendSlack(url, body) {
  let data = {
    "text": body
  }

  let options = {
    "method" : "post",
    "contentType": "application/json",
    "payload" : JSON.stringify(data)
  };

   UrlFetchApp.fetch(url, options);
}
```

## 3. 関数を実行させるタイミングをトリガーで設定
ついに大詰めです．
左のトリガータブから「＋トリガーを作成」をクリック．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/04ab6338-5c03-01e1-4b6a-1aafbdf72296.png)

「イベントの種類を選択」だけ起動時→フォーム送信時に変更して以下のようになればOK．関数autoSlackをフォーム送信時に実行してくれます．

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/9af09181-e0f2-09d8-8120-02e372c4fa3c.png">

保存した後，Googleアカウントでの承認を求められるのですが，初回の承認の時だけエラーのような表示がされてしまいます．このとき，「安全なページに戻る」ではなく左下の「詳細」から進めてください．（画像なくてすみません）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/665c35fb-2150-9c5c-b657-b2dc029848e0.png)

無事追加できれば終了です！お疲れさまでした．
↓こんな感じで通知されます．

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/ab7a3db9-fac1-8985-d88a-5321a5d4e55a.png">

# おまけ：スプレッドシートの並び替え
スプレッドシートも当然GASが使えるので，同様に~~コピペ~~ コーディングしましょう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3948000/c5cd31d3-a402-e982-cfe5-7851012f1a8e.png)

```java:sortByHope
function sortByHope() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet();
  var activeSheet = sheet.getActiveSheet();

  var lastRow = sheet.getLastRow();

  var range = "A2" + ":J" + lastRow;

  var range = activeSheet.getRange(range);

  range.sort([{column: 4, ascending: true}]);
  range.sort([{column: 3, ascending: true}]);
}
```
このコードではA2からJの範囲を４列目基準でソートした後に３列目でソートします．（確か）

追記：
後輩のS君がアップデートしてくれました！感激

# 参考にしたサイト
https://www.yukibnb.com/entry/gas_form_auto_slack_to_channel