🤖

Flask で開発する LINE ChatBot

2022/06/14に公開

LINE Chatbot 開発を行ったときのメモです.LINE Developers の管理画面から作成することもできますが,今回は Messaging API を利用して開発します.基本的には公式の LINE Documentation が情報豊富ですが,自分で開発したときの流れを備忘録として残していきます.

前提

  • LINE アカウントは持っているものとする
  • LINE Developers の開発者登録も済んでいるものとする
    • LINE Developers では Providers まで作成済みとする
  • バックエンドは Python のフレームワーク Flask を利用
  • GitHub の使い方には触れない
  • Heroku へのデプロイ方法の記述は最小限にする

開発環境

  • Macbook : macOS Monterey (12.3.1)
  • Python : 3.10.0
  • Flask : 2.1.2
  • line-bot-sdk : 2.2.1

1. Messaging API

LINE の ChatBot を作成するためには LINE Developers のコンソールから Messaging API の設定をします.やはり公式ドキュメント(MessagingAPI)が一番わかりやすいです.

  • LINE Developers の管理画面にアクセスする
  • Providers からアカウントを選択

img01

  • Channels で Create Messaging API channel を選択
  • アプリの名前やアイコンなど必要な設定を入れていく

img02

  • アカウントの Channels に作成した Bot が追加されていれば OK
    • Channel を作成すると「LINE Official Account」にもアカウントが作成されている

img03

2. Flask アプリの作成

次に LINE Bot からのリクエストを受けるバックエンド側の API を作成します.任意の場所にプロジェクトルートとなるディレクトリを作成します.今回は chatbot-backend という名前です.構成は以下のようになっています.

chatbot-backend
   L app.js
   L Procfile
   L README.md
   L requirements.txt
   L runtime.txt
  • pip install flask で Flask をインストール

2.1. Flask の動作確認

はじめに Flask の動作確認から行います.

  1. app.py を編集して最小構成の
from flask import Flask

# generate instance
app = Flask(__name__)

# endpoint
@app.route("/")
def test():
    return "<h1>It Works!</h1>"
  1. プロジェクトルートから app.py のある app ディレクトリへ移動する
  2. flask run でローカルに Web サーバを起動
  3. ブラウザから localhost (127.0.0.1:5000) にアクセス
  4. 画面に IT Works! と表示されれば OK

2.2. Heroku へのデプロイ

つぎに Heroku へのデプロイを行います.パブリックな API として正常に動作することを確認した後に LINE ChatBot とのつなぎこみを行います.作成したプロジェクト (chatbot-backend) は GitHub のリモートリポジトリに push しておいてください.

Heroku の準備

  • Heroku のアカウント登録を行う
  • Create new app でアプリを作成する
  • ドキュメントを参考に Heroku CLI をインストールする
  • プロジェクトルートで Heroku login
  • heroku git:remote -a app-name でローカルとリモートを接続

Flask デプロイの準備

  • pip freeze > requirements.txt で Flask で利用するパッケージを書き込む
    • 必要ないものも入ってしまうことがあるので手で記述
    • とりあえず以下のものが記述されていれば OK
Flask==2.1.2
Jinja2==3.1.2
gunicorn==20.1.0
line-bot-sdk==2.2.1
  • Procfile の編集
    • Heroku は Web を動かすためのコマンドを記述する Procfile がないとうごかない
    • プロジェクトルートに配置する
web: gunicorn app:app --log-file=-
  • runtime.txt の編集
python-3.10.0
  • runtime に指定できるバージョンは決まっているので注意が必要 (ここでは最新)

デプロイ

  • Git コマンドでデプロイする
    • git push heroku main
    • Heroku の管理画面の「Deploy」から GitHub のリポジトリと接続することで GitHub への push と同時に Heroku へデプロイする設定が入れられます (楽です)
  • Web アプリとしての設定を入れる
    • heroku ps:scale web=1
  • アプリケーションを開く
    • heroku open
  • ブラウザが開いて "It Works!" が表示されれば OK

ここまでで API のデプロイまでが完了しました.あとは LINE Bot 側から API を叩くようにつなぎこみができれば完成です.

3. LINE ChatBot と API を繋ぐ

