LoginSignup
3
0

[無料][2024年版] LINE Messaging API v3 + Python(Flask) でボットを作る [その9 - ポストバック編]

Last updated at Posted at 2024-01-08

ポストバックとは

一言でいうと、文字列データを付加してサーバーに送信することです。購入ボタンを押した時に buy という文字列を送信したり、リッチメニューを開いた時に open という文字列を送信したり、ポストバックイベントは何らかのアクションに紐づいてサーバー側に送信されます。元々は Webアプリケーションの用語で、現在のウェブページ自身に POSTリクエストを送るところから来ているようです。

そして、このポストバックで送信するデータというのは、ユーザーからは見えません。ですので、普通のメッセージのやりとり以外で送りたいデータがあるときにはポストバックが重宝します。

わかりやすい例なんかでは、商品の購入ボタンをした時に「その商品IDを送信する」というものです。商品名が一意に決まるのであれば、ポストバックを使わずとも普通のメッセージで商品名を送っても処理できますが、データベースと連携しているなんかの場合は、IDで受け取れるというのは非常に楽でしょう。

また、アンケートなどのように連続してメッセージをやり取りする際、前の回答データを残しておきたいことがあります。この場合にもポストバックが有効です。

ポストバックは、PostbackAction としてボタンなどに配置し、Webhook受信時には PostbackEvent として処理します。

ポストバックアクション

ポストバックイベント

ポストバック+クイックリプライを使ったクイズ例

早速ですが、ポストバックを利用した完成品の動画です。Quiz と入力すると歌詞の一部から曲名を当てるクイズモードに入ります。選択肢はクイックリプライを使って4つ表示されます。計5問が出題され、最後には合計得点をカウントして表示します。

output.gif

試してみたい方はぜひこちらからボット登録してください。
LINE BOT ID : @711mvjit

711mvjit.png

このプログラムでは各クイックリプライボタンに PostbackAction が設定されています。ポストバックはその中で4つのパラメータを保持しています。

パラメータ 説明
question 前問の問題番号 0 - 5
score 前問までの合計得点 0 - 5
answer 前問の正解の選択肢 曲名
correct 前問で正解したかどうか true / false

クイックリプライの選択肢を押して一問回答すると、これらの値が処理関数に渡されて動的に更新され、次の問題のポストバックのデータになるということです。

詳細なコードは長くなるので割愛します。興味のある方は、リポジトリから app.pybotfuncs_quiz.py を見て下さい。ここでは使い方のみ説明します。

PostbackAction

linebot.v3.messaging の中に存在するクラスです。ボタンや画像などに設定できる、アクションオブジェクトの一つです。基本的に以下の3つを指定します。

  • label : 表示する文字列(20文字以下)。ボタンでは必須、画像やリッチメニューでは任意
  • displayText : そのボタンを押した時に、メッセージとして表示される文字列。表示されるだけで、実際にそのメッセージが Webhook で送信されるわけではない
  • data : 送信する不可視のデータ文字列

この data にどうやって必要な情報を入れるかは、完全にお好みです。単純に必要な値だけをそのまま入れても良いですが、一般的なのは id=123&name=abc などのように、GETリクエスト同様のクエリ文字列にする方法です。私の実装でも 'question=0&score=0&answer=NONE&correct=true' のような文字列にして送信しています。0,0,NONE,true のように配列的にしてもいいですが、やはりキー・バリュー型にしておく方がコードの可読性が上がるという理由です。

このようなクエリ文字列をパースする方法として、組み込みライブラリの urllib.parse.parse_qs() を使う方法があります。

これを用いれば、クエリ文字列を自動的に辞書型に変換してくれますが、バリューがリストになってしまうのと、型変換まではやってくれないので、自作関数を作った方が早いです。私は以下のように実装しました。

utils.py
def parse_postback(postback:str) -> dict:
	"""
	"question=1&score=0&answer=Help!&correct=true"
	-> {'question': 1, 'score': 0, 'answer': 'Help!', 'correct': 'true'}
	"""
	result = {}
	for parameter in  postback.split('&'):
		key, value = parameter.split('=')
		try:
			result[key] = int(value)
		except:
			result[key] = value
	return result

これはコードそのままで、まず & で key=value というパラメータの組に分割し、さらにそれぞれを = で分割して、整数型に変換しているだけです。今回の場合、曲名である answer の部分には &= の文字が入らないので機能しますが、もしバリュー内にこのような文字が存在する可能性がある場合、別の仕切り文字を使う必要があります。
"question@1|score@0|answer@Help!|correct@true"
などでも良さそうですね。何なら、JSON文字列にしても構わないと思います。

