#7 Google Home（Action on Google）とDialogflowを連携する

####Google Homeと他のサービスを連携するには「Action on Google」を利用する必要がある。
今回はAction on GoogleとGoogleのAIであるDialogflowをつないでみる。


##Action on Googleでの作業
まず、Action on Googleにログインして、新しいプロジェクトを立ち上げる。
今回は近所の病院を教えてくれる「近所の病院」というプロジェクトにする。
![スクリーンショット 2017-12-16 22.17.17.png](https://qiita-image-store.s3.amazonaws.com/0/210201/e924c116-ec9b-80a1-b998-4b58866ee745.png)

プロジェクトの画面のOverviewに沿って、それぞれ進める。
以下は既に設定済みなので若干表示は違う。
![スクリーンショット 2017-12-16 22.18.55.png](https://qiita-image-store.s3.amazonaws.com/0/210201/e1f95336-e33d-ec0a-2653-d0c68cea5067.png)

##Dialogflowでの作業
「Action on Google」からの会話を受け付ける 「Dialogflow」を作る。

注意：言語は「日本語」にしておく。英語と混在していると後々うまく動かなかった。。
※おそらくGoogle側（特にAction on Google)のバグ？と思われる

会話の意図(Intent)を設定する。「CREATE INTENT」をクリック。
![スクリーンショット 2017-12-16 22.21.22.png](https://qiita-image-store.s3.amazonaws.com/0/210201/e8eed40f-ea72-f770-6d19-35d93e1ac4ef.png)


こんな感じ。User Saysは、複数登録しているが「病院の場所を送って」と解釈するためのもの。
後々「三田周辺」の部分を条件分岐で利用する。
なので「三田周辺の病院の場所を送って」というUser Saysだけ登録してあればOK。
![スクリーンショット 2017-12-16 22.23.14.png](https://qiita-image-store.s3.amazonaws.com/0/210201/5e31b541-cc19-da4f-be47-4bd4f6275c7e.png)

次にEntities。
「人間」「人」「human」のように、辞書としても利用できる。
今回は辞書ではなく、配列を管理するマスタテーブルとして使う。
会話に「三田周辺」というワードがあると、そのEntitiesとして、病院名を返すようなイメージ。
![スクリーンショット 2017-12-16 22.28.27.png](https://qiita-image-store.s3.amazonaws.com/0/210201/b7ab7ce9-2e0f-6628-ced0-b672df0a5306.png)

もう一つ、簡単なIntentとして「病院を知りたい」も登録しておく。
「病院を知りたい」と話しかければ「近所の病院をお伝えします」と回答するだけのもの。
![スクリーンショット 2017-12-16 23.19.25.png](https://qiita-image-store.s3.amazonaws.com/0/210201/6f8881aa-b3c5-a8ce-9dab-9ae47c16a362.png)


次は「Integration」で設定内容を反映する。
「Action on Google」はトグルが「On」になっている。
![スクリーンショット 2017-12-16 22.30.24.png](https://qiita-image-store.s3.amazonaws.com/0/210201/90b1c747-c157-6c02-da82-ff7dacc687b1.png)


こんな感じで設定し、「UPDATE DRAFT」
![スクリーンショット 2017-12-16 22.31.33.png](https://qiita-image-store.s3.amazonaws.com/0/210201/8103e8a2-f13c-ff52-5a30-a3a09972361a.png)

 SUCCESS!的な文章が出てくる。そのまま「Action on Googleのコンソール」にいく。

##Action on Googleでの各種登録
ここから再び「Action on Google」での設定。
ガイドに沿って「App Information」「Location Targeting」「Surface Capabilities」などを登録していく。
注意：言語は「日本語」にしておく。英語と混在していると後々うまく動かなかった。。Googleのバグ？


「App Information」はこんな感じ
![aa.png](https://qiita-image-store.s3.amazonaws.com/0/210201/b59186f1-a058-876a-a561-90e133aa67a9.png)


さて、「Simulator」で確認して見るとわかる通り、ここまでやれば簡単な会話はできる。
例は、Intent「病院を知りたい」の方。これくらいなら独自にサーバーサイドを用意する必要はない。
今回はIntentに「三田周辺」というワードが入っていると、三田周辺の病院を返したいので、サーバーが必要。
もっと柔軟に使いたいので、Webhookを利用し、サーバーサイドで処理をかく必要がある。
サーバーはNode.jsを利用し、Firebaseにホスティングする。

![スクリーンショット 2017-12-16 22.42.50.png](https://qiita-image-store.s3.amazonaws.com/0/210201/123be5a7-2c2f-704b-cb97-4236acc2c5a8.png)

##Firebaseでの作業（目的に合わせて、柔軟に作るために)
Firebaseにアクセスするには、「Action on Google」の「Cloud Function」から進む。
<img width="968" alt="aaaaa.png" src="https://qiita-image-store.s3.amazonaws.com/0/210201/6ab9df99-689b-209e-ad3e-974aa2bb9fa7.png">

ここからしばらくFirebaseの実行環境を利用して構築していく。
またサンプルソースをGithubからクローンする。クローンしたら、Firebase Initをする。
この辺りは以下の投稿を参考にした。
https://qiita.com/doki_k/items/11f8a23d71dce59409da

```
sudo git clone https://github.com/actions-on-google/dialogflow-webhook-template-nodejs.git
```


その後に条件分岐させるロジックを書く。ここも上記サイトがとても参考になる。

```index.js

'use strict';

process.env.DEBUG = 'actions-on-google:*';
const { DialogflowApp } = require('actions-on-google');
const functions = require('firebase-functions');

exports.yourAction = functions.https.onRequest((request, response) => {
  const app = new DialogflowApp({request, response});
  console.log('Request headers: ' + JSON.stringify(request.headers));
  console.log('Request body: ' + JSON.stringify(request.body));

  // Fulfill action business logic
  function responseHandler (app) {
    // Complete your fulfillment logic and send a response
    var speechStr = "";
    const uidStr = JSON.stringify(request.body.result.parameters.hospitalList);
    var uid = uidStr.replace(/\"/g, "");
if (uid === "三田周辺") {
      speechStr = "周辺の病院は、三田病院、済生会病院 です。";
    } else if (uid === "YYYYYY") {
      speechStr = "xxxxxです。";
    } else if (uid === "XXXXXX") {
      speechStr = "ZZZZZZです。";
    } else {
      speechStr = "該当するものはありません。";
    }
    app.tell(speechStr);    
  }

  const actionMap = new Map();
//  actionMap.set('input.welcome', responseHandler);
  actionMap.set('action.hospitalList', responseHandler);
  app.handleRequest(actionMap);
});

```
actionMap.set の引数は、Dialogflowのaction名（今回action.hospitalList）にする。

処理が書けたのでデプロイする。
```
firebase deploy --only functions
```
うまくいけばエンドポイントが取得できる。


##再び、Dialogflowへアクセス。
Webhookの設定は「Fulfillment」でする。
取得したエンドポイントをWebhookのURLに設定する。
![bbbb.png](https://qiita-image-store.s3.amazonaws.com/0/210201/83590bec-4a58-538e-486d-e3ac2f634f6d.png)

次に、IntentもWebhookを利用するようにする。
![スクリーンショット 2017-12-16 23.01.36.png](https://qiita-image-store.s3.amazonaws.com/0/210201/72ede318-4f8e-70ac-d6dd-b6e607122c89.png)

再び「Integration」を開いて、「UPDATE DRAFT」。

これでAction on GoogleとDialogFlowは連携できた。

##Action on Googleで確認
再びSimulatorで確認。
さっき書いた処理の通り、「三田周辺」という「User Says」のワードが条件となって、
三田周辺の病院のEntitiesの値を返しているので、FirebaseとDialogflowのWebhook周りがうまく動いていることがわかる。

![スクリーンショット 2017-12-16 23.04.01.png](https://qiita-image-store.s3.amazonaws.com/0/210201/1cb16e5a-71b1-7140-a4bb-cba03e1b15ed.png)


今回やってみて思ったことは、
Dialogflowを利用しつつ、柔軟に作りたい部分は適宜サーバーサイド側で作り込めば、
結構いい感じに色々できる。またしばらく楽しくなりそうだ。