🥅

PythonによるLINE Botローカル開発環境構築(Docker,ngrok)

2023/12/25に公開

LINE Messaging APIを使用したLINE Botの実装をPythonで行っています。
本番環境はFaas(Cloud Functions)かHerokuあたりを利用予定です。

開発生産性を考えると、本番環境にデプロイする前にローカルで動作確認できるようにし、アジリティ高く開発したいですよね。
また、Pythonは自分の経験上ローカルへのインストールの罠が多いため、Docker環境を利用します。

そこで本記事では、PythonによるLINE Botローカル開発環境構築の解説をします。

準備

LINE Bot開発における下記の基本的な準備を終えていることを前提とします。

  • LINE Messaging APIに対応したチャネルを用意していること
  • 新しく作成したチャネルに友だち登録をしていること
  • チャネルシークレット, チャネルアクセストークンを把握していること
  • Messaging API設定 -> Webhook設定の Webhookの利用がオンになっていること
  • LINE Official Account Manager → 応答設定 → 応答機能のチャットが恩になっていること
  • こちらの設定のみ、LINE DevelopersではなくLINE Official Account Managerから行うため注意が必要です

補足) 設定関連のスクリーンショット

設定関連はLINE Developersで行うのがおすすめです。LINE Official Account Managerでは一部の設定ができないため注意が必要です。

詳しい進め方は下記が参考になります。

https://developers.line.biz/ja/docs/messaging-api/overview/

ファイル

概要

ファイル構成は下記のとおりで、.env, .gitignore, Dockerfile, compose.yaml, main.py, requirements.txtについて記します。

$ tree -a -L 1
.
├── .env
├── .env.sample
├── .git
├── .gitignore
├── Dockerfile
├── README.md
├── compose.yaml
├── main.py
└── requirements.txt

2 directories, 8 files

main.py

基本は、line-bot-sdk-pythonのUsageを流用しています。

唯一の追加箇所にして一番の肝は、ngrokを使用していることです。
PublicなURLを発行してローカルホストへフォワード(プロキシ)しています。
ENV=devの時のみngrokを起動しています。

import os

from flask import Flask, request, abort

from linebot.v3 import (
    WebhookHandler
)
from linebot.v3.exceptions import (
    InvalidSignatureError
)
from linebot.v3.messaging import (
    Configuration,
    ApiClient,
    MessagingApi,
    ReplyMessageRequest,
    TextMessage
)
from linebot.v3.webhooks import (
    MessageEvent,
    TextMessageContent
)

app = Flask(__name__)

configuration = Configuration(access_token=os.getenv('LINE_CHANNEL_ACCESS_TOKEN'))
handler = WebhookHandler(os.getenv('LINE_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:
        app.logger.info("Invalid signature. Please check your channel access token/channel secret.")
        abort(400)

    return 'OK'


@handler.add(MessageEvent, message=TextMessageContent)
def handle_message(event):
    with ApiClient(configuration) as api_client:
        line_bot_api = MessagingApi(api_client)
        line_bot_api.reply_message_with_http_info(
            ReplyMessageRequest(
                reply_token=event.reply_token,
                messages=[TextMessage(text=event.message.text)]
            )
        )

def forward():
  import ngrok

  listener = ngrok.forward(8888, authtoken_from_env=True)
  print(f"Ingress established at {listener.url()}")


if __name__ == "__main__":
    if os.getenv('ENV') == 'dev':
        forward()

    app.run(port=8888)

.env

NGROK_AUTHTOKEN には、ngrokのSign upによって生成されたトークンを記載します。

NGROK_AUTHTOKEN=
LINE_CHANNEL_SECRET=
LINE_CHANNEL_ACCESS_TOKEN=

.gitignore

git commitしないよう気をつけましょう。

.env

Dockerfile, compose.yaml

# Dockerfile
FROM python:3.12

RUN apt-get update -y && \
	apt install -y wget make

RUN pip install --upgrade pip

WORKDIR /app

COPY ./ ./

RUN pip install --requirement requirements.txt

EXPOSE 8888
# compose.yaml
version: "3"
services:
  python:
    build:
      context: .
    volumes:
      - ./:/app
    ports:
      - "8888:8888"
    command: python main.py
    environment:
      ENV: "dev"
      NGROK_AUTHTOKEN: "${NGROK_AUTHTOKEN}"
      LINE_CHANNEL_SECRET: "${LINE_CHANNEL_SECRET}"
      LINE_CHANNEL_ACCESS_TOKEN: "${LINE_CHANNEL_ACCESS_TOKEN}"

サーバー起動〜動作確認

サーバー起動

ビルドしてから、

docker compose build

起動します。

$ docker compose up
WARN[0000] Found orphan containers ([llm-linebot-ngrok-1]) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
[+] Running 1/0
 ✔ Container llm-linebot-python-1  Created                                                                                                                           0.0s
Attaching to llm-linebot-python-1
llm-linebot-python-1  |  * Tip: There are .env or .flaskenv files present. Do "pip install python-dotenv" to use them.
llm-linebot-python-1  | Ingress established at https://bd9b-114-186-50-114.ngrok-free.app
llm-linebot-python-1  |  * Serving Flask app 'main'
llm-linebot-python-1  |  * Debug mode: off
llm-linebot-python-1  | WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
llm-linebot-python-1  |  * Running on http://127.0.0.1:8888
llm-linebot-python-1  | Press CTRL+C to quit
llm-linebot-python-1  | 127.0.0.1 - - [25/Dec/2023 03:04:16] "POST /callback HTTP/1.1" 200 -

https://bd9b-114-186-50-114.ngrok-free.app PublicなURLが発行されましたね。

このURLを次のステップで使います。

Webhook URLに登録する

LINEのコンソールから Webhook URL に登録します。

Pythonのflaskサーバーは /callback で待ち受けているため、上記のngrokによって発行されたURL `https://bd9b-114-186-50-114.ngrok-free.app` +`/callback` を記載します。

動作確認

最後に動作確認をしましょう。

友だち登録したBotに何かしらメッセージを送信します。

同じメッセージが帰ってきたら成功です!

デバッグ

「このアカウントでは個別のお問い合わせを受け付けておりません。」と応答される

動作確認の際に、メッセージを送信すると、下記のメッセージがBotから返信されることがあります。

メッセージありがとうございます!

申し訳ありませんが、このアカウントでは個別のお問い合わせを受け付けておりません。
次の配信までお待ちください(moon smile)

これは自動応答メッセージがオンになっているのが原因です。下記を参考に、応答メッセージをオフにすることで解決します。

既読マークはつくが返信が来ない

サーバー起動〜動作確認の手順をもう一度試してみてください。
ngrokは起動の都度URLが変わるため、Webhook URLへの登録も再度行う必要があります。

Discussion