GoogleHomeに話しかけて検索結果をブラウザに表示させる　❷FirebaseのFunctionsでボットに返事をさせる(Dialogflow V2 API)

GoogleHome(mini)を購入したので、聞き取ったキーワードから
PCのブラウザに**Googleの検索結果1位のサイトを表示**して、そのサイトの**タイトルを読み上げる**アプリを作成しました。検索したキーワードがDBに無かった場合はキーワードとURLとタイトルを登録します。
##**最終的に出来たアプリ**
![1_柳家も大好き💛.png](https://qiita-image-store.s3.amazonaws.com/0/237418/48c29f60-e824-ab23-a929-35b7f88ce4f5.png "ワタシが大好きなキムチ納豆ラーメンのお店　柳家さんは仙台東口にもあります")

##**ワタシがやったこと**
極力Googleさんのサービスだけで完結したいなーということで、DialogflowとFirebaseを使用
Firebaseがホスティングサーバーとデータベースと関数の管理と色々サービスを担ってくれているので
全部お任せしました。
![2_ワタシがやったことjpg.png](https://qiita-image-store.s3.amazonaws.com/0/237418/ad8611c7-628d-7d79-3d09-d8805a0472cb.png "本当Firebaseって素敵なサービスです💛")


:one:　**[Dialogflowを使用して簡単なチャットボットを作成](https://qiita.com/tedkuma/items/9ceb17936c714a205a74)　(前回の記事)**
:two:　**[Firebase](https://console.firebase.google.com/)のFunctions(cloud functions)**を使用して、:one:で作ったボットが適切な返事するように設定
:three: 　:two: を実行する際に**FirebaseのDatabase**にデータを追加するように設定
:four:  　:three: のデータ追加をトリガーに**FirebaseのHosting**で作成したページ(htmlファイル)にパラメータを渡し、
　　　windowopenで対象のページを開くように設定
:five:  　Google検索で一番上に表示されてるページを一発で表示させてみる(I'm feeling luckey!)
:six: 　データ登録時にページタイトルとサイトURLはオリジナルのものを保存する
　　　(webスクレイピングには**[cheerio-httpcli](https://www.npmjs.com/package/cheerio-httpcli)**を使用しました )
:seven:　 フリーのMP3データを**FirebaseのStorage**に入れてレスポンスに音声データを返す(SSML)

---
##:two: **[Firebase](https://firebase.google.com/?hl=ja)のFunctionsでチャットボットに返事をさせる**##
![3_stage2.png](https://qiita-image-store.s3.amazonaws.com/0/237418/52cf5893-81c6-087b-7ddc-d2ab5d46cefa.png)

Googleが提供している「Firebase」はアプリのバックエンド機能を提供するクラウドサービスです。
これを使えばワタシのようなバックエンド構築に慣れていない開発初心者でも思ったことが実現できます。

**[前回](https://qiita.com/tedkuma/items/9ceb17936c714a205a74)**で、GoogleHomeに話しかけたらそれっぽいことを言ってくれるチャットボットは作成できました。
でも、これではIntentsで登録した言葉しか返せません。ゆくゆくは難しいことを返してもらいますが
今回はとりあえずFirebaseのCloudFunctionsの関数で応答してもらうというのがゴールです。

最初に新しくディレクトリ(フォルダ)を作っておきます。
ワタシはデスクトップに**HOYA**という名前で用意しました。

では、Actions on googleの画面でBackend serviceタブを選び、
その中のCloud FunctionsにあるGET STARTEDをクリック
![4_Backend_service.png](https://qiita-image-store.s3.amazonaws.com/0/237418/00bd90f5-1aa0-2615-803c-feb5bac68063.png)

Functions画面が開くのでスタートガイドをクリック
![5Functionsの画面 .png](https://qiita-image-store.s3.amazonaws.com/0/237418/17642109-05bf-5f48-c82c-83dacc86837f.png)



FunctionsはNode.jsを使用します。だからコマンドでfirebase-toolsをインストールしてねと言ってます。
![6Functions の設定.png](https://qiita-image-store.s3.amazonaws.com/0/237418/95805dfd-81ce-553f-f48e-a6965b55f455.png)

**npm**：Node Package Managerの略　
NodeのPackage Manager に firebase で必要なツールをインストールするのね :slight_smile:
という訳で先ほど作った**HOYA**の中で実行します。

```Bash
npm install -g firebase-tools
```

<details><summary>**HOYAの中で実行するというのがピンとこない方はこちらを読んでください**</summary>
![cmd.png](https://qiita-image-store.s3.amazonaws.com/0/237418/065f16b6-4aeb-f6cd-647b-d0821f0a3182.png)
コマンドプロンプトで先ほど作成したディレクトリに移動して(cdコマンド)、npm installを実行して下さい。

</details>はい。色々インストールされました。
では、ブラウザに戻って先ほどのFunctionsの設定の画面の次へをクリックします。

この画面は、コマンドを教えてくれている画面なので終了ボタンを押します。
　　　　- プロジェクトを開始(初期化)する時のコマンドはfirebase initと入力するよ。
　　　　- 関数をアップロードして使えるようにする時のコマンドはfirebase deployと入力してね。という内容
![8_デプロイの仕方.png](https://qiita-image-store.s3.amazonaws.com/0/237418/e9b60281-06df-4947-d3b1-65ee556205f3.png)

すると最初のデプロイを待機していますと表示されます。さあ、いつでもOKよ:ok_woman_tone1:ってことですね。
![9_最初のデプロイを待機しています.png](https://qiita-image-store.s3.amazonaws.com/0/237418/e4cfff8d-edd8-6d8f-1af2-6646b976197a.png)

さて、ではプロジェクトを始める前にFirebaseにログインします。

```Bash
firebase login
```
? Allow Firebase to collect anonymous CLI usage and error reporting information? (Y/n)
Firebaseが匿名で使用法とエラー報告情報を収集できるようにしますか?と聞いてます
Yでもnでもお好みで入力して下さい。そしてEnterすると‼‼

ブラウザがパーンと切り替わりました。アカウントを選んで許可ボタンを押します。
![10_firebaselogin.png](https://qiita-image-store.s3.amazonaws.com/0/237418/a9ae1a47-191a-7aff-5ab5-ce37d62b115c.png)

ログインに成功するとこの画面が表示されます。
٩(ˊᗜˋ*)وヤッタネー！これですぐに始められるよ~と言ってます。
![11_ログインできたー！.png](https://qiita-image-store.s3.amazonaws.com/0/237418/643afb14-9660-d5d8-d259-ce655ec1895e.png "WooHooって・・・欧米の方はリアクションが大きいですもんね")




###**では、いよいよプロジェクトを始めます:grinning:**###

```Bash
firebase init
```
準備はいいですか？にはY
どの機能を設定しますか？にはFunctionsを選択します。
![12_firebase_init.png](https://qiita-image-store.s3.amazonaws.com/0/237418/82cb4c77-a5a2-6e32-e433-d6e769ba1f1b.png)

このディレクトリをFirebaseプロジェクトに関連づけましょうと言われてるので
Dialogflowで作成したFirstAPPを選びます。
![13_firebase_init.png](https://qiita-image-store.s3.amazonaws.com/0/237418/18c508c4-70bd-a53a-6225-df495933e7ce.png)


何の言語を使用したいですか？　→　 JavaScript
ESLintを使用してバグを見つけ、スタイルを強制しますか?　　→　No
npmで依存関係をインストールしますか？　　→　Yes　　と答えています。
![14_firebase_init.png](https://qiita-image-store.s3.amazonaws.com/0/237418/5bfe6b34-5991-7e64-d28f-fd72286856b0.png)

また色々とインストールされて・・・+  Firebase initialization complete!が出れば初期化完了です。
![15_firebase_init.png](https://qiita-image-store.s3.amazonaws.com/0/237418/d661d33e-1f3d-a162-e415-9f69239c1c06.png)

そうするとHOYAの中にはこの３つが出来ています。
![16_HOYAの中.png](https://qiita-image-store.s3.amazonaws.com/0/237418/cc0c56ae-765a-1530-8bf7-69b19dd91c55.png)

functionsの中はこのようになっていて、この中にあるindex.jsにコードを書いていきます。
![17_functionsの中.png](https://qiita-image-store.s3.amazonaws.com/0/237418/d847dfe6-ea3d-2e90-8b2e-a28f9dcd10cb.png)



とりあえずindex.jsに書いてあるコードのまま、fuctionsが使えるか試してみます。
requireはjavaとかでいうところのimport文みたいな感じです。
helloWorldというのが関数名、"Hello from Firebase!"というリクエストを送信してねという関数です。

~~~javascript:index.js
const functions = require('firebase-functions');

// Create and Deploy Your First Cloud Functions
// https://firebase.google.com/docs/functions/write-firebase-functions

 exports.helloWorld = functions.https.onRequest((request, response) => {
  response.send("Hello from Firebase!");
 });
~~~

このままdeployします。

```Bash
firebase deploy
```
Function URL(helloWorld)の後に書いてあるURLをブラウザに表示するとリクエストが確認できます。
　　**`https://us-central1-ここにプロジェクトIDが入る.cloudfunctions.net/ここは関数名が入る`**
![18_Function_URL出ました.png](https://qiita-image-store.s3.amazonaws.com/0/237418/220bcf3f-7571-576b-c102-3e9548e3163f.png)

実際に確認してみます。出ました~！:v_tone2:
![19_ Hello_from_Firebase!出ました.png](https://qiita-image-store.s3.amazonaws.com/0/237418/00b3f069-ee8b-7fbf-1c70-bd92d550cbb1.png)


という訳で、コードを書き換えます。
前回作ったIntents 朝の挨拶に返事をさせます。(おはようって話しかけるやつ）
　-関数名はhoyatalkに変更しました。
　-request.body.queryResult.parameters.の後ろにパラメータ名を指定することで
　 Dialogflowから送られてきたパラメータが受け取れます。
　-fulfillmentTextに返事してほしい言葉を入力します。

~~~javascript:index.js
const functions = require("firebase-functions");//クラウド関数を作成してトリガを設定するためのFirebase SDKのクラウド関数。

exports.hoyatalk = functions.https.onRequest((request, response) => {
    const dialogflow_param = request.body.queryResult.parameters.Greeting_in_the_morning;  //Dialogflowから来てるパラメータを受けとる 

//ログの3行は実行時に不要なので消して下さいね。(解説のために入れてるだけです)
    console.log(request.body);  
    console.log(request.body.queryResult.parameters.Greeting_in_the_morning);
    console.log(request.body.queryResult.queryText);

    response.setHeader("Content-Type", "application/json"); //Dialogflowに情報を返す
    response.send(
        JSON.stringify({
            "fulfillmentText": "おはようございます"
        })
    );
});
~~~

このままdeployします。関数名を変えるとFunction URLも変わります。
※このFunction URLは今からDialogflowで連携させるのに使います。
![20_hoyatalkURL.png](https://qiita-image-store.s3.amazonaws.com/0/237418/639c40c0-3053-9209-35c1-d3468ba8b76d.png)


<details><summary>**dialogflow V1 APIとV2 APIの違い**</summary><div style="background-color:pink;">
余談ですが:slight_smile:ワタシはこのアプリが完成して、Qiitaに書いてみよう！ってこの記事を始めました。
でも、色々試行錯誤して完成したのでどの手順が無駄だったのか不明
<br>というわけで、もう１つgoogleさんのアカウント作って　一からアプリを作成しながら書いてます。
前のを作ったときは V2 がベータ版とか書いてあったのでデフォルトのV1 APIで作ったのですが
今後はV2 APIがデフォルトになるみたいで、まぁいいかとV2でそのまま作成していたら
ここのコード書くところで違いがハッキリしてまして、見事に躓きました。

<変更点１> parameters が result → queryResult の中に変更
<変更点２> response の speech → fulfillmentText に変更

~~~javascript:(V1_APIで作成した当時の）index.js
const functions = require("firebase-functions");//クラウド関数を作成してトリガを設定するためのFirebase SDKのクラウド関数。

exports.hoyatalk = functions.https.onRequest((request, response) => {
    const dialogflow_param = request.body.result.parameters.Greeting_in_the_morning;  //Dialogflowから来てるパラメータを受けとる

    response.setHeader("Content-Type", "application/json");  //Dialogflowに情報を返す
    response.send(
        JSON.stringify({
            "speech": "おはようございます"
        })
    );
});
~~~


~~~javascript:(V2_APIで作成する場合の）index.js
const functions = require("firebase-functions");//クラウド関数を作成してトリガを設定するためのFirebase SDKのクラウド関数。

exports.hoyatalk = functions.https.onRequest((request, response) => {
    const dialogflow_param = request.body.queryResult.parameters.Greeting_in_the_morning;  //Dialogflowから来てるパラメータを受けとる 

    response.setHeader("Content-Type", "application/json"); //Dialogflowに情報を返す
    response.send(
        JSON.stringify({
            "fulfillmentText": "おはようございます"
        })
    );
});
~~~

他にも変更になっているフィールドが沢山あるので**[V1とV2の比較ページ](https://dialogflow.com/docs/reference/v2-comparison)**で確認して下さい。</div></details>

###**Dialogflowへ戻って仕上げていきます！**###
左側のFullfillmentを選んでWebhookを　　DISABLED　→　ENABLEDにします。
![21_Fullfillment画面.png](https://qiita-image-store.s3.amazonaws.com/0/237418/06233539-9f15-aff1-f899-fdc226b53d92.png)

URLのところにFunction URLを貼り、下にあるボタンでSAVE
![22_webhookに貼り付けます.png](https://qiita-image-store.s3.amazonaws.com/0/237418/010eee1d-890f-9ad6-7a92-6d5952f0c755.png)

では、Intents(朝の挨拶)を手直しします。Training phrasesの「おはよう」を選択
すると@sys.colorとか@sys.emailとか色々出てきました。この中から自分で登録したEntitiesの
**`@Greeting_in_the_morning`**を選択します。すると「おはよう」の下とAction and parametersの欄に

　　　PARAMETER NAME 　　　　　　　　　ENTITY　　　　　　　　　　　　　VALUE
　　Greeting_in_the_morning　　　`@Greeting_in_the_morning`　　$Greeting_in_the_morning

　が表示されます。これでGreeting_in_the_morningというパラメータを送る設定が出来ました。
![23_パラメータを設定.png](https://qiita-image-store.s3.amazonaws.com/0/237418/b843311b-5c1c-52e5-43c1-3328dcf56025.png)


<details><summary>@sys.colorとか@sys.emailとかいっぱい出てきてるのは何？</summary>
これはデフォルトで登録されているEntitiesです。
デフォルトで「色だよ」とか「メアドだよ」という定義はしてくれているのです。
だから、日付とか時間とか汎用なものはいちいちEntitiesに登録しなくてもよくて、
このTraining phrasesでこの言葉は日付だよとか指定すればいいんです。
※ワタシのアプリでは使ってないのでこの辺の説明は飛ばします。</details>

続いて、Responsesに入れてあったText responseの「おはようございます」を消します。
Fulfillmentの右側にある **∨** を押して、表示された Enable webhook call for this intentをオンにしてSAVE
これでこのIntentsが呼ばれたときにFunction URLを貼ったwebhookを呼びますよの設定が出来ました。
![24_Webhookを適用.png](https://qiita-image-store.s3.amazonaws.com/0/237418/c0d6e832-b8c9-e671-efee-1397753ccdbe.png)

###**さてー！シミュレータでテストしてみます！おー！出ました:v_tone2:**###
![25_出来ました.png](https://qiita-image-store.s3.amazonaws.com/0/237418/9ed6c057-240c-f328-64d5-90f922376d62.png "昨日の夜アップデートしたので、前回の記事とシミュレータ画面が変わってますがお気になさらずに・・・")

では、どうなってるか　せっかくログ出したので確認します。
Firebaseに戻りFunctionsタブでログを選択します。おー出てますね。
![26_ログを確認.png](https://qiita-image-store.s3.amazonaws.com/0/237418/9e0c82d8-8024-7473-3883-0d2e6aeda68d.png "Firebaseの画面もアップデートしたせいで記事冒頭のキャプチャと若干違いますが気にしないでください")


最初に出した赤枠のところはrequest.bodyの中身です。これを見れば中の構造が分かりますよね。
だから２番目のログではqueryResult.parameters.Greeting_in_the_morningで　おはよう　が
　　　３番目のログではqueryResult.queryTextで　おはよー　が取れています。
![27_ログの中身.png](https://qiita-image-store.s3.amazonaws.com/0/237418/71e496a6-5a27-7423-9e7a-df20283eb382.png)

今回も長々と書きましたが、最後まで読んで下さりありがとうございました。

---
　　　　　　　
　　　　　　　　**次の記事：[❸FirebaseのFunctions実行時 Databaseにデータを追加する](https://qiita.com/tedkuma/items/38326f509e6a9f595eac)**
　　　　　　　　**前の記事：[❶Dialogflowで簡単なチャットボットを作成](https://qiita.com/tedkuma/items/9ceb17936c714a205a74)**