FlaskでLINEBotを作成してみる

Python 3.7.8
Flask 2.1.1
heroku 7.53.0

## 作成手順
1．LINE developersへ登録
2．コーディング
3．herokuへの配置

### 1．LINE developerへの登録
https://developers.line.biz/ja/へアクセス
サイト右上のログインから自分のラインアカウントを使用しLINE developersへ登録
![LINE_Developersホーム画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1770621/ffc2ce70-d062-d2b3-c33e-03defc3b8c46.png)
ここでは今回作成したproductというチャネルが表示されています。

プロバイダーの作成を押し、プロバイダー名を入力します。
その後Messaging APIを選択し、チャネルを作成しておきます。

### 2．コーディング
タイトルにもあるように今回はPythonのフレームワークであるflaskを使用して実装を行います。

私はローカルの環境には極力ライブラリはインストールしたくないので
```
python -m venv env
```
上記のコマンドで仮想環境を作成し、その中でflaskなどのライブラリをインストールするようにします

仮想環境を作成したら
```
.\env\scripts\activate
```
で仮想環境をアクティブにし、今回使用するライブラリをインストールします。
```
pip install flask
pip install line-bot-sdk
pip install gunicorn
```
LINEBotを作成するにあたり必要なファイルは以下です。
1．main.py
2．Procfile
3．runtime.txt
4．requirements.txt
5．.gitignore
```
/
├ Product/ # 今回の仮想環境、運用には必要ないので.gitignoreでcommitしないようにする
├ main.py
├ Procfile
├ runtime.txt
├ requirements.txt
├ .gitignore
```
#### main.py

LINEBotのメインの動きです。
```
from flask import Flask, request, abort

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)
import os

#Token取得

YOUR_CHANNEL_ACCESS_TOKEN = "チャネルアクセストークン"
YOUR_CHANNEL_SECRET = "チャネルシークレット"

app = Flask(__name__)
app.debug = False

line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)

@app.route("/callback", methods=['POST'])
def callback():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    df_list = [
        ['Google','https://www.google.com/'],
        ['yahoo','https://www.yahoo.co.jp/'],
        ]

    for i in range(len(df_list)):
        url = None
        if event.message.text == df_list[i][0]:
            url = df_list[i][1]
            line_bot_api.reply_message(
                event.reply_token,
                TextSendMessage(text=url)
                )
        else:
            continue

        if url == None:
            line_bot_api.reply_message(
                event.reply_token,
                TextSendMessage(text='そのようなサイトは存在しません')
                )


if __name__ == "__main__":
    port = int(os.getenv("PORT"))
    app.run(host="0.0.0.0", port=port)
```
細かく区切って解説します。
```
YOUR_CHANNEL_ACCESS_TOKEN = "チャネルアクセストークン"
YOUR_CHANNEL_SECRET = "チャネルシークレット"

app = Flask(__name__)
app.debug = False

line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)
```
ここではLINE developersで作成したMessaging APIの
アクセストークン(Messaging APIを呼び出すときに使用するチャネルアクセストークン)と
チャネルシークレット(アプリに対して、チャネルへのアクセスを許可したことを示す秘密鍵)を取得します。
```
@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']

    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'
```
LINE DevelopersのWebhookでURLを指定してWebhookからURLにイベントが送られるようにし、LINEアプリを通して送られてきたリクエストかを検証し、問題なければ処理関数を呼び出す。
```
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    df_list = [
        ['Google','https://www.google.com/'],
        ['yahoo','https://www.yahoo.co.jp/'],
        ]

    for i in range(len(df_list)):
        url = None
        if event.message.text == df_list[i][0]:
            url = df_list[i][1]
            line_bot_api.reply_message(
                event.reply_token,
                TextSendMessage(text=url)
                )
        else:
            continue

        if url == None:
            line_bot_api.reply_message(
                event.reply_token,
                TextSendMessage(text='そのようなサイトは存在しません')
                )
```
メインの処理。今回はチャットでユーザが
「Google」と送信するとhttps://www.google.com/が返信され、
「yahoo」と送信するとhttps://www.yahoo.co.jp/が返信され、
それ以外だと'そのようなサイトは存在しません'というメッセージが返信される。
```
if __name__ == "__main__":
    port = int(os.getenv("PORT"))
    app.run(host="0.0.0.0", port=port)
```
Pythonのおまじない。
今回の話の本筋ではないため説明は記載しません。
詳細を知りたい方は以下のサイトが分かりやすく解説してくださっています。
https://blog.pyq.jp/entry/Python_kaiketsu_180207

#### Procfile

herokuでアプリを動かすために必要となるファイル。
使用するWSGIを記載する。今回はgunicornを使用するので以下のような記述になる
```
web: gunicorn main:app --log-file -
```
main:appはメイン処理のファイル名によって適宜変えてください。
--log-file -はherokuのログでWSGIの挙動をログ出ししてくれる(省略可)

#### runtime.txt

このファイルがないとデフォルトで指定されているPythonのバージョン(投稿時ではPython3.9.10)が使用されてしまう。
```
python-3.7.8
```
※必ずpythonのpは小文字で記載すること。

#### requirements.txt

herokuでのビルド時にpip install -r requirements.txtが実行されて必要なライブラリがインストールされる。

開発終了時にpip freeze > requirements.txtで作成する。

#### .gitignore

不要なファイル・フォルダを記載する。
今回はPython仮想環境が必要ないのでProduct¥を追記する。

### herokuへの配置

事前にherokuのサイトでアカウントを作成し、SSH接続できるように公開鍵を登録します。
サイトでログイン後New ⇒ Create new appでアプリを作成します。

gitコマンドでローカルリポジトリへコミットします。
```
git init
git add .
git commit -m "コミットメッセージ"
```
herokuへログインしリモートリポジトリへpushします。
```
heroku login
heroku git:remote -a アプリ名
git push heroku master
```
もう一度LINE developersへ戻り、Webhookの設定を行う。
Webhook送信：利用する
Webhook URL：https://<アプリ名>.herokuapp.com/callback

ここまで出来ればLINE developersの今回の作成物の設定ページにあるQRコードを読んで友達追加し、動作確認を行うことが出来る。