オウム返しLINEBOTでpostgresを利用してみる。

#はじめに
とりあえず、2回、heroku postgresの設定をしたが、
あっちみたりこっちみたりですんなり進まなかったので、手順をまとめてみる。

#前提
* Windows
* Python
* Heroku
* オウム返しBOTの作成に成功している。
    * この記事では[line-bot-sdk-python](https://github.com/line/line-bot-sdk-python)のREADME.rst内のソースをもとにしている。
* postgresを普通に使ったことがある。

正しくオウム返しされるBOTを変更する想定で書いていきます。
* 変更前にオウム返しができていること
* バックアップを取っていること（任意）
を確認してください。

#heroku postgresの利用設定
herokuで使用するアプリを選択し、Resource＞Add-onsでHeroku Postgresを検索。

![pgsample1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401158/052aa077-5367-d0f5-912f-9a8f7d3ecc00.jpeg)

出てきたHeroku Postgresを押下。

![pgsample2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401158/e0a9a982-2b18-6c23-2ee4-319f037a7ebf.jpeg)

「Hobby Dev - Free」のまま、Provisionを押下。

![pgsample3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401158/02a0954c-13e5-0c75-55a3-3731d600c147.jpeg)

Heroku Postgres押下。
すぐにデフォルトのDBの生成が行われる。

![pgsample4](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401158/903b702a-488c-2ba3-65a3-cc5545e3a460.jpeg)

Settings＞View Credentialsを押下。

![pgsample5](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401158/0b46bb4b-fc1f-abc0-dacd-8e2970413f99.jpeg)

画像は意図的に右側カットしてます。
この情報を後ほど使うので、ブラウザを開いたまま取っておく。

#OpenSSLの設定
たぶん必要。たぶん。
というか、LINEBOTをherokuで実装していたら、ここは終わってるはず。
この手順飛ばして、ダメだったら戻ってきてもいいかも。

インストール方法は外部の記事にお任せ。

[WindowsにOpenSSLをインストールして証明書を取り扱う（基本編）](https://www.atmarkit.co.jp/ait/articles/1601/29/news043.html)

#Pythonとpostgresの連携
##ローカルにpostgresのDBアクセスドライバをインストールする。

```console:コマンドプロンプト
pip install psycopg2
```
※追記
ローカルにpostgresは別途インストールしなきゃだめ。
~~というか、今回ローカルで動作検証することないので、やらなくてもいいかも・・・。~~
やっぱり必要。pg:psqlはローカルにpostgresをインストールすることで使えるようになるコマンドらしい。
[heroku pg:psqlでsh: psql: command not foundと言われたけれど、ローカルにPostgreSQLを入れたら直りました。](https://qiita.com/pugiemonn/items/af2bbd93549db67af04e)
（heroku上で動かすためにインストールさせる方法は後述）

##コマンドプロンプトからheroku postgresにアクセス可能か確認

```console:コマンドプロンプト
heroku pg:psql -a ＜アプリケーション名＞
```
＜アプリケーション名＞はherokuのアプリケーション名

##psycopg2をherokuで使えるようにする(DBドライバのインストール)
Pythonからpostgresにアクセスするドライバ

requirements.txtに追加。

```text:requirements.txt
psycopg2==2.8.1
```

これをgitでpushすればインストールが行われてPythonからheroku postgresにアクセスできるようになる。
後でソースとまとめてプッシュするため、git操作は割愛。

#プログラム修正
[line-bot-sdk-python](https://github.com/line/line-bot-sdk-python)のREADME.rst内のソースをmain.pyとして配置している前提で説明していきます。

##psycopg2のインポート
インポート文の追加

```python:main.py
import psycopg2
```

##DBコネクションを取得する関数の定義
記述場所は、
import文とか環境変数の取得の後で、
@app.route("/callback", methods=['POST'])の前あたりでよいかと。
スクリプト言語なので、関数呼び出しの前に定義があればOK。

```python:main.py
# DBコネクション取得関数
def get_connection():
    dsn = "host=＜Host＞ port=5432 dbname=＜Database＞ user=＜User＞ password=＜Password＞"
    return psycopg2.connect(dsn)
```

＜＞で括った部分はさっき開きっぱなしにしてもらったheroku postgres＞Settings＞View Credentialsの内容。
"＜"、"＞"は不要。

##DBアクセスする部分の関数の定義
とりあえず、DBアクセスしてエラーにならずに返ってくればよいので、
適当なSQLを実行して返す関数を用意。
OracleDBみたいにdual表があれば楽だったんだけど、
とりあえず、どこのテーブルも参照しないSQLにしてみる。

記述場所はget_connection()関数定義の次くらいで。

```python:main.py
# 返事取得関数（今は暫定で日付返す関数）
def get_response_message(mes_from):
    # "日付"が入力された時だけDBアクセス
    if mes_from=="日付":
        with get_connection() as conn:
            with conn.cursor(name="cs") as cur:
                try:
                    sqlStr = "SELECT TO_CHAR(CURRENT_DATE, 'yyyy/mm/dd');"
                    cur.execute(sqlStr)
                    (mes,) = cur.fetchone()
                    return mes
                except:
                    mes = "exception"
                    return mes

    # それ以外はオウム返し
    return mes_from
```

"日付"と発言されたときだけ、DBアクセスして現在日時をとってくる処理。
エラー処理は動作と一緒に説明したいので後述。

##オウム返しメソッドの変更
handle_message関数を変更する。

```python:main.py（変更前）
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))
```
```python:main.py（変更後）
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=get_response_message(event.message.text)))
```
event.message.textをget_response_message()を経由させて渡すように変更。

#デプロイ
gitコマンド

```console:コマンドプロンプト
git add .
git commit -m "Commit Message"
git push heroku master
```

"Commit Message"は適当に。
~~私はいつもめんどくさいので"a"で。~~
新しく何かしようとし始めた時だけちゃんと入力するかも。"heroku postgres test"とか。

#動作確認
挙動はおよそ5パターン。
* オウム返しされる
* 日付が返ってくる。
* "exception"が返ってくる。
* 既読無視される。
* 既読がつかない。

##オウム返しされる
###入力が"日付"以外
if文の中に入らず、普通に受け取った文字列をそのまま返せている。

###入力が"日付"
* if文の条件が失敗している。
* LINEで話しかけてるBOTと修正したBOTが違う。

##日付が返ってくる
if文の中に入りSQLの実行に成功し、日付が返ってきた。

##"exception"が返ってくる
try～exceptの間でエラー発生。
主にSQLが間違っている可能性が高い。

##既読無視される
* SQLの結果がJSONで対応してない型が返ってきた(日付型とか)

今回の記事を書いている最中に気付いたが本題から外れるので対象外とした。
get_response_message内でtryしているものの、そこではエラーとならず、
呼び出し元に戻り値を返したタイミングでエラーとなっている模様。
戻り値の型指定したらtryでキャッチできるかなと思ったけど、
どうも呼び出し元にエラーを返してしまうらしい（要確認）。

次の記事とかが参考になりそう。
[pythonでjson出力する際で対応していない型(e.g. datetime)の値を変換しながら出力したいline-bot-sdk-python](https://qiita.com/podhmo/items/dc748a9d40026c28556d)

とりあえず、BOTは動いているが、アプリがクラッシュしてそうなので、
ログを見て、解析してほしい。

```console:コマンドプロンプト
heroku logs --tail
```

##既読がつかない
* ネットワークに問題ないか。
* BOTは生きているか。

#まとめ
"日付"を入力して日付が返ってきた AND それ以外を入力してオウム返しが起きた
が確認できれば、無事成功です。

ここまでできた人なら、あとはDB使っていろいろできるんじゃなかろうかと。
MES_FROM,MES_TOだけのテーブル構成で大量に組み合わせ登録して、
MES_FROMで検索して、MES_TOを取得して返すというようなことをすれば、
一昔前の大量IF文で構成した簡易AIみたいになったり。

ここまで読んだ方、お疲れ様でした。

#参考にしたサイト
* [【Heroku Postgresql】アドオンを追加し、テーブル操作を行う](https://www.shookuro.com/entry/2018/03/11/160201)
* [いちばん簡単にPostgreSQLを使う ～HerokuPosgresに繋いでみよう](https://qiita.com/rzl5/items/09f61d308080bf801a8b)
* [【Linebot #1】windows8.1(local)とherokuでpostgreSQL環境構築 (psycopg2)](http://motojapan.hateblo.jp/entry/2017/07/10/115123)
* [HerokuでApplicationErrorが発生したときの対処法](https://qiita.com/Oakbow/items/1565922ddcdea0ce9ab5)
 
もっと見ていた気がするけど、postgres絡みやエラー処理絡みで、
ブックマークに残していたのは以上でした。