Python 初心者がチャット BOT を作ったから作り方をまとめてみたよ

最近、AI・IOT セミナーなるものに行ってきました。
内容的にはエンジニア向けというよりは事務担当者のスキルアップにプログラミングなんてどうですか？的な話だったのですが、Python 触ってみようかなーと私が思い至るには充分でした！ありがとう！(*‘∀‘)

ってなわけで今回は、Python 初心者の私がいつも作ってる LINEWORKS BOT を Python で実装してみる話です！ 

## 制作環境

言語：python
WEB フレームワーク：Flask
エディタ：Visual Studio Code
サーバ：ローカル環境（ngrok）

検証サーバを持ってないので相変わらずローカルです（笑
ngrok 大好きー！( ﾟДﾟ)

## 環境構築

細かい構築手順は先輩方の記事を参考にしました！
ありがとうございます！(*'▽')
ここでは解説しませんので、リンクを貼っておきます('ω')ノ　ﾍﾟﾀﾍﾟﾀ

1. [PC に Python と Visual Studio Code をインストール](https://qiita.com/hashito/items/8f77a61ffdfe9f3d0ecb)
2. [Flask のインストール](https://qiita.com/zaburo/items/5091041a5afb2a7dffc8)
3. [ngrok で自分のローカルを外部に公開](https://qiita.com/proken/items/80d20adcf24b2b53c149#%E3%82%84%E3%82%8A%E3%81%8B%E3%81%9F)
4. [LINEWORKS で API を使うための設定と Bot 登録](https://qiita.com/tokotan/items/f615f4a62219d655436f#developer-console%E3%81%A7api%E3%82%92%E4%BD%BF%E3%81%86%E3%81%9F%E3%82%81%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%A8bot%E3%82%92%E7%99%BB%E9%8C%B2%E3%81%99%E3%82%8B)

BOT 登録時の Callback URL は ngrok で取得した URL を使用してください。
もちろん、自前でサーバをお持ちの方はそちらをお使いくださいませ～。
これで設定は完了！

いよいよ Python で BOT 本体を作っていきますよ～(*´Д｀)

## BOT へのメッセージを受け取る

Flask を使って BOT に送られたメッセージを受け取ります。

Flask は Python の WEB フレームワークです。
Python では Django の方が有名で高機能らしいのですが個人開発なので軽量の Flask にしてみました。

知見がいっぱいあって本当に助かります！(*^▽^*)

では、まず受信したメッセージをコンソールに表示するようにします。こんな感じ。

![1585814270.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363367/c9e7cdd2-e3f8-8ef9-1e3b-e38a6c4e3371.png)

```
b'{"type":"message","source":{"accountId":"xxx@yyy-zzz"},"createdTime":1585813140779,"content":{"type":"text","text":"hoge"}}'
```
コードを書いていきまする。

```py:bot.py
from flask import Flask, request
app = Flask(__name__)

@app.route('/callback', methods=['POST'])
def callback():
    data = request.get_data()
    print(data)
    return "200 ok"

## port 設定
if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=8000)
```

#### つまづいたところ

- メッセージを受け取る request.get_data() を使うには

最初の１行なんですが、flask の import だけだとメッセージを受け取れないようです。
後ろに `,request` と続けて request も import します。

- return 入れないと怒られる

最初、return を空っぽにしてたら怒られました。

```
TypeError: The view function did not return a valid response. 
The function either returned None or ended without a return statement.
```

取り合えず受信したよ！ってことで `200 ok` 返しときました。
…たぶん、javascript の `res.send` みたいなちゃんと返すやり方がある気がします。(。-`ω-)

- port のデフォルトは 5000

port 設定で何もしないとデフォルトは 5000 になるそうです。

```py
if __name__ == "__main__":
    app.run(debug=True) # port は 5000 に設定される
```

いや、5000 でもいいんですけど、ngrok さんで 8000 に設定してたもので「繋がらない！( ﾟДﾟ)」ってなりましたｗ

っていう感じでつまづいてましたが、先輩方の知見のおかげで自己解決しました！
ありがとうございます！(*'▽')

では、次の STEP へと進みましょう！

## メッセージを受け取ったら返信する

返信するには LINEWORKS API を使用するのですが、PyPI にライブラリを公開してくれている方がいらっしゃいます！感謝！(*^▽^*)
[PyPI - lineworks 0.1.0](https://pypi.org/project/lineworks/)

さっそくインストールして使っていきましょう。

```
pip install lineworks
```

インストールしたらサンプルを参考にコードを書き足していきます。

```py:lineworks_bot.py
from flask import Flask, request
app = Flask(__name__)

from lineworks import TalkBotApi
api_id = "your api id."
server_api_consumer_key = "your server api consumer key"
server_id = "your server id."
private_key = "your private key."
domain_id = "your domain id."
bot_no = "your bot number."

import json
@app.route('/callback', methods=['POST'])
def callback():
    data = json.loads(request.get_data())
    talk_bot = TalkBotApi(api_id, server_api_consumer_key, server_id, private_key, domain_id, bot_no)
    # メッセージ送信（オウム返し）
    talk_bot.send_text_message(send_text=data['content']['text'], account_id=data['source']['accountId'])
    return "200 ok"

## port 設定
if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=8000)
```

Visual Studio Code で実行して準備完了！
さぁ、話しかけてみましょう！(*´▽｀*)

![1586240207.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363367/ccae1f5f-0b48-08cd-84fb-798034004e2f.png)

YES！NINJA！
ちゃんとオウム返ししてくれましたね！成功です♪

### つまづいたところ

- privateKey の書き方

これはつまづいたというか案の定なのですが、認証キーは実際に見てもらうとわかる通り、もの凄く長い上に変な改行が入っています。なので、少々面倒なのですが、
`"-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9...\n-----END PRIVATE KEY-----"` となるように `\n` と `\n` の間に認証キーを入れて一行で表記します。

- JSON データの取り扱い

JSON ライブラリの loads で parse してから使います。
ですが、`data.source.accountId` と、Javascript のようにパラメータで扱おうとするとエラーになります。（私のやり方が悪い可能性は大ですが。。。）
なので中身を取り出すときは前述の通り `data['source']['accountId']` と記載します。
例では JSON データ内の `text` と `accountId` を取り出して使用しています。

何はともあれ、これで無事に完成です！(^O^)


# おわりに
ここまでお付き合いいただきありがとうございました。

いやー、やってることはいつもと同じなんですが、言語違うと勝手が違いますね～。
でも、少し仲良くなれた気がします！

この勢いで、機械学習 API とかと連携する BOT とか開発していきたいですね～。
ではまた！(^^)/

# 参考にさせていただきましたm(_ _)m
[LINEWORKS Developers](https://developers.worksmobile.com/jp/document?lang=ja)
[windows10にpythonとVisualStudioCodeをインストールする(2020年4月版)](https://qiita.com/hashito/items/8f77a61ffdfe9f3d0ecb)
[Flaskの簡単な使い方](https://qiita.com/zaburo/items/5091041a5afb2a7dffc8)
[LINEbot開発、ローカル環境で動作確認したい](https://qiita.com/proken/items/80d20adcf24b2b53c149)
[LINE WORKSで初めてのBot開発！(前編)](https://qiita.com/tokotan/items/f615f4a62219d655436f)
[Flaskでpostされたデータをそのまま受け取る時はrequest.get_data()をつかう](https://qiita.com/fetaro/items/97c4054dafa8aac10541)
[LINE WORKSのAPIを呼び出すためのPythonライブラリを作成しました](https://qiita.com/0yan/items/4a41c292050aeec16c32)