Python / FastAPIでおうむ返しBot

リプライメッセージの基礎となるおうむ返しBotですが、PythonのwebフレームワークであるFastAPIを利用した記事が見当たらなかったので、初心者の方向けにコピペで作成できるよう、記事をまとめてみました。

## FastAPIについて

https://qiita.com/tamura__246/items/672bd98a8f30b76789b6

こちらの記事がわかりやすいので引用させていただきました。
記事にある通り、webフレームワークの一つです。比較的新しいフレームワークで、これまで使われてきたフルスタックのフレームワークである`Django`やマイクロフレームワークの`Flask`に続いて、人気のフレームワークと言えるかなと思います。
開発・更新も盛んであり、現在注目されているフレームワークです。

## 開発環境
> MacOC version 12.6
> Python 3.11.3
> ngrok 2.3.40
> Pythonのライブラリ関連は`Requirements.txt`参照
 <details><summary>Requirements.txt</summary>
 
```text:Requirements.txt
aiohttp==3.8.4
aiosignal==1.3.1
anyio==3.6.2
async-timeout==4.0.2
attrs==23.1.0
certifi==2022.12.7
charset-normalizer==3.1.0
click==8.1.3
fastapi==0.95.1
frozenlist==1.3.3
future==0.18.3
h11==0.14.0
idna==3.4
line-bot-sdk==2.4.2
multidict==6.0.4
pydantic==1.10.7
requests==2.28.2
sniffio==1.3.0
starlette==0.26.1
typing_extensions==4.5.0
urllib3==1.26.15
uvicorn==0.21.1
yarl==1.9.2
```

> </details>

`Requirements.txt`をコピーして
実行ファイルを置いたフォルダの中に貼り付けてください。
ライブラリをインストールするときは
`pip install -r requirements.txt`
で一括インストールできます。

:::note warn

Pythonで開発するときは、**必ず仮想環境を立ててその中にライブラリをインストールしてください**。後で後悔します。

誤ってlocalにインストールしてしまった場合、
`pip freeze | xargs pip uninstall`
これでライブラリを全て削除できます。

仮想環境の立て方は`python -m venv .venv`
起動するときは
windows: `.venv\Scripts\activate.bat`
mac: `. .venv/bin/activate`
止めるときは`deactivate`

詳細は以下のページを見てください。
:::

https://www.python.jp/install/windows/venv.html

Pythonはこの辺りの開発環境構築するところがNode.jsと比較して~~クソだるいですね~~初心者の方には難しいですね。~~Python初心者向けって言ったやつ誰だよ~~

## サンプルコード

```python:main.py
from fastapi import FastAPI, Request
import logging
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, TextSendMessage
from dotenv import load_dotenv; load_dotenv()
import uvicorn
import os

CHANNEL_SECRET = os.environ.get('CHANNEL_SECRET') or 'CHANNEL_SECRETをコピペ'
CHANNEL_ACCESS_TOKEN = os.environ.get('CHANNEL_ACCESS_TOKEN') or 'CHANNEL_ACCESS_TOKENをコピペ'

app = FastAPI()

line_bot_api = LineBotApi(channel_access_token=CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(channel_secret=CHANNEL_SECRET)

logger = logging.getLogger(__name__)

@app.post("/callback")
async def callback(request: Request):
    signature = request.headers['X-Line-Signature']
    
    body = await request.body()
    logger.info("Request body:" + body.decode())
    
    try:
        handler.handle(body.decode(), signature)
    except InvalidSignatureError:
        logger.warning("Invalid signature")
        return "Invalid signature"
    
    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__":
    uvicorn.run("app:app", host="0.0.0.0", port=8000, log_level='info')
```

:::note
channel access token, channel secretはLINE Developersから取得し、書き換えてください。

:::

## 実行方法

### `main.py`の実行

いつもの感じで`python main.py`ではエラーが出ます。
ターミナルに`uvicorn main:app --reload`と打ち込んで実行してください。

### `ngrok`でトンネリング

ターミナルを分割して、
`ngrok http 8000`を実行してください。
**8000**です。注意してください。

ngrok自体の使い方はこちら

https://zenn.dev/protoout/articles/47-ngrok-setup-2022

### `webhook url`の設定

上記記事の通りngrokに出てくるURLをコピペするのですが、末尾に`/callback`をつけてください。

`https://0962-2404-7a81-9aa0-9e00-4dc8-c253-9e65-2ad4.ngrok-free.app/callback`

こんな感じのエンドポイントになると思います。

## 実際に試してみる

![スクリーンショット 2023-04-27 1.18.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/16e0950b-8042-b2e3-7d59-fd590719c415.png)

画像のように返ってくれば成功です。
お疲れ様でした。

## 参考にさせていただいたサイトさま

https://chat.openai.com/

![スクリーンショット 2023-04-27 1.20.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/a4527a12-9794-d721-eaac-9e7076f22f22.png)

つまりはそういうことです。