Google HomeにSlackのメッセージを自発的に読んでもらう（Amazon Pollyの声で）

# 課題

- スマートスピーカーが流行って久しいが、どれもこれも自分で話しかけないといけない。何か新着情報を知りたい時、プル型のコミュニケーションが必要
- プッシュ型で通知を受け取ろうとすると、モバイル端末かデスクトップ通知に頼るので、視覚的に把握しなければならないし、認証だなんだで情報一つ見るのに手間がかかる
- 耳という入力経路を持っているのに、耳への入力がプッシュ型ではないので、そこをなんとかしたい

# 解決策

- スマートスピーカーが自発的に喋ればよい

# どう実現するか

## 検討したこと

### スマートスピーカー
- 外から指示して喋ってくれるスマートスピーカーはほとんどない
- 標準機能では、カレンダー連携やリマインダーで、ある時刻になったら喋るような感じにしかできない
- Google Homeはどうやら任意の音声を再生することができるらしい

### 通知の受け渡し
- AWSのSQSとSNSを評価したものの、コストが高かったり、通知を受ける側がモバイルデバイスやAWS内のリソースである必要があった
- Google Cloud Pub/Subはよさそう
- 新たにPub/Subする経路を設けるよりは、普通に使っているSlackがそのまま使えればいいのではないか

