Bottle と docomo 雑談対話 API で作った会話アプリを Pipenv と Heroku で公開

# はじめに
Python の Web フレームワーク Bottle と docomo の雑談対話 API を使って、シンプルな会話アプリを作りました。仮想環境の構築には Pipenv を使用し、作ったアプリは Heroku で公開しました。

作ったものはこちらです。
http://simple-conveasation.herokuapp.com/

# 会話アプリ作成

アプリ作成に先立ち、docomo の Developer として登録し、雑談対話 API の API key を取得して控えておきます。
https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=dialogue&p_name=api_reference

作業用ディレクトリ `simple-conversation`をつくり、その中でアプリを作成します。まずは画面表示用の HTML テンプレートです。

```index.tpl
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>Simble Conversation</title>
  </head>
  <body>
    <h2>Simple Conversation</h2>
    <p>Notes: This app only supports Japanese language.</p>
    <form action="/" method="post">
      <input type="text" id="reqMsg" name="reqMsg">
      <input type="submit" value="Send">
    </form>
    <p>{{ user_msg }}</p>
    <p>{{ res_msg }}</p>
  </body>
  <script type="text/javascript">
    document.getElementById('reqMsg').focus();
  </script>
</html>
```

純粋な HTML ファイルではなくあくまでテンプレートであることを強調するため、拡張子を `.tpl` としています。

入力フィールドと送信ボタンのみを持つ form 要素と、結果表示用の p 要素を配置してあります。 p 要素内の`{{}}`の部分にはテンプレートの機能で、 Python から受け取った変数が埋め込まれます。

スクリプト部分ではページを開いたときに入力フィールドにフォーカスが来るようにしています。この部分は別になくても構いません。

次に、Web アプリの本体を作成します。

```app.py
from bottle import get, post, template, request, run
import requests
import json
import os

API_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

@get('/')
def index():
    user_msg = ''
    res_msg = ''
    return template('index.tpl', user_msg=user_msg, res_msg=res_msg)

@post('/')
def send():
    req_msg = request.forms.reqMsg

    url = 'https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY=' + API_KEY
    payload = {'utt': req_msg}
    res = requests.post(url, data=json.dumps(payload))
    res_msg = '<< ' + res.json().get('utt')
	
    user_msg = '>> ' + req_msg
	
    return template('index.tpl', user_msg=user_msg, res_msg=res_msg)

if __name__ == '__main__':
    run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))
```

`API_KEY = ...` の部分には各々、取得した API key を指定します。

ここでは2つのリクエストハンドラ `index` と `send` を定義し、それぞれルートへの GET リクエストと POST リクエストに紐付けています。

`send` の中では requests ライブラリを使用し、雑談対話 API へのアクセスを行っています。 JSON でリクエストを渡し、 JSON で結果を受け取っています。リクエストには様々な key が設定できるようですが、最低限、発言内容である `utt` を渡せば動くようなので、 `utt` のみを設定しています。API からの応答メッセージは同様に `utt` という key で取得できます。

この時点で動作を確認してみます。

```
$ python app.py
Bottle v0.12.13 server starting up (using WSGIRefServer())...
Listening on http://0.0.0.0:5000/
Hit Ctrl-C to quit.
```

ブラウザから `localost:5000` へアクセスし、想定どおりに動くことを確認できれば、アプリは完成です。

# Heroku で公開

Heroku へのデプロイは Git の commit を push することで行います。

まずはローカルリポジトリを作成し、

```
$ git init
```

ここまでの変更を commit します。

```
$ git add app.py index.tpl.html
$ git commit -m "first commit"
```

依存ライブラリと起動コマンドを Heroku に伝えるためには、 `requirements.txt` と `Procfile` を作成することが必要です。このために、まずは Pipenv でローカルに仮想環境を作り、動作を確認してから Heroku にデプロイすることにします。

仮想環境の作成は次のようにします。

```
$ pipenv install
```

試しに次のコマンドで仮想環境内でアプリを起動しようとすると、必要なライブラリがないというエラーが出ることが確認できます。

```
$ pipenv run python app.py
Traceback (most recent call last):
  File "app.py", line 1, in <module>
    from bottle import get, post, template, request, run
ImportError: No module named 'bottle'
```

次のように仮想環境内に必要なライブラリを導入した後に、

```
$ pipenv install bottle
$ pipenv install requests
```

再びアプリを起動すると、

```
$ pipenv run python app.py
Bottle v0.12.13 server starting up (using WSGIRefServer())...
Listening on http://0.0.0.0:5000/
Hit Ctrl-C to quit.
```

正常にアプリが動作することが確認できるはずです。

この状態で `requirements.txt` と `Procfile` を作成します。

```
$ pipenv run pip freeze > requirements.txt
$ echo web: python app.py > Procfile
```

ここまでの変更を commit した後、

```
$ git commit -am "prepared for heroku deployment"
```

以下のようにして Heroku へデプロイします。

```
$ heroku apps:create <アプリの名称>
$ git push heroku master
```

`<アプリの名称>` の部分は各々わかりやすい名前を付けます。Heroku 全体で一意であることが要求されているようです。（筆者は `simple-conveasation` としました。これはタイプミスですが、結果的に一意な名称になっていました。）

コンソールログにアクセス用の URL が出ていると思います。そこにブラウザからアクセスして、正常な動作が確認できれば、アプリの公開が完了です。

# 参考

Heroku×bottleを使って4分33秒でWebアプリを公開する
https://qiita.com/ohbarye/items/55ec574f10685a012baf

pipenv を使ってみたのでメモ
https://qiita.com/subarunari/items/dcbdad68ba1809b721b9

Requests の使い方 (Python Library)
https://qiita.com/sqrtxx/items/49beaa3795925e7de666

雑談対話 | docomo Developer support | NTTドコモ
https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=dialogue&p_name=api_reference