LINE の ChatBot 側から今デプロイした API を叩きチャットボットとしての機能を完結させます.

3.1. Webhook の設定

  • LINE Developers コンソールでチャンネルアクセストークンを発行しておく
  • 発行したトークンを環境変数として Heroku にセットする
    • heroku config:set ACCESS_TOKEN="アクセストークン" --app アプリ名
    • heroku config:set CHANNEL_SECRET="チャンネルシークレット" --app アプリ名
  • LINE Developers のコンソールから Messaging API の設定を行う
    • 「あいさつメッセージ」と「応答メッセージ」の設定は「無効」にする
  • Webhook URL を設定する
    • Heroku にデプロイしたときに払い出された URL を指定する
    • https://hoge.herokuapp.com/callback
    • callback を追加するようにする

これで Webhook の設定は完了ですが,まだ app.py のソースコードを書き換えていないので「検証」はうまくいきません.次で Flask のソースコードを書き換えます.

3.2. app.py にエンドポイントを追加する

公式ドキュメントの「Heroku でサンプルボットを作成する」のページに line-bot-sdk-python のリポジトリへのリンクがあるので,ここの README を参考に Flask にソースコードを追加します.ただしコピペではうまく行かなかったので適宜変更をしていく必要があります.

  • app.py を以下のように編集します
    • 以下のコードは 2.1 節で作成した app.pyline-bot-sdk-python のサンプルコードを追加した完成形です
from flask import Flask
from flask import request
import os
from linebot import (LineBotApi, WebhookHandler)
from linebot.exceptions import (InvalidSignatureError)
from linebot.models import (MessageEvent, TextMessage, TextSendMessage,)

# generate instance
app = Flask(__name__)

# get environmental value from heroku
ACCESS_TOKEN = os.environ["ACCESS_TOKEN"]
CHANNEL_SECRET = os.environ["CHANNEL_SECRET"]
line_bot_api = LineBotApi(ACCESS_TOKEN)
handler = WebhookHandler(CHANNEL_SECRET)

# endpoint
@app.route("/")
def test():
    return "<h1>It Works!</h1>"

# endpoint from linebot
@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:
			print("Invalid signature. Please check your channel access token/channel secret.")
			abort(400)
		return 'OK'

# handle message from LINE
@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()
  • サンプルコードと違う点として以下のような処理を追加しています
    • Heroku にセットした環境変数を取ってくる処理を追加
      • import os も忘れずに
    • Messaging API からの「検証」で POST のリクエストをさばくために Flask の request というモジュールが必要なので追加
      • from flask import request
      • これがないと request が undefined 的なエラーで怒られました
  • LINE Developers コンソールの Messaging API 設定タブの「検証」を行い,「成功」と表示されればつなぎ込みは完了!!

3.3. オウム返し

ChatBot 開発における Hello, World 的な定番機能であるオウム返しを試します.app.py@handler.add の部分がメッセージイベントの処理部分です.特に修正しなくてもこのままでオウム返しができます.

ユーザからのメッセージは event の中に含まれます.eventjson 形式のデータになっており以下のようなデータで構成されています.

{
  "message": {
    "id": "xxxxx",
    "text": "sample",
    "type": "text"
  },
  "mode": "active",
  "replyToken": "xxxxxxxxxx",
  "source": {
    "type": "user",
    "userId": "xxxxxxxxxxxxxxx"
  },
  "timestamp": 1655090322382,
  "type": "message"
}

event.message.text がユーザが送信してきたメッセージなので app.pyTextSendMessage() でそのまま指定することでオウム返しができるようになっています.

img04

4. ChatBot としての機能を実装する

ここまでで,ChatBot の本体である API と Messaging API をつないで動かすことに成功しました.ここからはようやく ChatBot としての機能を実装していきます.今回はサンプルなので機械学習などは使わずルールベースで返答を返していきます.

4.1. 追加機能の実装準備

追加の機能を直接 app.py に書いてしまうとコードが読みづらいので,役割分担してプロジェクトルートに src フォルダを作成し,Python のモジュールとして実装して app.py から呼び出すようにします.フォルダ構成は以下のようになっています.

chatbot-backend
   L app.js
   L Procfile
   L README.md
   L requirements.txt
   L runtime.txt
   L src
       L dict
	       L mydict.py
	   L services
	       L handle_message_service.py