### 誰が処理を中継するか
- ローカルマシンでDocker使う
- もしくはRaspberry Piでやる
- [Google Home Notifier](https://www.npmjs.com/package/google-home-notifier)を試したけどどうもすんなり動かず、いろいろ手を加える必要があった。その他作るべきモノもある


## 最終的に実装したもの

<img width="1830" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107873/3b3bdafc-2fa0-7ae7-ddf8-f8fb9fa4db17.png">


### 1. Google Home - Amazon Polly - REST API

[google-home-amazon-polly-bridge](https://github.com/hjmsano/google-home-amazon-polly-bridge)を作った。

- Google Homeに喋らせる言葉を受け取るHTTP APIエンドポイント
- 諸設定と文言を受け取って、Amazon Pollyを使って音声合成
- [castv2-client](https://www.npmjs.com/package/castv2-client)と[mdns-js](https://www.npmjs.com/package/mdns-js)を使ってGoogle Homeに合成した音声を再生させる

の処理をする。

Dockerでも動くし、Raspberry Piでも動くようにした。


### 2. Slack - Google Home bridge

[slack-google-home-bridge](https://github.com/hjmsano/slack-google-home-bridge)を作った。

- 最小限のSlackbotをBotKitで構成
- チャンネルと通知設定の組み合わせを定義しておいて、チャンネルごとに設定通りの音声で読み上げるように 1のAPIを叩く


# セットアップ方法

## 必要なもの
- AWSアカウントとAmazon PollyがつかえるIAMのクレデンシャル（普通は無料の範囲内）
- SlackのAPIトークン（Slack APIでAppを作成、ボット機能を有効化して `Install App`メニューの`Bot User OAuth Access Token`を取得）
- 当然、Google Homeデバイスが1つ以上

## Google Home - Amazon Polly - REST API を動かす

1. [google-home-amazon-polly-bridge](https://github.com/hjmsano/google-home-amazon-polly-bridge)を入手（git cloneでもzipでもok）

### Dockerの場合

2. `docker build -t googlehome-polly-bridge .` でDockerイメージをビルドする
3. `docker run` する時に環境変数一式を渡す

```sh
docker run --net=host \
-e "AWS_ACCESS_KEY_ID=ABCDEFG" \
-e "AWS_SECRET_ACCESS_KEY=zxcvbnm1234567789asdfghjkl" \
-e "AWS_REGION=ap-northeast-1" \
-e "POLLY_LANG=ja-JP" \
-e "POLLY_VOICE=Takumi" \
-e "POLLY_TYPE=text" \
-e "POLLY_SAMPLE_RATE=22050" \
-e "HTTP_PORT=8080" \
-e "HTTP_HOST=192.168.0.2" \
-e "GOOGLE_HOME=Kitchen" \
--restart=on-failure
googlehome-polly-bridge
```

### 直接動かす場合（Raspberry Piとか）

2. `.env` ファイルか環境変数で必要情報を渡せるようにしておく
3. `npm install` して `npm start` する

### 共通）設定する環境変数

|変数名|サンプル|メモ|
|:----|:----|:----|
|AWS_ACCESS_KEY_ID|`ABCDEFG`|AWS 認証情報|
|AWS_SECRET_ACCESS_KEY|`zxcvbnm1234567789asdfghjkl`|AWS 認証情報|
|AWS_REGION|`ap-northeast-1`|AWS リージョン|
|POLLY_LANG|`en-GB`|Pollyの動かすときのデフォルトになる言語指定|
|POLLY_VOICE|`Brian`|Pollyのデフォルトにしたい声| 
|POLLY_TYPE|`text`|textかssml、これもPolyのデフォルトにしたいもの|
|POLLY_SAMPLE_RATE|`22050`|Pollyで生成するオーディオのサンプルレート|
|HTTP_PORT|`8080`|APIで開放するポート|
|HTTP_HOST|`192.168.0.2`|APIを動かすホストのIPアドレス（Google Homeから見たIPアドレス）|
|GOOGLE_HOME|`Kitchen`|デフォルトのGoogle Home端末の名前またはIPアドレス|

基本的に設定値はこのアプリケーションがデフォルトで使う値。リクエスト時にGETパラメータで指定することで上書きできる。

##  Slack - Google Home bridge を動かす

1. [slack-google-home-bridge](https://github.com/hjmsano/slack-google-home-bridge) を入手（やはりgit cloneでもzipでも可）
2. `device-settings.sample.json` を元に `device-settings.json` を用意する。 SlackのチャネルIDとPolly用の設定の組み合わせを記述する感じ

### Dockerの場合

3. `docker build -t slack-google-home-bridge .` でDockerイメージをビルドする
4. `docker run` する時に環境変数一式を渡す

```sh
docker run --net=host \
-e "SLACK_TOKEN=abcdefg1234567890xyz" \
-e "BRIDGE_URL=http://localhost:8080/" \
--restart=on-failure
slack-google-home-bridge
```

### 直接動かす場合（Raspberry Piとか）

3. `.env` ファイルか環境変数で必要情報を渡せるようにしておく
4. `npm install` して `npm start` する

### 共通）設定する環境変数

|変数名|サンプル|メモ|
|:----|:----|:----|
|SLACK_TOKEN|`abcdefg1234567890xyz`|Slackボット用の`Bot User OAuth Access Token`|
|BRIDGE_URL|`http://localhost:8080/`|Google Home - Amazon Polly - REST API のエンドポイントURL|


### 補足
- 同じ端末上で両方動かすのであれば、`BRIDGE_URL` はlocalhostでok
- `device-settings.json` の仕組み的に、1つのチャネルで1つのルールになる（ssmlとtextの使い分けとか、声を切り替えるとかはチャネル単位になる）
- メッセージの組み立て、読み上げ対象の取捨選択、等は`index.js`の中の `controller.hears()` メソッド内をいじればいくらでも可能


# CircleCIのビルド結果をGoogle Homeに通知させる

1. [Slack Appディレクトリ](https://my.slack.com/apps)へ行き、 `CircleCI`を検索しインストール、連携するチャネルを指定する
2. インストール後に表示される指示に従ってCircleCIでプロジェクトのSettingページから、Notifications→Chat Notificationsを開く
3. Slackの設定についてのボックスが表示されるので、Slackの設定後に表示されたWebhook URLをコピー＆ペーストして Saveする（念のためTest Hookを押して、指定のチャンネルメッセージが来ることを確認する）
4. Slack - Google Home bridge の `device-settings.json` にCircleCIの通知が来るチャンネルのIDと、読み上げ条件その他を好みに合わせて設定する（必要があれば`index.js`もいじってメッセージの読み上げる箇所や表現を良い感じにする）。そしてプロセスを再起動しておく
5. CircleCIで適当なプロジェクトのビルドを実行してみる（通知のテスト用に、`exit 1`や`exit 0`を実行するだけのワークフローを作っておくと確認できる）