GoogleHomeとSlackを連携させてみた

こんにちは、Advent Calendarの参加は初めてなので緊張しています。
@CharaDenMitz さんにお誘い頂き、書かせて頂きます。

ちなみに書く内容は今年の5月に私が所属している[ギークナビ](https://geeknavi.net/)という団体で開催したハッカソンで発表したプロダクトになります。
更にちなみに[【増席】IoT縛りの勉強会/SIer主催 SIerIoTLT vol9@サポーターズ](https://iotlt.connpass.com/event/89493/)でも登壇した内容になります。
どんだけこのネタ引っ張るんだっていうね！

# 作ったもの
## 概要
おうちにいる子どもが、職場にいるパパ（ママ）とGoogle HomeとSlackを通じて会話ができるIoTを作りました。
![1.png](https://qiita-image-store.s3.amazonaws.com/0/73844/7b2d09db-fdd3-cc61-8dd9-c2b8513bdfdf.png)

## 全体構成
使っているサービスを含めた全体構成図はこんな感じです。

最初はAWSも考えましたが、Google Homeを使うならGoogleのサービスにこだわろう！という気持ちでやりました。（Slackを除く）

GCPのサービスを使っているので今回は「iot-tours2018」というGCPのプロジェクトを作成して全てこのプロジェクトに紐づけています。
![2.png](https://qiita-image-store.s3.amazonaws.com/0/73844/a703dfb2-c526-b6bf-5b1e-e55e8abbaee4.png)

# GoogleHomeに話しかけた言葉をSlackにPOSTする
Google Homeに話しかけた言葉をSlackに投稿するには、Slack上のBotが必要です。

また、自然言語を解析するプラットフォームとしてはGoogleが買収したDialogflowが有名ですのでこれを使っていきます。

## Slackの設定
以下のURLからSlackのBotを作成します。

https://myteam.slack.com/apps/A0F7YS25R-bots

「myteam」の部分は参加しているSlackチームのホスト名に適宜変えてください。
Botを作成したらAPIトークンをメモし、任意のチャンネルにBotを招待しておきます。Slack側の設定は一旦これでOKです。
![3.png](https://qiita-image-store.s3.amazonaws.com/0/73844/03b5770b-48d4-d609-fb55-efd497cdc84f.png)

## Dialogflowの設定
### Intentsの設定
Dialogflowで新たなAgentを作成したら、まずは「Intents」を作成します。

「Intents」では、どういうフレーズを受け取った時にどう認識、反応すべきかを定義します。

個人で製作したGoogle Homeアプリを呼び出すには「OK Google、○○につないで」と話しかけます。さらにアプリに繋いだ後のGoogle Homeからの応答を「Default Welcome Intent」で以下のように定義します。

「Set this intent as end of conversation」のトグルをOFFにしておくと、会話が終了せずに入力待機の状態になります。
![4.png](https://qiita-image-store.s3.amazonaws.com/0/73844/4ec86c49-1ebc-50f6-3edf-0c871a67eb68.png)

続いてもう1つIntentsを追加します。

今回、受け取った言葉を丸々全てSlackにPOSTするため、以下のように例文の「こんにちは」を全て選択（ドラッグ）し、Entityに`@sys.any`をセットし、Valueを`$any`とします。

Responseにこの処理が終わったあとにGoogle Homeのレスポンス内容を設定します。ここで`$any`というパラメータを利用することが出来るので、下記の画像の例で言えば「スラックにこんにちはとポストしました」というレスポンスがなされます。
![5.png](https://qiita-image-store.s3.amazonaws.com/0/73844/62830f7b-9ced-cab7-7485-85072c866209.png)

### Fulfillmentの設定
「Fulfillment」は、受け取ったフレーズに対して実行する処理を定義します。

これは「Webhook」と「Inline Editor」の2種類があります。

- Webhook … 外部のWebhookを受けられるサービスに連携、処理は外部で行う。
- Inline Editor … FirebaseにCloud functionsがデプロイされ、そこで処理が実行される。

今回はCloud functionsを利用しましたが、Webhookを使って実行できたことも確認しています。
※AWS API Gatewayでエンドポイントを作成し、そこを経由してLambda上のスクリプトを実行させる形です。
![6.png](https://qiita-image-store.s3.amazonaws.com/0/73844/dc672beb-3ce1-4f13-63cf-96e21fd4494e.png)

index.js は[こちらのコード](https://gist.github.com/ikasamah/904e075f46e9418c6df569cfdc6755a2)を流用させていただきました。
`package.json`の内容は以下の通りです。

```json:package.json
{
  "name": "dialogflowFirebaseFulfillment",
  "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase",
  "version": "0.0.1",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "~6.0"
  },
  "scripts": {
    "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
    "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
  },
  "dependencies": {
    "slackbots": "^1.1.0", # 追加
    "actions-on-google": "^1.0.0", # 修正
    "firebase-admin": "^4.2.1",
    "firebase-functions": "^0.5.7",
    "dialogflow": "^0.1.0",
    "dialogflow-fulfillment": "0.3.0-beta.3"
  }
}
```

設定が完了したら「DEPLOY」を実行します。
DEPLOYを実行すると、紐づけたGCPプロジェクト内にCloud functionsが作成されます。

### Integrationの設定
Integrationでは連携するサービスを選びます。今回、入力元にはGoogle Homeを使うので「Google Assistant」を選択しました。
※他社サービスとアイコンの大きさの差が露骨～と感じますね(^○^)
![7.png](https://qiita-image-store.s3.amazonaws.com/0/73844/11e76dfd-1155-b6b9-8472-40a114aa1d37.png)

選択すると以下のようなポップアップが表示されるので、「MANAGEMENT ASSISTANT APP」を選択します。
するとActions on GoogleというGoogle Assistantアプリのテストや管理が行えるコンソールが表示されます。
![8.png](https://qiita-image-store.s3.amazonaws.com/0/73844/6398e706-2a47-c504-ee6a-7a34b98d8f76.png)

## テストする
Actions on Googleで、作成したGoogle Assistantアプリに関する情報の登録が必要なので適当に入力、設定します。
※画像を用意する必要があって意外と面倒でした。。

今回は「パパとおしゃべり」という名前のアプリケーションとして登録しました。

そして、左メニューのSimulatorで実際のやり取りをテストを行います。
![9.png](https://qiita-image-store.s3.amazonaws.com/0/73844/f98ea56d-d731-d26c-85c0-82bf7aeec1dd.png)

「パパとおしゃべりにつないで」と発言するとアプリに繋がり、Dialogflowで設定した「スラックに何とポストしますか」とGoogle Assistantが応答を返します。

ここで「明日の晩ご飯はお好み焼きが食べたいです」と入力すると、以下のようにGoogle Assistantが返事をしてくれます。
![10.png](https://qiita-image-store.s3.amazonaws.com/0/73844/77a7a5bb-38d3-d597-4d07-f7d5d9a472c4.png)

するとすぐにSlackに以下のようにPOSTされます。
![11.png](https://qiita-image-store.s3.amazonaws.com/0/73844/e52d6662-8355-9184-3d92-cb675d24f6be.png)
テストは大丈夫そうですね。実機でもテストしてみてSlackにPOSTされたらGoogle Home ➡ Slackは完了です！

# SlackにPOSTした内容をGoogle Homeに喋らせる
## Raspberry Pi3の設定
約1年ぶりにラズパイを取り出したのでまずは諸々のアップデートからｗ

ちなみにRaspbian Stretchにアップデートしました。

## google-home-notifierをインストールする
Google Homeを喋らせるには google-home-notifier というNode.js製のパッケージを使うのが簡単で主流っぽかったのでこちらを利用しました。

[google-home-notifier(Github)](https://github.com/noelportugal/google-home-notifier)

まずはラズパイにNode.jsをインストール。

```shell:
$ nvm install --lts
```
続いてgoogle-home-notifierをインストール。

```shell
$ npm install google-home-notifier
```

その後はGithubの記述に従って設定を進めてテストしてみます。
Google HomeのIPアドレスを確認するにはスマートフォンのGoogle Homeアプリを利用します。

```shell
$ node example.js
Endpoints:
    http://{GoogleHomeのIP}:8091/google-home-notifier
    https://xxxxx.ngrok.io/google-home-notifier
GET example:
curl -X GET https://xxxxx.ngrok.io/google-home-notifier?text=Hello+Google+Home  - to play given text
curl -X GET https://xxxxx.ngrok.io/google-home-notifier?text=http%3A%2F%2Fdomain%2Ffile.mp3 - to play from given url
POST example:
curl -X POST -d "text=Hello Google Home" https://xxxxx.ngrok.io/google-home-notifier - to play given text
curl -X POST -d "http://domain/file.mp3" https://xxxxx.ngrok.io/google-home-notifier - to play from given url
```

このような感じで出力されるので `http://{Google HomeのIP}:8091/google-home-notifier?text=HelloGoogleHome` にアクセスして、Google Homeが「Hello Google Home」と喋り出せばOKです。

ngrokのURLにアクセスすると外部ネットワークから実行できるので、このURLをメモしておきましょう。
ngrokのURLは実行するたびに変わってしまうので注意が必要です。（ngrokの有料プランでは固定させることが出来る様子）

## Slackの設定（Outgoing Webhook）
Slackの管理画面から「Outgoing Webhook」を追加してインストールします。
チャンネルとトリガーとなるワード、Hook先のURL（後述するGoogle Apps Script）を設定します。
また、Google Apps Scriptを実行する際にトークンが必要なのでメモしておきます。
![12.png](https://qiita-image-store.s3.amazonaws.com/0/73844/c06c74bf-9ec7-f73e-29de-e90575155a29.png)

## Google Apps Scriptの設定
SlackからのWebhookを受け取るGoogle Apps Scriptを設定します。

適当な名前でファイルを作成し、以下のコードで動かします。
token には先ほど取得したOutgoing Webhookのトークンを指定し、urlにはgoogle-home-notifierを起動した際に表示されるngrokのURLを指定します。

ちなみに、Outgoing Webhookをキックするためのトリガーワードである「ねぇGoogle」も読み上げてしまうため、

```javascript
var text = e.parameter.text.replace('ねぇGoogle', '');
```

として、トリガーワードをカットしています。

```javascript:trigger.gas
function doPost(e) {
    var token = "Your Token"; 

    if (token != e.parameter.token) { 
      return; 
    } 
    var text = e.parameter.text.replace('ねぇGoogle', '');
    text = 'パパからのメッセージ' + text; 
    return request(text); 
} 


function request(text) { 
    var url = 'https://********.ngrok.io/google-home-notifier'; 
    var urlFetchOption = { 
        'method' : 'post',     
        'contentType' : 'application/x-www-form-urlencoded', 
        'payload' : { 'text' : text}  
    }; 

    var response = UrlFetchApp.fetch(url, urlFetchOption); 
    return response; 
}
```

設定したら「公開」⇒「ウェブアプリケーションとして導入」を選択し、「アプリケーションにアクセスできるユーザー」を「全員（匿名ユーザーを含む）」に設定します。

これで外部から実行できるようになります。
![13.png](https://qiita-image-store.s3.amazonaws.com/0/73844/b13e1371-2e07-cff5-f544-5f8b86e9b724.png)

# 完成！
[デモ動画はこちらになります（Youtube）](https://www.youtube.com/watch?v=ISDSO_m9gPI)

個人的にIoTは好きなんですが、回路組んだりはんだ付けしたりするのは避けて通りたい派なのでそういうのを一切使わないIoTに今後もチャレンジしていきたいです。