こんにちは。技術4課の河野です。
今回は、LINEBot 入門編第二弾となります。
入門編第一弾では、「おうむ返し」Botを紹介しました。
第二弾ではよりインタラクティブなBotを目指して、じゃんけんBotを紹介します。
※当記事は、【入門編①】Serverless Framework で 「おうむ返し」LINE Bot を作るを 実施した前提で進めていきます。
作成するもの
下図のイメージ通りで、Bot とじゃんけん対決できます。
構成図
構成図は、前回の記事で紹介したものと変わっていません。
本記事では、構成図の「AWS Lambda with line-bot-sdk-python」 を変更していきます。
環境
- Python 3.8.2
- pipenv
- serverless Framework Core 1.81.1
実装
各メッセージ処理ごとに解説していきます。
ソースの全容は最後に載せているので、とにかく動かしてみたい方はそちらをご参照ください。
「最初はぐー、」メッセージを表示
まず、このメッセージを表示させるイベント(メッセージを表示させるタイミング)について説明します。
- FollowEvent
- ユーザーが友達登録した際に発生するイベントです。
- 通常は「友達登録ありがとうございます...」などの挨拶文やBotの使用方法などを記載しますが、今回はいきなりじゃんけんを仕掛けるようにします。
- MessageEvent
- ユーザーがテキストを送信した際に発生するイベントです。
- 「ぐー」「ちょき」「ぱー」以外のメッセージが送信された場合に表示します。
- default
- どのイベントにも属さない場合に発生するイベントです。
- line-bot-sdk-python で独自に用意されているイベントハンドラーになります。
イベントの詳細については、公式ドキュメントをご参考ください。
line-bot-sdk-python
では、それぞれのイベントに該当するイベントハンドラーが用意されています。イベントハンドラーは@handler.add(arg1, arg2)
のデコレータを追加することで、簡単に実装することができます。
第一引数にイベントの種類、第二引数は、第一引数がMessageEvent
の場合、そのメッセージの種類(テキスト、写真、スタンプ等)を指定します。指定しない場合は、全てのメッセージの種類が該当することになります。
イベントとメッセージ一覧は、Github をご参照ください。
まずは、メッセージを表示するJSONファイルを作成します。
saisyohaguu_message.json
を新規で作成し、下記のように編集します。
{ "type": "bubble", "direction": "ltr", "body": { "type": "box", "layout": "vertical", "contents": [ { "type": "text", "text": "最初はぐー、じゃんけん、", "align": "center" } ] }, "footer": { "type": "box", "layout": "horizontal", "contents": [ { "type": "button", "action": { "type": "message", "label": "ぐー", "text": "ぐー" } }, { "type": "button", "action": { "type": "message", "label": "ちょき", "text": "ちょき" } }, { "type": "button", "action": { "type": "message", "label": "ぱー", "text": "ぱー" } } ]} }
bot_execute.py
を開いて、下記内容を追記します。
from linebot.models import MessageEvent, TextMessage, TextSendMessage, FollowEvent, FlexSendMessage @handler.add(FollowEvent) def handle_follow(event): with open('./saisyohaguu_message.json') as f: saisyohaguu_message = json.load(f) line_bot_api.reply_message( event.reply_token, FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message) ) @handler.default() def default(event): with open('./saisyohaguu_message.json') as f: saisyohaguu_message = json.load(f) line_bot_api.reply_message( event.reply_token, FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message) )
フォローイベントとデフォルトイベントを実装しました。メッセージイベントは、じゃんけん応答メッセージの表示で合わせて実装します。
「じゃんけんの応答メッセージ」の表示
じゃんけんの応答メッセージでは、以下のメッセージを同時に返信します。
- Bot側の手
- 勝利判定
- スタンプ
- あいこ
- 次のじゃんけん(最初はぐー)
aikode_message.json
を新規で作成し、下記のように編集します。
{ "type": "bubble", "direction": "ltr", "body": { "type": "box", "layout": "vertical", "contents": [ { "type": "text", "text": "あいこで", "align": "center" } ] }, "footer": { "type": "box", "layout": "horizontal", "contents": [ { "type": "button", "action": { "type": "message", "label": "ぐー", "text": "ぐー" } }, { "type": "button", "action": { "type": "message", "label": "ちょき", "text": "ちょき" } }, { "type": "button", "action": { "type": "message", "label": "ぱー", "text": "ぱー" } } ]} }
bot_execute.py
を開いて、下記内容を追記します。
from linebot.models import MessageEvent, TextMessage, TextSendMessage, FollowEvent, FlexSendMessage, StickerSendMessage @handler.add(MessageEvent, message=TextMessage) def handle_message(event): request_message = event.message.text bot_answer = random.choice(['ぐー', 'ちょき', 'ぱー']) with open('./saisyohaguu_message.json') as f: saisyohaguu_message = json.load(f) with open('./aikode_message.json') as f: aikode_message = json.load(f) reply_messages = [] win_reply_message = [TextSendMessage(text='私の勝ちです')] win_reply_message.append(StickerSendMessage(package_id='1', sticker_id=random.choice(['106', '407', '125', '100', '110']))) win_reply_message.append(FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message)) lose_reply_message = [TextSendMessage(text='私の負けです')] lose_reply_message.append(StickerSendMessage(package_id='2', sticker_id=random.choice(['152', '18', '25', '173', '524']))) lose_reply_message.append(FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message)) draw_reply_message = [FlexSendMessage(alt_text='あいこで', contents=aikode_message)] if request_message == 'ぐー': reply_messages.append(TextSendMessage(text=bot_answer)) if bot_answer == 'ぐー': reply_messages.extend(draw_reply_message) elif bot_answer == 'ちょき': reply_messages.extend(lose_reply_message) elif bot_answer == 'ぱー': reply_messages.extend(win_reply_message) elif request_message == 'ちょき': reply_messages.append(TextSendMessage(text=bot_answer)) if bot_answer == 'ぐー': reply_messages.extend(win_reply_message) elif bot_answer == 'ちょき': reply_messages.extend(draw_reply_message) elif bot_answer == 'ぱー': reply_messages.extend(lose_reply_message) elif request_message == 'ぱー': reply_messages.append(TextSendMessage(text=bot_answer)) if bot_answer == 'ぐー': reply_messages.extend(lose_reply_message) elif bot_answer == 'ちょき': reply_messages.extend(win_reply_message) elif bot_answer == 'ぱー': reply_messages.extend(draw_reply_message) else: reply_messages.append(FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message)) line_bot_api.reply_message(event.reply_token, reply_messages)
ロジックの説明は省きますが、以下補足します。
reply_message
は同時に最大5件まで送信できます。送信時は、リストにメッセージオブジェクトを入れる必要があります。- スタンプは、IDの組み合わせを指定して送信します。組み合わせ表はこちらを参照しました。
動作確認
デプロイして、動作を確認します。
sls deploy --stage dev
さいごに
これで入門編は以上になります。 次回は外部のAPIを使って、より実用的なBotを紹介できればと思います。
以下、bot_execute.py
の全容です。
import os import json import boto3 import logging import random from linebot import LineBotApi, WebhookHandler from linebot.exceptions import InvalidSignatureError from linebot.models import MessageEvent, TextMessage, TextSendMessage, FollowEvent, FlexSendMessage, StickerSendMessage channel_secret = os.getenv('LINE_CHANNEL_SECRET') channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN') logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # LineBotAPI line_bot_api = LineBotApi(channel_access_token) handler = WebhookHandler(channel_secret) sqs = boto3.client('sqs') queue_url = os.getenv('QUEUE_URL') # Lambda Response ok_response = { "isBase64Encoded": False, "statusCode": 200, "headers": {}, "body": "" } error_response = { "isBase64Encoded": False, "statusCode": 401, "headers": {}, "body": "" } @handler.add(FollowEvent) def handle_follow(event): with open('./saisyohaguu_message.json') as f: saisyohaguu_message = json.load(f) line_bot_api.reply_message( event.reply_token, FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message) ) @handler.add(MessageEvent, message=TextMessage) def handle_message(event): request_message = event.message.text bot_answer = random.choice(['ぐー', 'ちょき', 'ぱー']) with open('./saisyohaguu_message.json') as f: saisyohaguu_message = json.load(f) with open('./aikode_message.json') as f: aikode_message = json.load(f) reply_messages = [] win_reply_message = [TextSendMessage(text='私の勝ちです')] win_reply_message.append(StickerSendMessage(package_id='1', sticker_id=random.choice(['106', '407', '125', '100', '110']))) win_reply_message.append(FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message)) lose_reply_message = [TextSendMessage(text='私の負けです')] lose_reply_message.append(StickerSendMessage(package_id='2', sticker_id=random.choice(['152', '18', '25', '173', '524']))) lose_reply_message.append(FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message)) draw_reply_message = [FlexSendMessage(alt_text='あいこで', contents=aikode_message)] if request_message == 'ぐー': reply_messages.append(TextSendMessage(text=bot_answer)) if bot_answer == 'ぐー': reply_messages.extend(draw_reply_message) elif bot_answer == 'ちょき': reply_messages.extend(lose_reply_message) elif bot_answer == 'ぱー': reply_messages.extend(win_reply_message) elif request_message == 'ちょき': reply_messages.append(TextSendMessage(text=bot_answer)) if bot_answer == 'ぐー': reply_messages.extend(win_reply_message) elif bot_answer == 'ちょき': reply_messages.extend(draw_reply_message) elif bot_answer == 'ぱー': reply_messages.extend(lose_reply_message) elif request_message == 'ぱー': reply_messages.append(TextSendMessage(text=bot_answer)) if bot_answer == 'ぐー': reply_messages.extend(lose_reply_message) elif bot_answer == 'ちょき': reply_messages.extend(win_reply_message) elif bot_answer == 'ぱー': reply_messages.extend(draw_reply_message) else: reply_messages.append(FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message)) line_bot_api.reply_message(event.reply_token, reply_messages) @handler.default() def default(event): with open('./saisyohaguu_message.json') as f: saisyohaguu_message = json.load(f) line_bot_api.reply_message( event.reply_token, FlexSendMessage(alt_text='最初はぐー', contents=saisyohaguu_message) ) def lambda_handler(event, context): try: for record in event['Records']: record_body = json.loads(record['body']) signature = record_body["headers"]["X-Line-Signature"] event_body = record_body['body'] handler.handle(event_body, signature) except InvalidSignatureError as e: logger.error(e) return error_response except Exception as e: logger.info({'error': e}) return error_response return ok_response