こんにちは。技術4課の河野です。
今回は、Serverless Framework を使用してLINEBot を作成する方法について紹介します。
作成するもの
LINEBot 入門編では定番の「おうむ返しBot」を作成していきます。
構成図
以下のようなAWSサービスを使用してLINEBotを実現します。
①:利用者がLINEからメッセージを送信すると、LINEプラットフォームでWebhookイベントが発生します。
Webhookには、メッセージ内容やメッセージを返信するために使用するreplyToken
などが含まれます。
オブジェクトの詳細は公式ドキュメントに記載されています。
②:Webhook のリクエストは、Amazon API Gateway(以下API Gateway) のエンドポイントに送信されます。
③:API Gateway は Webhook の HTTP リクエストを受けて、AWS Lambda(以下Lambda) を発火します。
④:Lambda は Webhook のオブジェクト内容をそのまま Amazon SQS(以下SQS) にキューイングし、即座にLINEプラットフォーム側にステータス200を返却します。
⑤:SQSにメッセージがキューイングされたタイミングで、後続の Lambda が発火します。
⑥:Lambda は SQSのメッセージ内容を取得し、LINEのトーク画面に返信します。
メッセージの受信と送信は、SQSを使用して非同期構成にしている点がポイントです。
公式ドキュメントも非同期構成を推奨されています。
HTTP POSTリクエストの処理が後続のイベントの処理に遅延を与えないよう、イベント処理を非同期化することを推奨します。
また、法人向け開発ガイドラインにも以下の記載があります。
ボットサーバーでリクエストを受信したら、まずは迅速にステータスコード200を返却するようにしてください。 ステータスコードの返却は、1000ミリ秒(1秒)以内が目安です。1秒を超過した場合はエラーメールが自動で送信されます。
環境
- LINE Messaging API Channel は作成済とします。
- 作成方法はMessaging APIを始めように記載されています。
- Python 3.8.2
- pipenv
- aws-cli 1.18.3
- serverless Framework Core 1.81.1
実装
Serverless プロジェクトの作成
Serverless Framework を使用して、構成図で示したリソースをプロビジョニングします。
serverless create
でServerless プロジェクトを作成します。
serverless create --template aws-python3 --path sls-line-oumu-app
Serverless Framework で使用するプラグインをインストールします。
serverless plugin install --name serverless-python-requirements
serverless.yml
を開き、以下のように編集します。
service: line-oumu-bot provider: name: aws runtime: python3.8 region: ap-northeast-1 stage: ${opt:stage, self:custom.defaultStage} timeout: 60 environment: LINE_CHANNEL_SECRET: XXXXXXXXXX LINE_CHANNEL_ACCESS_TOKEN: XXXXXXXXX plugins: - serverless-python-requirements custom: defaultStage: dev package: exclude: - .mypy_cache/** - .venv/** - node_modules/** - .gitignore - package-lock.json - package.json - Pipfile - Pipfile.lock functions: enqueue: handler: bot_enqueue.lambda_handler description: lineからのwebhookをsqsにキューイングします timeout: 60 memorySize: 256 role: BotEnqueueRole environment: QUEUE_URL: Ref: BotJobQueue events: - http: path: / method: post response: headers: Content-Type: "'application/json'" template: $input.path('$') # カスタムレスポンスコードの設定 statusCodes: 200: pattern: '' template: $input.path("$.body") execute: handler: bot_execute.lambda_handler description: sqsメッセージを解析して、LINEに返信します(line-bot-sdk-python) timeout: 60 memorySize: 256 role: BotExecuteRole environment: QUEUE_URL: Ref: BotJobQueue events: - sqs: arn: !GetAtt BotJobQueue.Arn batchSize: 1 resources: Resources: BotJobQueue: Type: AWS::SQS::Queue Properties: KmsMasterKeyId: alias/aws/sqs MessageRetentionPeriod: 72000 Tags: - Key: AppName Value: ReserveBot VisibilityTimeout: 60 BotEnqueueRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: enqueuePolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - sqs:SendMessage - sqs:SendMessageBatch Resource: - !GetAtt BotJobQueue.Arn BotExecuteRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: executePolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - sqs:ReceiveMessage - sqs:DeleteMessage - sqs:DeleteMessageBatch - sqs:GetQueueAttributes Resource: - !GetAtt BotJobQueue.Arn
LINE_CHANNEL_SECRET
とLINE_CHANNEL_ACCESS_TOKEN
は作成したChannel画面から参照して記載してください。
Lambda(メッセージ受信)を作成
まずは、必要なライブラリをインストールします。
pipenv install boto3
bot_enqueue.py
を新規で作成し、以下のように編集します。
import os import boto3 import json import logging sqs = boto3.client('sqs') queue_url = os.getenv('QUEUE_URL') logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # Lambda Response ok_response = { "isBase64Encoded": False, "statusCode": 200, "headers": {}, "body": "" } error_response = { "isBase64Encoded": False, "statusCode": 401, "headers": {}, "body": "" } def bot_job_enqueue(sqs_client, target_queue_url, message_body): json_str = json.dumps(message_body) sqs_client.send_message( QueueUrl=target_queue_url, MessageBody=json_str ) def lambda_handler(event, context): try: logger.info('event: %s', event) bot_job_enqueue(sqs, os.getenv('QUEUE_URL'), event) except Exception as e: logger.info(e) return error_response return ok_response
Lambda(メッセージ送信)を作成
まずは、必要なライブラリをインストールします。
pipenv install line-bot-sdk
bot_execute.py
を新規で作成し、以下のように編集します。
import os import json import boto3 import logging from linebot import LineBotApi, WebhookHandler from linebot.exceptions import InvalidSignatureError from linebot.models import MessageEvent, TextMessage, TextSendMessage 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(MessageEvent, message=TextMessage) def handle_message(event): line_bot_api.reply_message( event.reply_token, TextSendMessage(text=event.message.text) ) 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.error(e) return error_response return ok_response
デプロイ
上記で作成したリソースをAWSにデプロイします。
serverless deploy --stage dev
Service Information
service: line-oumu-bot
stage: dev
region: ap-northeast-1
stack: line-oumu-bot-dev
resources: 16
api keys:
None
endpoints:
POST - https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/
functions:
enqueue: line-oumu-bot-dev-enqueue
execute: line-oumu-bot-dev-execute
layers:
None
エンドポイントhttps://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/
をWebhookURLに設定します。
「vertify」を押して、Successが出ればOKです。
動作確認
LINEでQRコードから友達追加し、メッセージを送ってみます。
おうむ返しになっていますね!
さいごに
今回は、入門編ということで「おうむ返しBot」を紹介しました。
次回は、よりインタラクティブな振る舞いを目指して、
トーク上でボタンを表示したり、ボタンの選択によってメッセージを変えたりする方法について紹介できればと思います。