LINE ChatBot 制作④「ポケモンの名前を入力すると、おすすめのヨーヨーを教えてくれる」 -メイン処理 実装編-
こんにちは、KimYasです。
前回は、 S3とCloudFrontの実装を行いました。
今回からはいよいよ、メインの処理となるコードの記述を行っていきます。
早速行きましょう!
(参考)
LINE ChatBot 制作①「ポケモンの名前を入力すると、おすすめのヨーヨーを教えてくれる」- 概要編 -
LINE ChatBot 制作②「ポケモンの名前を入力すると、おすすめのヨーヨーを教えてくれる」 -LINE Developers 登録編-
LINE ChatBot 制作③「ポケモンの名前を入力すると、おすすめのヨーヨーを教えてくれる」 -S3 実装編-
今回の実装箇所
今回は、Lambda, API Gateway, DynamoDBを実装していきます。
AWSをあまりよく知らない方のために、それぞれの役割を簡単に書くと次のようになります。
- Lambda…メイン処理を行うコンピューティングリソース。「●●というポケモンの名前が入力されたら、■■というヨーヨーの名前を返す」という処理を行う。
- API Gateway…LINEアプリ(クライアント)とLambda(サーバー)とを繋ぐ橋のようなもの。LINEアプリ上で何かしらのイベントが起こったときに、Lambdaを起動する。
- DynamoDB…NoSQL型のデータベース。151匹分のポケモン名や説明文、画像URL、ヨーヨー名などの情報を格納する。Lambdaからの要求に応じて、必要な情報を取り出してLambdaに渡す。
つまり、図の番号を用いて処理の流れを説明すると、
②LINE上でメッセージを受信すると、API GatewayがLambdaを起動する。Lambdaはレスポンス用の情報を作る処理(メイン処理)を始める。
③メッセージをもとに、Lambdaが必要な情報をDynamoDBに問い合わせる。
④DynamoDBがテーブル内から必要な情報を取り出し、Lambdaに返却する。
⑤LambdaがAPI Gatewayに対し、ステータスコード200を返す。
⑥ API GatewayがLINEプラットフォームに対し、 ステータスコード200を返す。
⑦ LambdaがReply URLに対し、 完成したレスポンス情報をPOSTリクエストで返却する。
このようになります。
今回の実装は、DynamoDB→Lambda→API Gatewayの順で行っていきます。
DynamoDB 実装
AWSコンソールからDynamoDBを選択し、画面右上の「テーブルの作成」をクリックします。
テーブル名は自由に設定します。
パーティションキーは、テーブルの項目を一意に区別できるような属性があれば、それを指定します。
今回はポケモンの名前が一意になるので、"PokemonName"とします。
ソートキーは今回は指定しませんが、パーティションキーだけでは項目を一意に区別できないような場合に指定します。
例えば、テーブル属性に「進化前ポケモン名」と「進化数」があった場合、
パーティションキーに「進化前ポケモン名」、ソートキーに「進化数」を指定すれば、両者を組み合わせることにより、項目を一意に特定できますね。
具体的に書くと、このようなイメージです。
{"進化前ポケモン名": "ゼニガメ", "進化数": 0} # ゼニガメ
{"進化前ポケモン名": "ゼニガメ", "進化数": 1} # カメール
{"進化前ポケモン名": "ゼニガメ", "進化数": 2} # カメックス
{"進化前ポケモン名": "ピカチュウ", "進化数": 1} # ライチュウ
{"進化前ポケモン名": "バリヤード", "進化数": 0} # バリヤード
設定の欄は、こだわりがなければデフォルト設定のままで大丈夫です。
デフォルトではこのような設定になっているかと思います。
読み込み/書き込みキャパシティーはDynamoDBの料金に影響してきますが、プロビジョンドキャパシティモードならば、無料利用枠で25WCU及び25RCUを使用できます(2022年2月時点)。
他にDynamoDBのテーブルを立ち上げていなければ、デフォルトは5WCU及び5RCUなので無料利用枠内に収まります。
無料利用枠は変更になる可能性がありますので、詳細は公式ページをご覧ください。
このまま画面を下までスクロールし、右下にある「テーブルを作成」をクリックします。
テーブルを作成できたら、画面右上にある「項目を作成」ボタンをクリックします。
テーブルに追加したい項目の属性と値をJSON形式で入力します。
入力できたら、画面右下の「項目を作成」ボタンをクリックします。
これを151回繰り返すことで、151匹分のポケモンデータをDynamoDBに登録できます。気合で頑張りましょう!
…というのは大変すぎますよね。
何かいい方法は無いかなあと調べてみると、あっさり解決方法が見つかりました。
AWS公式ブログに、CSVファイルをDynamoDBへ一括で取り込む方法が掲載されていましたので、その手順に従いました。
また私の方でも手順をまとめ、近々記事をアップしたいと思います。
ということで、テーブルが完成しました。
151匹分のコメントを書くのはAWSの環境構築以上に時間がかかりましたが、楽しかったので辛さはゼロでした。
所々に深夜テンションで書いたコメントが紛れているので、よかったら探してみてください。
(完成版のLINE ChatBotには、こちらの記事のリンクから友だち登録できるので是非どうぞ!)
DynamoDBの実装はここまでです。
続いて、Lambdaを実装していきましょう。
Lambda実装
AWSコンソールからLambdaを選択します。
画面右上にある、「関数の作成」をクリックします。
関数名は好きなように入力します。
また、今回はPythonでコードを書いていくので、ランタイムはPython3.9を選びました。
アーキテクチャはデフォルトのx86_64ままでOKです。
画面右下にある、「関数の作成」をクリックします。
Lambda関数が作成できました。
作成した関数をクリックして開きます。
最初から「コード」タブが選択されており、lambda_function.pyというファイルが作成されています。
画面中央にエディタ画面があるので、ここにコードを書いていきます。
import json
import os
import urllib.request
import boto3
import logging
import random
logger = logging.getLogger()
logger.setLevel(logging.INFO)
LINE_CHANNEL_ACCESS_TOKEN = os.environ['LINE_CHANNEL_ACCESS_TOKEN']
CLOUDFRONT_DISTRIBUTION_URL = os.environ['CLOUDFRONT_DISTRIBUTION_URL']
REPLY_URL = os.environ['REPLY_URL']
KIMYAS_PROFILE_URL= os.environ['KIMYAS_PROFILE_URL']
def lambda_handler(event, context):
for message_event in json.loads(event['body'])['events']:
#イベントからユーザーの入力文字列を取得
input_word = message_event['message']['text'] # ここでのエラーはAPIGatewayで弾く
logger.info([input_word,message_event['source']['userId']])
# dynamoDBからデータ取得し、タプルで変数に格納
data_tuple = get_data_from_dynamoDB(input_word)
# レスポンス用のheaderとbodyを作成(必要事項はLINEの公式ドキュメントで定義されている)
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN # Bearerの直後には空白が必要(仕様)
}
body = {
'replyToken': message_event['replyToken'],
'messages': [
{
"type": "template",
"altText": data_tuple[4],
"template": {
"type": "buttons",
"thumbnailImageUrl": data_tuple[0],
"imageAspectRatio": "square",
"imageSize": "contain",
"imageBackgroundColor": "#FFFFFF",
"title": data_tuple[3] + "-" + data_tuple[4],
"text": data_tuple[1],
"defaultAction": {
"type": "uri",
"label": "詳細ページ",
"uri": data_tuple[2]
},
"actions": [
{
"type": "uri",
"label": "もっと詳しく!",
"uri": data_tuple[2]
},
{
"type": "message",
"label": data_tuple[5] + "を調べてみる",
"text": data_tuple[5]
},
{
"type": "uri",
"label": "このBotの開発者について",
"uri": KIMYAS_PROFILE_URL
}
]
}
}
]
}
req = urllib.request.Request(REPLY_URL, data=json.dumps(body).encode('utf-8'), method='POST', headers=headers) # HTTPリクエストを作成
with urllib.request.urlopen(req) as res: # 引数のreq(URL)をオープン(=HTTPリクエストを実行)。返り値resには、HTTPResponseクラスのオブジェクトが返送される。
logger.info(res.read().decode("utf-8")) # 成功ならステータスコード200と空のJSONオブジェクト{}が返ってくる(LINEドキュメントより)
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
def get_data_from_dynamoDB(input_word):
dynamoDB = boto3.resource('dynamodb')
table = dynamoDB.Table('テーブル名') #'テーブル名'には、DynamoDBのテーブル名を入れる
target_item = table.get_item(Key={'PokemonName': input_word})['Item']
# target_itemの各値を取り出して変数に格納
s3_url = CLOUDFRONT_DISTRIBUTION_URL + target_item['S3ObjectName']
yoyo_maker = target_item['YoyoMaker']
yoyo_item = target_item['YoyoItem']
comment = target_item['Comment']
rewind_url = target_item['RewindURL']
next_pokemon = target_item['NextPokemon']
return(s3_url, comment, rewind_url, yoyo_maker, yoyo_item, next_pokemon)# 要素の順番を替える場合は、メイン処理のタプル順も編集すること
コード内に出てくる環境変数は、「設定」タブの「環境変数」で設定できます。
他人に知られたくない情報や、将来的に変更の可能性があるURL等の文字列は、ここに格納しましょう。
セキュリティ及びコードの保守性が向上します。
12行目の"CLOUDFRONT_DISTRIBUTION_URL"には、ヨーヨーの画像を格納しているS3バケットに紐づいている、CloudFrontのディストリビューション名が入っています。つまるところ、86行目が
s3_url = "ヨーヨーの画像URL"
となるように記述できれば、環境変数を用いなくてもOKです。
また、11行目の"LINE_CHANNEL_ACCESS_TOKEN"には、前回の記事で登録したLINE Developersのチャネルアクセストークンを格納します。
LINE Developers にログインし、作成したチャネルのMessaging API設定からチャネルアクセストークンをコピーし、Lambdaの環境変数に設定しましょう。
(詳しくは、LINE Developers登録編の記事をご覧ください。)
Lambda実装はここまでです。
続いて、API Gatewayを実装します。
API Gateway 実装
AWSコンソールからAPI Gatewayを選択し、画面右上の「APIを作成」をクリックします。
今回はREST APIで作成したいので、REST APIの「構築」ボタンをクリックします。
任意のAPI名を入力します。
エンドポイントタイプはデフォルトの「リージョン」のままでOKです。
画面右下の「APIの作成」ボタンをクリックします。
画面左メニューバーの「リソース」を開き、画面上部「アクション」から「リソースの作成」をクリックします。
任意のリソース名を入力します。ここでは"sample"としました。
画面右下の「リソースの作成」をクリックします。
作成したリソースを選択した状態で、画面上部の「アクション」から「メソッドの作成」をクリックします。
ドロップダウンリストからPOSTを選択し、リスト右横のチェックマーク✅をクリックしてメソッドを作成します。
作成したPOSTメソッドを選択して、設定を行います。
「統合タイプ」はLambda関数を選択します。
「Lambdaプロキシ統合の使用」のチェックボックスはデフォルトで入っていないので、チェックします。
「Lambda関数」には、作成したLambda関数のARNまたは関数名を入力します。
「デフォルトタイムアウトの使用」はチェックを入れたままでOKです。
画面右下の「保存」ボタンをクリックします。
このような画面が出るので、OKをクリックします。
(参考)
ここでOKを押したことにより、Lambdaに次のようなリソースベースポリシーが追加されます。
興味のある方は、作成したLambda関数の「設定」→「アクセス権限」→「リソースベースのポリシー」→「ポリシードキュメントを表示」を確認してみてください。
{
"Version": "2012-10-17",
"Id": "default",
"Statement": [
{
"Sid": "xxxxxxxx-1111-2222-3333-44444444444",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:ap-northeast-1:111111111111:function:hoge_function",
"Condition": {
"ArnLike": {
"AWS:SourceArn": "arn:aws:execute-api:ap-northeast-1:111111111111:abcdefghijk/*/POST/hoge"
}
}
}
]
}
少し脱線しましたが、API Gatewayに戻ります。
画面上部の「アクション」から「APIのデプロイ」をクリックします。
このような画面が出るので、「デプロイされるステージ」は「新しいステージ」を選択します。
「ステージ名」に任意の名前を入力します。ここではtest_stageとしました。
画面右下の「デプロイ」をクリックします。
デプロイが完了しました。
画面上部にある「URLの呼び出し」に表示されているURLが、このAPI Gatewayに外部からアクセスする際のURLあたります。
つまり、このURLをLINEアプリのWebhook URLに指定することで、LINEアプリとAPIGatewayとを繋ぐことができます。
ということで、このURLをコピーし、LINE Developersの管理画面を開いてWebhookURLに設定します。
※LINE Developersについてはこちらの記事で詳しく解説しています。
以上でAPI Gatewayの設定は完了です。
これでChatBotが動作するための設定が一通り完了しました。
ここまでお疲れ様でした!
動作確認
トークルームでポケモンの名前を入力すると、おすすめのヨーヨーが画像と共に返ってきます。初めてエラー無く動いた瞬間は感動しました。
1回目のレスポンスには1秒強ほどかかりますが、2回目からは1秒以内に返ってきますね。
これはLambdaのコールドスタートという仕様であり、初回起動時に実行環境を構築する分どうしても時間を要します。
対策はいくつかあり、オーソドックスなのはLambdaの同時実行数をプロビジョニング(確保)しておくことです。
別料金が発生するので、どの程度の遅延までなら許されるのか考慮して、予算が許されるのであれば導入してみると良いですね。
今回は個人利用の範囲なので、このままで十分だと考えています。
また、青紫色のヨーヨーの写真では、トークルーム下部にメニューボタンがありますね。これはLINEのリッチメニューという機能で、実装することでユーザーが入力しやすい画面を作ることができます。
この作り方については、また記事をアップしたいと思います。
まとめ
ここまで4回にわたってChatBotの制作過程を解説してきました。
上図のうちLightsail(DNS Zone)とCertigicate Managerはまだ導入していませんが、一通りChatBotとして動作する機能は実装しました。
Lightsail(DNS Zone)とCertigicate Managerについては番外編という形で、スポット的に解説をしていきたいと思います。
AWSをバックグラウンドにしたChatBot制作は大変な分、完成したときには大きな達成感を感じられます。
また、本やネット上の解説を読んでAWSの勉強をするだけでなく、コンソールを触ったり構成図を書くなど実際に手を動かすことで、AWSのサービスに対する理解が格段に深まりました。
これからAWSのサービスを使ってみたいという方は、是非ChatBot制作をしてみてはいかがでしょうか。
私の記事が参考になれば幸いです。
それでは次の記事でお会いしましょう!
KimYas