src 以下は自作の単語辞書を格納する dict というフォルダと,ボットとしての機能を格納していく services というフォルダを作成します.

4.2. 単語辞書の作成

まずは特定の単語が来たらなにか返答をしてほしいのでその辞書を手作りします.ボットとしてはあまりいい方法ではないですが,今回はお試しということでこのような方法をとります.

  1. dict 以下に mydict.json を作成する
  2. mydict.json の中身は以下のようにする
    • テスト用なので登録するセリフは適当でよいです
{
  "test": "ChatBot Works!",
  "sample": "Hi, I am sample ChatBot!",
  "hello": "Hello, World!",
  "おはよう": "おはようございます!コーヒーでもどうですか?",
  "こんにちは": "こんにちは!お昼は何をたべましたか?",
  "こんばんは": "こんばんは、いい夜をお過ごしください。",
  "except": "すみません、よくわかりませんでした。"
}

これで辞書の準備は OK です.

4.3. メッセージの処理

特定のメッセージに反応して返信を行う機能を作ります.

  1. src/services 以下に handle_message_service.py というファイルを作る
  2. handle_message_service.py を次のようにする
import json

class handle_message_service :
	def generate_reply_message(receivedMessage) :
		json_file = open('/app/src/dict/mydict.json', 'r')
		mydict = json.load(json_file)

		for key in mydict :
			if (receivedMessage == key) :
				return mydict[key]
		return mydict['except']

この handle_message_service.py はユーザが送信したメッセージを処理するためのプログラムです.今回は json で作成した辞書を読み込んでそれを json のキーと照らし合わせて一致した値を返すようにしています.

4.4. app.py を書き換える

最後に app.py から handle_message_service.py を呼び出すように書き換えます.app.py の中身を以下のようにします.

  1. 作成した handle_message_service.py をモジュールとして読み込むため import 文に from src.services.handle_message_service import * を追加
  2. @handler.add の処理を以下のように書き換える
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
	# メッセージ生成用のモジュール呼び出し
	reply = handle_message_service.generate_reply_message(event.message.text)
	line_bot_api.reply_message(
		event.reply_token,
		TextSendMessage(text=reply))

こうすることで今後ユーザから送信されてきたメッセージの処理を書き換える際に app.py を処理する必要はなくなり hanndle_message_service.py だけを修正すればいいだけにしています.全体像としては以下のようになります.

from flask import Flask
from flask import request
import os
from linebot import (LineBotApi, WebhookHandler)
from linebot.exceptions import (InvalidSignatureError)
from linebot.models import (MessageEvent, TextMessage, TextSendMessage,)
from src.services.handle_message_service import *  # 追加した行

# generate instance
app = Flask(__name__)

# get environmental value from heroku
ACCESS_TOKEN = os.environ["ACCESS_TOKEN"]
CHANNEL_SECRET = os.environ["CHANNEL_SECRET"]
line_bot_api = LineBotApi(ACCESS_TOKEN)
handler = WebhookHandler(CHANNEL_SECRET)

# endpoint
@app.route("/")
def test():
	return "<h1>It Works!</h1>"

# endpoint from linebot
@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:
		print("Invalid signature. Please check your channel access token/channel secret.")
		abort(400)
	return 'OK'

# handle message from LINE
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
	# メッセージ処理のモジュール呼び出し
	reply = handle_message_service.generate_reply_message(event.message.text)
	line_bot_api.reply_message(
		event.reply_token,
		TextSendMessage(text=reply))

if __name__ == "__main__":
	app.run()

これで今回作ったサンプルのチャットボットのプログラムは完成です.Heroku にデプロイして動かしてみます.

img05

きちんと返ってきているので成功です!

まとめ

LINE のチャットボット開発系の情報はググればたくさん出てきますが,自分で開発したときのフローをメモとして残しました.今回はすべて決め打ちで返事を返すだけの,チャットボットと言えるか怪しいプログラムですが,ここまで形ができていればあとは中身を書き換えていけばいいだけなので開発に集中できます.今後は機械学習のライブラリの導入などを行ってチャットボットっぽくしていきたいと思います.

Reference


Discussion