Flask + HerokuでLINE Botを開発 (Gitを使用したデプロイ編)

# はじめに
- Flask + Herokuでオウム返しを行う簡単なLINE Botの開発を行う。
- 本記事では、Gitを使ってアプリケーションをHerokuにデプロイする
- LINE Developersへの登録や、Herokuへの登録は概要のみ伝える。

## 環境
- python 3.9.16
※Herokuでサポートされているversionは2023年6/16現在では主に4つあります。
　詳しくは本記事、「LINE Botの実装」の「Pythonランタイムを指定する」という欄で説明しています。
- Flask 2.2.3
- line-bot-sdk 2.4.2
- gunicorn 20.1.0
- git

## 開発手順
1. LINE Developersに登録
1. Botの実装
1. Herokuの登録＆設定
1. アプリをHerokuへデプロイ


# 1. LINE Developersに登録
https://developers.line.biz/ja/

登録後の流れは以下のようになる。
細かい設定に関しては-- [チャネルを作成する](https://developers.line.biz/ja/docs/liff/getting-started/)を参照すること。
1. 開発者名を登録
2. 新規プロバイダーを作成
※プロバイダー：LINEプラットフォームを通じてサービスを提供する個人、企業、またはそのほかの組織を意味する情報 -- [LINE Developers](https://developers.line.biz/ja/docs/liff/getting-started/)
3. チャネル作成 (LINE Messaging APIを選択)
※チャネル：チャネルは、LINEプラットフォームが提供する機能を、プロバイダーが開発するサービスで利用するための通信路 -- [LINE Developers](https://developers.line.biz/ja/docs/liff/getting-started/)


# 2. LINE Botの実装
## ディレクトリ構成
最終的なディレクトリ構成は以下のようになる。
> sample-app/
　├ app.py
　├ Procfile
　├ requirements.txt
　└ runtime.txt

### 2-1. コードを準備する
今回は、https://github.com/line/line-bot-sdk-python にあるオウム返しを行うシンプルなLINE Botを使用する。少し変更を加えた以下のコードをコピペすれば上手く動く。
```python:app.py
import os
from flask import Flask, request, abort

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

app = Flask(__name__)

#環境変数取得
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]

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

@app.route("/")
def hello_world():
    return "hello world!"

@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):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))

if __name__ == "__main__":
#    app.run()
    port = int(os.getenv("PORT"))
    app.run(host="0.0.0.0", port=port)
```

### 2-2. アプリの依存関係をローカルでインストールする
1. requirements.txtをルートディレクトリに配置する
   - requirements.txt：プロジェクトの依存関係を一覧にしたテキストファイル。このファイルに必要なパッケージとそのバージョンを記述する。
    - アプリをデプロイするとHerokuはこのファイルを読み出し、`pip3 install -r` コマンドを使用してPythonの依存関係をインストールする。つまり、requirements.txt​によって、自分のアプリがPythonアプリであることをHerokuが認識する
requirements.txtは以下のようになる。
   ```
    Flask==2.2.3
    line-bot-sdk==2.4.2
    gunicorn==20.1.0
   ```
1. 依存関係(パッケージ)をローカルにインストール
    ```
    $ pip3 install -r requirements.txt
    ```
    最後に`pip3 list`でPythonの依存関係がインストールされているか確認する。
    ※Python の依存関係をインストールすると、ほかの依存関係もいくつかインストールされる。

### 2-3. Procfileを定義する
- Procfile：このファイルを使って、アプリの起動時に実行するコマンドを明示的に宣言する。
Procfileは以下のようになる。
```
web: gunicorn app:app
```
このコードによって、webプロセスタイプとして、Gunicornを使用してPythonのwebアプリを使用できる。これにより、webサーバーが適切に設定され、アプリケーションがリクエストを受け付ける準備が整う。以下がこのコードの詳細である。
- "web"：Webアプリケーションサーバーを実行するためのプロセスタイプを指定するために一般的に使用される。
- "gunicorn"：Webアプリケーションサーバーとして、gunicornというPythonのパッケージが使用されている。Gunicornは、PythonのWebアプリケーションを高速かつ効率的に実行するためのWSGI（Web Server Gateway Interface）サーバー。
- "app:app"：起動するPythonアプリケーションを指定する。1つ目のappは、Pythonモジュール名またはファイル名で、実際のアプリケーションのコードを含んだPythonファイルを指定する。2つ目のappは、実際に実行されるアプリケーションオブジェクトの名前を指定する。これにより、Gunicornは指定されたPythonアプリケーションを起動し、Webアプリケーションを提供する。

### 2-4. Pythonランタイムを指定する
- runtime.txt：Heroku側で使用するPythonのバージョンを指定するファイル
以下のように記述する。
```
python-3.9.16
```
Herokuでサポートされている以下の4つのバージョンであれば、どれでも良い。
> python-3.11.2​ (すべてのスタック​で利用可能。推奨​)
  python-3.10.10​ (すべてのスタック)​で利用可能)
  python-3.9.16​ (すべてのスタック)​で利用可能)
  python-3.8.16​ (Heroku-18​ および Heroku-20​ スタックでのみ利用可能)
  　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　-- [Heroku の Python サポート](https://devcenter.heroku.com/ja/articles/python-support#supported-runtimes)


# 3. Herokuの登録＆設定

### 3-1. Herokuにアカウントを登録
まずは、[Heroku](https://id.heroku.com/login)にアカウントを登録してください。
（ 注意事項としては2022年11月28日から無料プランの提供を停止したことです。そのため、それ以前のQiitaの記事やブログでは無料プランを利用してデプロイを行っているため、記事の更新日時を確認した方が良いです。しかし! 朗報があります。なんと、学生に対しては毎月13＄使えるクレジットを1年間(=合計$156)使わせてくれるのです。あやかりたい学生はこちらの[公式サイト](https://www.heroku.com/github-students)から登録できます。 )

### 3-2. Heroku CLIをインストール
Heroku CLIはコマンドラインからHerokukアプリを作成・管理できるようにしてくれるツールです。
インストール方法は3つほどあります。公式サイトを参照したい方は[こちら](https://devcenter.heroku.com/articles/heroku-cli)から。
1. インストーラーによるインストール
こちらの[公式サイト](https://devcenter.heroku.com/articles/heroku-cli)から自分のOSに合うものをインストールしてください。
2.  tar書庫によるスタンドアローンインストール
CLI を /usr/local/lib/heroku​ および /usr/local/bin/heroku​ で設定するには、以下のスクリプトを実行する必要がある。このスクリプトでは sudo が必要で、Windows に対応していません。CLIの自動更新を行ってくれます。
``` shell
$ curl https://cli-assets.heroku.com/install.sh | sh
```
3. Ubuntu / Debian apt-get でのインストール
私はwsl2を利用しており、こちらからインストールしました。自動更新は行われないため、`apt-get​` を使用して手動で更新してください。

### 3-3. Heroku CLIを使用する
まずは、無事にインストールが出来ているか、`heroku --version`コマンドで確認してください。
以下のように表示されていれば問題ありません。
```shell
$ heroku --version
# heroku/8.1.7 wsl-x64 node-v16.19.0
```
続いて、`heroku login`コマンドを実行して、ログインします。任意のキーを押すとwebブラウザに飛び、ログインが完了します。以下のように表示されていれば問題ありません。
``` shell
$ heroku login
#heroku: Press any key to open up the browser to login or q to exit
# ›   Warning: If browser does not open, visit
# ›   https://cli-auth.heroku.com/auth/browser/***
#heroku: Waiting for login...
#Logging in... done
#Logged in as me@example.com
```


# 4. アプリをHerokuへデプロイ
`pwd`コマンドを実行し、現在のディレクトリがsample-appであることを確認してから作業を行う。
> デプロイとは、つまり、アプリケーションをローカルシステムから Heroku に移動することです。Heroku にはアプリケーションをデプロイできる方法が複数用意されています。-- [Herokuの仕組み](https://devcenter.heroku.com/ja/articles/how-heroku-works/)
### 4-1. アプリを作成する
今回は“example” という名前の新しいアプリを作成するため、以下のコマンドを実行する。
違うアプリ名で作成したい場合、"example"の部分を変更する(例. heroku create hoge)。
※アプリ名の"example"を省略した場合、ランダムな名前が生成される。
```shell
$ heroku create example
# Creating ⬢ example... done
# https://example.herokuapp.com/ | https://git.heroku.com/example.git
```
この出力は、アプリが`http://example.herokuapp.com`で利用可能になることを示している。2 番目の URL である`https://git.heroku.com/example.git​`は、リモート Git リポジトリの URL 。デフォルトでは、この URL を指す “heroku” という名前の Git リモートが heroku create​ コマンドによって自動的に追加される。
### 4-2. 環境変数を設定する
※"example"の部分は必要に応じて、自身のアプリケーション名に変更すること。
　本記事に沿って、"example"という名前で作業を進めている場合はそのままで問題がない
```
$ heroku config:set YOUR_CHANNEL_SECRET="チャンネルシークレットの文字列" --app example
$ heroku config:set YOUR_CHANNEL_ACCESS_TOKEN="アクセストークンの文字列" --app example
```
最後に、設定に誤りがないかを確認する。
```
$ heroku config --app example
```
::: note info
実は、Herokuに設定した環境変数はapp.pyの以下の部分で読み込んでいる。
確認したい方は、「2. LINE Botの実装」に記述したapp.pyを確認してみてほしい。
``` python
#環境変数取得
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]
```
:::
### 4-3 .webhookの設定
自分のチャネルの"Messaging API設定"画面から設定する。
![MessagingAPI設定画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2601652/c53ff856-692f-1b5c-80df-241424bc3651.png)
- `https://example.herokuapp.com`の後ろに`/callback`を付ける。
- webhookの利用をオンにすること。
※webhook(ウェブフック)：Webアプリケーションでイベントが実行された時に、外部サービスにHTTPで通知する仕組み。webアプリケーションやサービスが外部のシステムやサービスと自動的に連携するための仕組みであると言える。linebotはWebhookを使用することで、LINEのチャット画面上で発生するイベントを自動的に監視し、それに応じた処理を自動的に実行することができる。下の図のように、Linebotによって、ユーザーとのコミュニケーションを自動化することができる。
![MessagingAPIの仕組み.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2601652/54748070-1585-98a1-024a-d3cefa78cfb4.png)
　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　-- [Messaging APIの概要](https://developers.line.biz/ja/docs/messaging-api/overview/)
1. ユーザからLINE Botにメッセージが送信される
1. LINE Platform からBotサーバーのwebhook URLにwebhookイベントが送信される
1. BotサーバーからLINE Platformを通してwebhookイベントに応じた応答(処理)をユーザに返す

### 4-4. 新しいGitリポジトリを作成する
新規または既存のディレクトリにgitリポジトリを初期化する。
```
$ cd sample-app/
$ git init
$ heroku git:remote -a example
```
### 4-5. アプリをデプロイする
```
$ git add .
$ git commit -am "first commit"
$ git push heroku master
```
### 4-6. デプロイの成功を検証する
```
$ heroku open
```
このコマンドでwebサイトを開く事ができる
Hello Worldが表示されたら正常にデプロイされている。
### 4-7. ログを確認する
```
$ heroku logs --tail
```
このコマンドでリアルタイムでログを出力する
`heroku logs`でもログを出力できる(デフォルトでは最新の100ログ行を出力する)。
ログのストリーム出力を停止するには、Control+C​ を押すｒ。


# その他
## Herokuの主要な操作
ログイン
```
$ heroku login
```
停止・スタート
``` shell
# 停止 
$ heroku ps:scale web=0
# 再度スタート
$ heroku ps:scale web=1
#リスタート
$ heroku restart
```
変更
``` shell
$ git add . 
$ git commit -am "commit message"
$ git push heroku master
```
ログ出力
```shell
# 通常の確認
$ heroku logs
# リアルタイムで確認
$ heroku logs --tail 
```

# おわりに
　今回、LINE BOt を使った開発を行ったことをきっかけに本記事を作成した。Flask+Herokuの環境でLINE Botを開発する記事には一通り目を通したが、少し情報が古かったり、公式サイトだけでは理解が難しかったため、このように一連の作業をまとめた。ぜひ、Flask+Herokuを使ったLINE Bot開発を行う方には役立ててもらえると嬉しい。また、文章や説明等に不備がございましたら、教えて頂けますと幸いです。


# 参考記事
- [Heroku スターターガイド(Python)_Heroku公式](https://devcenter.heroku.com/ja/articles/getting-started-with-python)
- [Git を使用したデプロイ_Heroku公式](https://devcenter.heroku.com/ja/articles/git)
- [Heroku の Python サポート_Heroku公式](https://devcenter.heroku.com/ja/articles/python-support#supported-runtimes)
- [Herokuの仕組み_Heroku公式](https://devcenter.heroku.com/ja/articles/how-heroku-works)
- [チャネルを作成する_LINE公式](https://developers.line.biz/ja/docs/liff/getting-started/)
- [【Python】herokuへデプロイしLINE通知する方法【初心者OK】_Blog](https://nkmrdai.com/python-heroku-linenotify/#Heroku%E4%B8%8A%E3%81%AB%E6%96%B0%E8%A6%8F%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%BD%9C%E6%88%90)
- [【初心者向け】PythonによるHeroku環境で簡単LINEBot開発_Blog](https://miyabi-lab.space/blog/21)
- [Python + HerokuでLINE BOTを作ってみた_Qiita](https://qiita.com/shimajiri/items/cf7ccf69d184fdb2fb26)
- [【メモ】Heroku関連のコマンドまとめ_Qiita](https://qiita.com/nt-7/items/e64505a67e48ee1a4460)