逆に、これらのパラメータをポストバックとして送る際には一つの文字列にまとめる必要があります。urllib.parse.urlencode というメソッドがありますが、これは don'tdon%27t のようにパーセントエンコードされてしまうので、オススメしません。自作関数でも以下のように一行で済みますので、これを使いましょう。

utils.py
def encode_postback(postback_dict:dict) -> str:
	return '&'.join(f'{key}={value}' for key, value  in postback_dict.items())

PostbackEvent

これは MessageEventFollowEvent 同様に Webhook受信のタイプを示すもので、linebot.v3.webhooks の中に入っています。app.py の中に、このように新しいイベントハンドラを作ります。

app.py
@handler.add(PostbackEvent)
def handle_postback(event):
	with ApiClient(configuration) as api_client:
		line_bot_api = MessagingApi(api_client)
	
	## get the postback data
	postback_data = event.postback.data

	## get replay message - QuickReply(with postback) or TextMessage
	messages = create_postback_reply(postback=postback_data)

	## send reply message
	line_bot_api.reply_message(ReplyMessageRequest(
		replyToken=event.reply_token,
		messages=messages
	))

この create_postback_reply(postback) がポストバックを受け取って次のポストバック付きクイックリプライを作成する具体的な処理関数です。それ以外の部分は、他のイベントハンドラと大差ありません。

クイズの設計

ではこれらを使って、全5問のクイズを作ってみましょう。大まかな処理の流れは以下のようになります。

  1. ユーザーが "Quiz" というメッセージを送信
  2. MessageEvent ハンドラ (handle_message 関数) 内でクイズモードに分岐、実際にはポストバックはないが、パラメータ初期値を疑似ポストバックとして用意して create_postback_reply(postback) に渡す
  3. create_postback_reply(postback) が与えられたポストバックから1問目のポストバック付クイックリプライを作成し、MessageEvent ハンドラ内で返信
  4. ユーザーがクイックリプライボタンを選択、ポストバック送信
  5. PostbackEvent ハンドラ (handle_postback 関数) 内でポストバック受信、create_postback_reply(postback) に渡す
  6. create_postback_reply(postback) が与えられたポストバックから2問目のポストバック付クイックリプライを作成し、PostbackEvent ハンドラ内で返信
  7. 以下2問目の回答受信〜5問目の問題送信も同様
  8. 5問目の回答のポストバックを受信して create_postback_reply(postback) に渡すと、関数は結果を表示するだけの TextMessage を作成し、送信して終了

ということです。つまり、 create_postback_reply(postback) 内で

  • 開始 & 1問目作成
  • 1問目受信 & 2問目作成 ~ 4問目受信 & 5問目作成
  • 5問目受信 & 結果作成

という3パターンに分けて処理が必要ということですね。この判断のために、ポストバックに question というパラメータを用意し、分岐させています。ポストバックに格納しているパラメータを再掲しておきます。

パラメータ 説明
question 前問の問題番号 0 - 5
score 前問までの合計得点 0 - 5
answer 前問の正解の選択肢 曲名
correct 前問で正解したかどうか true / false

score は、正解のボタンのポストバックのみ値が +1 されており、correct も正解のボタンのみ true になっています。answer は前問の正解を表示するためのものなので、各回ごとにどのボタンでも同じ値を保持しています。

クイズは Flex Message を使って作り、コードかなり長くなっていますのでここには載せませんが、GitHub レポジトリ で公開していますから良ければ参考にして下さい。ちなみに、TextMessage を使って作るなら

## インスタンス化
quick_reply = QuickReply(items=[])

## 各ボタンの作成
for ... :
    item = QuickReplyItem(action=PostbackAction(
        label=xxx,
        displayText=xxx,
        data=encode_postback(postback_dict)
    ))
    quick_reply.items.append(item)

## メッセージオブジェクト化
messages = [TextMessage(text=xxx, quickReply=quick_reply)]

のような感じでできるはずです。

さらなる高みへ

実は今回のバージョンで、記事には書いていない様々な機能をこっそり追加しています。詳しくはリポジトリの README を参照して下さい。

LINE Messaging API の最後の重要な機能としてリッチメニューがあるのですが、やれることはポストバックアクションだったり、URIアクションだったり、他の要素と結局は同じです。それはあくまで LINE Messaging API の範囲内なので仕方がないのですが、逆に言えば API の範囲外であればやれることはもっと増えるわけです。

そこで、次回は一気に LIFF の導入をしようと思います。これを使用することで、「入力フォームを使って検索」のようなこともできるようになり、ボットの UI/UX 向上にもなる(?)ことでしょう。その過程で、リッチメニューも導入します。

目次 : [無料][2024年版] LINE Messaging API v3 + Python(Flask) でボットを作る

GitHub レポジトリ
older_version 内に、各回時点での app.pyrequirements.txt があります。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0