Hangout Chatで空調アンケート集計Botを作ったら色々学べた話

こんにちは。
PORT株式会社の開発部でサーバサイドエンジニアで、また私生活では2児の父の@kehondaです。
今回は自社で運用しているプロダクトに関してのネタではなく、社内環境改善の一環として行なった施策の一例を紹介します。

今年の夏に非公式で空調に関するアンケートを集計するためのBotを作って運用してみたって話です。

できたものは、こんな感じです。
![スクリーンショット 2018-12-05 9.33.01.png](https://qiita-image-store.s3.amazonaws.com/0/140026/61f70e57-a049-86f7-7b3a-4d8953de9745.png)

集計結果
![スクリーンショット_2018-12-05_11_38_38.jpg](https://qiita-image-store.s3.amazonaws.com/0/140026/6264ef16-2986-b9fc-7dc5-fb7ebe746731.jpeg)

# 概要
- なぜ作ろうと思ったか
    - 余談
- 仕様
- 構成
- 実装
- 問題点
- 学び
- 最後に

# なぜ作ろうと思ったのか
オフィスの空調暑い寒い問題は結構あるあるな話だと思うのですが、弊社でもまさにそれがありました。限度が過ぎると、生産性に大きく関わる話なので、どうにかしたいなぁと考えていました。執務室が広かったりすると、空調の気温や風量を一概に決めにくいのかなぁと思います。

- エリアによって暑い・寒いが異なる
- 時間帯によって暑い・寒いが異なる
- 個人差によって暑い・寒いが異なる

そのため、まずはそれぞれがどう感じているのか（**今暑いのか、寒いのか、ちょうど良いのか**）を、可視化することで、室温を変えるためのきっかけが作れたり、席替えの提案など、社内環境改善のためのコミュニケーションがよりよくできるのでは、と考えました。

そこで、1日数回決まった時間に社員へのアンケートを送付し、個々人の直感を気軽に答えてもらえるようなBotを作り、データを集計してみよう、となりました。

## 余談（なぜHangout Chatなのか）
弊社の現場で働くメンバー（エンジニア・デザイナー・ディレクター・マーケターなど）はSlackを利用しています。
今回は、**従業員全員に参加してもらえるような仕組みを作りたかった**ので、ハングアウトは候補だったのですが、あまりにも使い勝手が悪く、ましてやBotが作れません。またslackのアカウントを持っている人が必ずしも全員ではない状況で試行錯誤した末、アンケートのプラットフォームは弊社環境で無料で使えるGoogleのHangoutChatを採用しました。

[Google、「ハングアウト」を2020年に終了か](http://www.itmedia.co.jp/news/articles/1812/01/news039.html)
[Google、一般向け「ハングアウト」も「ハングアウトChat」と「ハングアウトMeet」に分割へ](http://www.itmedia.co.jp/news/articles/1812/03/news051.html)

（最近こんな記事もタイムリーに出てきたので、もっとHangout Chatが使われる日もそう遠くはないはずだ・・！）

# 仕様
- 決まった時間にbotがアンケートを発言する
- ユーザはbotのアンケートに回答できる
- 回答データは集計されて、閲覧できる

こんな感じです。[polly](https://www.polly.ai/hangouts)等を利用していないのは、色々な軸で蓄積したデータを閲覧したかったからです。

# 構成
サーバーレスで気軽に作りたかったので、色々調べた結果下記のようにしました。
また、今回の構成は、**botが発言するフロー**、**botが回答を受けるフロー**、と2つに分け、集計データはre:dashで参照しました。

## botが発言するフロー
![スクリーンショット 2018-12-05 12.26.53.png](https://qiita-image-store.s3.amazonaws.com/0/140026/314a2cea-8b13-df6f-ff7d-8ade8443f946.png)

- AWS CloudWatchEventが実行のトリガー
    - 決まった時間にアンケート投げる
    - cron式で書けるので簡単にトリガー作れた
- AWS LambdaがHangout Chat APIを実行する
    - pythonで、hangout chat　apiへリクエストを送る
- Botがチャネルに発言

## botが回答を受けるフロー
![スクリーンショット 2018-12-05 12.28.10.png](https://qiita-image-store.s3.amazonaws.com/0/140026/4f32abf6-c3d3-52f7-0b0b-2da0843a516d.png)

- Botにリクエスト
    - 事前準備
    - Google Cloud Platformで接続先をApps Script Projectにしとく
- Google Apps ScriptでonCardClickハンドラーメソッドでコールバックを受け取り、[JDBC](https://developers.google.com/apps-script/guides/jdbc)経由でmysqlにデータ保存

GCPの設定をみていたらイベントハンドラーでGASが使えるのを知り、楽そうだったので使ってみました（*後述しますが、これが後々よくなかった*）

## 蓄積データの閲覧
![スクリーンショット 2018-12-05 12.28.16.png](https://qiita-image-store.s3.amazonaws.com/0/140026/9697ab26-3bff-89ac-29dd-dcbe0560f747.png)

- Mysqlをre:dashで参照

# 実装
事前準備として、GoogleCloudPlatformのConsoleでHangouts Chat APIを有効にする必要がありました。
![スクリーンショット 2018-12-05 15.55.31.png](https://qiita-image-store.s3.amazonaws.com/0/140026/c1da0af5-c081-d262-a294-80edf5502245.png)

## botが発言するフロー
pythonからAPIをcallするスクリプトを準備して、lambdaにアップロードして実行できるようにすればOKです。

特別なことはしてないんですが、Hangout Chatが用意しているComponentがあって、json形式でAPIのrequest bobyに渡すのですが、色々表現できて結構楽しいです。
[リファレンス](https://developers.google.com/hangouts/chat/reference/message-formats/cards)

```sample_body.json
{ "text" : '<users/all> 空調アンケートです。15分後に集計します。',
    "cards": [
      {
        "header": {
          "title": '自席にいる方に質問",
          "subtitle": "現在の体感温度はどうですか？",
          "imageUrl": "http://hoge.hoge/image.jpg"
        },
        "sections": [
          {
            "widgets": [
              {
                "buttons": [
                  {
                    "textButton": {
                      "text": "暑いです。",
                      "onClick": {
                        "action": {
                          "actionMethodName": "sample_method_name",
                          "parameters": [
                            {
                              "key": "feeling",
                              "value": "hot"
                            }
                          ]
                        }
                      }
                    }
                  }
                ]
              }
            ]
          },
          {
            "widgets": [
              {
                "buttons": [
                  {
                    "textButton": {
                      "text": "寒いです。",
                      "onClick": {
                        "action": {
                          "actionMethodName": "sample_method_name",
                          "parameters": [
                            {
                              "key": "feeling",
                              "value": "cold"
                            }
                          ]
                        }
                      }
                    }
                  }
                ]
              }
            ]
          },
          {
            "widgets": [
              {
                "buttons": [
                  {
                    "textButton": {
                      "text": "ちょうど良いです。",
                      "onClick": {
                        "action": {
                          "actionMethodName": "sample_method_name",
                          "parameters": [
                            {
                              "key": "feeling",
                              "value": "just"
                            }
                          ]
                        }
                      }
                    }
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
```
![スクリーンショット 2018-12-05 9.33.01.png](https://qiita-image-store.s3.amazonaws.com/0/140026/cdeab859-6573-2cd3-4094-338df84dfa0c.png)
Card　Formatで組んでみました。

### botが回答を受けるフロー
今回GCP側の設定で、botの接続先をGASにしたと前述しました。
![スクリーンショット_2018-12-05_16_04_52.jpg](https://qiita-image-store.s3.amazonaws.com/0/140026/ebfb4a96-99bd-7689-27db-ac608d633d5c.jpeg)

そうしたことで、GASで以下のようにイベントを受け取れます。
`onCardClick`の引数`event`には、クリックしたユーザの情報や、どのボタンをクリックしたかの情報が入ってくるので、その情報を整形し、Mysqlへ保存しました。（DBの接続には[JDBC](https://developers.google.com/apps-script/guides/jdbc)）を利用しました。

```gas.js
function onCardClick(event) {
  // 押したユーザの表示名
  console.log(event.user.displayName);
  // 押したユーザのメールアドレス
  console.log(event.user.email);
  // ボタンに設定したactionMethodName
  console.log(event.action.actionMethodName);
　　　　// ボタンに設定したparameters[0].value
  console.log(event.action.parameters[0].value);
}
```

# 問題点
楽だからという理由で、回答を受けるフローで接続先をGASでJDBCを利用してDBに回答を保存したら、
**Bot利用する人に初回にJDBC利用権限を許可してもらわないといけなくなりました。**

回答しようとするとこんな感じで
![スクリーンショット 2018-12-05 10.27.19.png](https://qiita-image-store.s3.amazonaws.com/0/140026/a28a22df-e0fd-045e-6557-f7e9bfc0a008.png)

![スクリーンショット_2018-07-12_16_22_21.jpg](https://qiita-image-store.s3.amazonaws.com/0/140026/a3bfcff4-4bef-1d22-2f03-0b9d1b5f091e.jpeg)

使えなくはないですが、気軽さは軽減してしまいました。

# 実装面の学び
- Hangout Chatの接続先として、GASだとできることは限定される
    - とりあえず返答したりするだけだったら十分だが。。
    - 接続先をURLにして、AWSのAPIGatewayとLambdaでやればよかったかな・・
    - チャットボットはとりあえず軽く作って動かしてみないとわからない

今度はAPIGatewayとLambdaでサーバーレスな構成で実現してみようと思っています。

また、最近**AWS lambdaがrubyも使えるようになった**ので、私自身としては楽ができて嬉しいです。
[［速報］AWS LambdaがRubyに対応。さらにカスタムランタイムであらゆるプログラミング言語にも対応へ。AWS re:Invent 2018](https://www.publickey1.jp/blog/18/aws_lambdarubyaws_reinvent_2018.html)

この後のPORTアドベントカレンダーで@rnitta くんがAWS LambdaでRubyを使った記事を書いてくれるので期待！

# 最後に
運用してみての感想ですが

- 今まで声をあげなかった人やチームが気軽に答えてくれた
- 場所や人によって感じ方が全然違うことがわかった
- バックオフィス側のメンバーと会話したり提案するきっかけになった

など、社内向けサービスを公開することで、他部署の方とコミュニケーションをするきっかけになったように感じました。
エンジニアなんで、社内環境もテクノロジーで解決していきたいですね。

---

最後になりますが、PORT株式会社では[自社サービスを支えてくれる優秀なRubyエンジニアを募集しています](https://www.theport.jp/)(Rubyエンジニア以外も)。
もくもく会も行なっていますので、ぜひ一緒にもくもくしましょう！
[PORTもくもく会](https://freestyle-mokumoku.connpass.com/)


[PORT Advent Calendar](https://qiita.com/advent-calendar/2018/port)はまだまだ続きます。乞うご期待！