GoogleHomeをDialogflow(旧:API.ai)でスプラトゥーンアシスタントにする

#はじめに

Google Homeを買ったはいいものの、テレビ無し、外出ない、家電使わない自分には少々物足りないので
生活の大半を占めるスプラトゥーンのアシスタントをGoogle Homeには勤めてもらいます。

#目標
<img src="https://qiita-image-store.s3.amazonaws.com/0/54085/d0da5f8c-c3b8-230b-0d37-ed60101b112f.jpeg" width=80%>

- スプラトゥーン用お供AI

#機能
実装する機能は2つです。

- ブキ毎の勝敗の登録
- ブキ毎の勝率の呼び出し

#使用ツール・環境

- Google Home
- [IFTTT](https://ifttt.com/)
- [Dialogflow](https://dialogflow.com)
- python3.5.2

webhookの置き場に[heroku](https://heroku.com)使用しました。

#1.勝敗の登録
まず、お手軽に[IFTTT](https://ifttt.com/)を使ってGoogleスプレッドシートへ勝敗を入力します。

GoogleAssistant → spreadsheetのAppletを作ります。
<img src="https://qiita-image-store.s3.amazonaws.com/0/54085/fb76fd54-bd0d-13fe-0601-63ef7b9cdc57.png" width=80%>

IFTTTの設定は[先人の方](https://qiita.com/kazunori279/items/fdadbfde58ed133bb9d5)参考に
同様に負けた時用のApletも作ります。

これで
「**勝利 スプラシューター**」「**負けた キャンピングシェルター**」等と話せば自動的にGoogleDriveのスプレッドシートに勝敗が記入されるようになりました。

<img width="471" alt="20171012123447.png" src="https://qiita-image-store.s3.amazonaws.com/0/54085/cdf52193-9626-dc52-cdf3-229bb874795d.png">
スプレッドシートに登録されると「**イカ登録完了**」とGoogle Homeが喋ります。
#2.勝率の呼び出し
次に、Googleスプレッドシートで計算した武器毎の勝率を呼び出してもらいます。

目標とする実装↓
>僕「**勝率 〇〇(武器名)**」

>GoogleHome「**〇〇の勝率は××パーセントです**」


この機能はGoogleAssistant+IFTTTでは賄えないので、
スプラトゥーンアシスタントAIを[Dialogflow](https://dialogflow.com)(旧:API.ai)で作ります。

これを使って入力を解析(と言ってもブキ名を取得するだけ)し、webhookで外部へ流します。
そこでスプレッドシートの内容を取得し、JSONレスポンスでAI(Bot)へ戻せばBotが受け取った情報を元に喋ってくれるという仕組みです。
こんな流れです。
<img width="512" alt="20171012130549.png" src="https://qiita-image-store.s3.amazonaws.com/0/54085/5f41c4f1-119f-b396-08e1-b59d763a6b82.png">
##Dialogflowのセッティング
初期設定は[こちら](http://tokibito.hatenablog.com/entry/2017/06/06/213548)参照

###Intents
`Intents`で入力の受け取りや失敗した際の返答を登録します。
今回はユーザーの発言が「勝率」「成績」だった場合にBotが反応し、
勝率の後に続く部分を`weapon_name`として入力で受け取ります。
<img width="512" alt="20171012131826.png" src="https://qiita-image-store.s3.amazonaws.com/0/54085/8accfb78-53c9-25c9-c681-2942f72ce62f.png">

###Entities
入力として受け取る単語は`Entities`から登録します。
都市名や日時はデフォルトでシステム側が用意してくれていますが、
残念ながらスプラトゥーンのブキデータベースは無いそうです。
<img width="512" alt="20171012132128.png" src="https://qiita-image-store.s3.amazonaws.com/0/54085/c502e698-8540-b79c-f631-d9461d76020e.png">

###Fulfillment
`Fulfillment`でwebhookの送信先を設定します。
今回はherokuを利用しているのでそのURLを入力。
<img width="512" alt="20171012132542.png" src="https://qiita-image-store.s3.amazonaws.com/0/54085/bef0eca6-7e72-0fc7-af6f-0fce9cd58217.png">

`Intents`の `Use webhook`に忘れずチェックを入れておきましょう。
ついでにDefaultの返答には検索が失敗した際の文章を入れておきます。
<img width="512" alt="20171012133200.png" src="https://qiita-image-store.s3.amazonaws.com/0/54085/99e8179a-1ab8-b43c-656c-c6b2deb93c19.png">

##Webhookを作る
いよいよAIから受け取った`weapon_name`を元に勝率を返すwebhookを作れば終了です。

[こちら](http://ai-kenkyujo.com/2017/05/29/webhook/)の中で使われているwebhookを参考に、

https://github.com/dialogflow/fulfillment-webhook-weather-python

以下がgspreadを使ってスプレッドシートの情報を取得しJSONで返すサーバーです。

```python:webhook-dialogflow.py
from __future__ import print_function
from future.standard_library import install_aliases
install_aliases()

from urllib.parse import urlparse, urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError

import json
import os

from flask import Flask
from flask import request
from flask import make_response, jsonify
import gspread
from oauth2client.service_account import ServiceAccountCredentials


# Flask app should start in global layout
app = Flask(__name__)


@app.route('/webhook', methods=['POST'])
def webhook():
    req = request.get_json(silent=True, force=True)
    result = req.get("result")
    parameters = result.get("parameters")
    weapon_name = parameters.get("weapon_name")

    scope = ['https://spreadsheets.google.com/feeds']
    
    #ダウンロードしたjsonファイルを同じフォルダに格納して指定する
    credentials = ServiceAccountCredentials.from_json_keyfile_name('xxxxxxxxxx.json', scope)
    gc = gspread.authorize(credentials)
    
    # # 共有設定したスプレッドシートの名前を指定する
    worksheet = gc.open("Google Assistant Commands").get_worksheet(1)

    cell = worksheet.find(weapon_name)

    text = str(cell.value) + str(worksheet.cell(cell.row,cell.col+1).value) + "パーセント"
    r = make_response(jsonify({'speech':text,'displayText':text}))
    r.headers['Content-Type'] = 'application/json'
    return r
    
if __name__ == '__main__':
    port = int(os.getenv('PORT', 5000))

    print("Starting app on port %d" % port)

    app.run(debug=False, port=port, host='0.0.0.0')
```

gspreadの使い方は[こちら](http://temcee.hatenablog.com/entry/gspread_python)から

#テストする
Dialogdlowのコンソール右側に入力すればテキストでテストが可能です。
<img width="512" alt="20171012135115.png" src="https://qiita-image-store.s3.amazonaws.com/0/54085/9a5ebda2-e364-28b3-3a7e-6c572454cadf.png">

「勝率 スプラシューター」と入力すると…
<img width="283" alt="20171012134832.png" src="https://qiita-image-store.s3.amazonaws.com/0/54085/b7f7ca88-2a0d-7f14-fcf7-12679484a39a.png">

「スプラシューター50パーセント」と勝率が返って来ます。

ちなみにJSONはこんな感じになります。
<img width="401" alt="20171012135239.png" src="https://qiita-image-store.s3.amazonaws.com/0/54085/ec70d6a2-46b8-1a50-55db-c885cce0fde6.png">


#完成

動作はこちらの通りです。
[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/74VPbzj0ZNQ/0.jpg)](http://www.youtube.com/watch?v=74VPbzj0ZNQ)
とりあえず自分がほしい機能は実装できました！

#おわりに
現在、GoogleAssistantからDialogflowで作ったアプリを呼び出さなければいけないので、登録と呼び出しが別動作(Google Homeからテスト用アプリを起動している形)なのですが、
次はこれをDialogflowで一括にまとめようかなと思ってます。
後はステージ情報の取得とかでしょうか。しばらくはGoogle Homeで楽しめそうです。

そして良きスプラトゥーンライフを！

#参考
- [Google HomeにつぶやくとTwitterでつぶやくやつ、IFTTTですぐできた。 - Qiita](https://qiita.com/kazunori279/items/fdadbfde58ed133bb9d5)
- [Actions on Googleでapi.aiを使ってGoogle Homeに何か言わせてみる-Qiita](https://qiita.com/syarihu/items/53ea1a65f481f8121109)
- [API.AIを試す - 偏った言語信者の垂れ流し](http://tokibito.hatenablog.com/entry/2017/06/06/213548)
- [無料でチャットボットを作れる「api.ai」のWebhook機能を使ってみた | AI入門ブログ](http://ai-kenkyujo.com/2017/05/29/webhook/)
- [Pythonを使ってGoogleスプレッドシートを自動で読み書きする - Wicの中から](http://temcee.hatenablog.com/entry/gspread_python)