はじめに
今回はLINE APIを利用した「S3に写真保存」botの作成をしていきます。
動きとしてはLINEのMessaging APIを利用して写真を送信します、写真をきっかけとしてAWS API GatewayがLambdaを起動させて、Lambdaに記述されているコードの動作(S3に写真を保存して保存された場合返信する)を行います
制作意図
9月に結婚式を挙げた友人用に考えた構築のハンズオンです(一部異なる点ありますが、ほぼほぼ)。
結婚式当日に参加した人がQRコードからLINEのお友達登録をしてもらうことで、当日撮影した写真を枚数・保存期間を気にせずS3に投げ込み、別途構築した2人のオリジナルHPから投げ込んだ写真を全員が閲覧できることを考えました。ちなみに後日でも、LINEから写真を投げるだけでOK。
実際の構築では動画・メッセージも送れるようにしていましたが、今回は写真だけに絞ってハンズオンしていきます。
構成図
実装
1.LINE Developersでプロバイダーの登録(省略)
1.1 前回記事『AWS Lambdaを利用したLINEbotハンズオン〜鸚鵡返し〜』における『1.LINE Developersでプロバイダーの登録』部分を参照ください
AWS Lambdaを利用したLINEbotハンズオン〜鸚鵡返し〜
1.2 作成完了画面
2.作業ディレクトリ作成(ローカル環境)
LINE公式ドキュメントより、LINE APIのSDK(ソフトウェア開発キット)のPythonをダウンロードする
※注意書きにもあるようにダウンロードは必須ではありませんが、構築が容易になるので、このQiita記事ではダウンロードしています
2.1 作業ディレクトリ作成とSDKインストール
tetutetu214@mbp projects % mkdir 20211117_S3_SavePhotos
tetutetu214@mbp projects % cd 20211117_S3_SavePhotos
tetutetu214@mbp 20211117_S3_SavePhotos % python -m pip install line-bot-sdk -t .
2.2 boto3をインストール
AWS S3のファイルを操作するために『boto3』をインストールをしていきます
tetutetu214@mbp 20211117_S3_SavePhotos % pip install boto3
2.3 「lambda_function.py」ファイルを作成する
フォルダのなかに下記ファイル「lambda_function.py」を作成する
※このファイル名以外だと読み込まれないので注意する
import os
import sys
import logging
# boto3を利用できるようにimport
import boto3
from linebot import (LineBotApi, WebhookHandler)
from linebot.models import (MessageEvent, ImageMessage,TextSendMessage)
from linebot.exceptions import (LineBotApiError, InvalidSignatureError)
logger = logging.getLogger()
logger.setLevel(logging.ERROR)
# 1.LINEBOTと繋げるための記述
# 環境変数からline botのチャンネルアクセストークンとシークレットを読み込む
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
# 無いならエラー
if channel_secret is None:
logger.error('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is None:
logger.error('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)
# apiとhandlerの生成(チャンネルアクセストークンとシークレットを渡す)
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
######################################################
# boto3を利用してS3連携
# バケット名は各自命名したものを利用する
s3 = boto3.client("s3")
bucket = "XXXXXXXXXXXX"
######################################################
# 2.イベントからLINEBOT署名とボディ内容を受け取る
# Lambdaのメインファンクション
def lambda_handler(event, context):
# 検証用のx-line-signatureヘッダー
signature = event["headers"]["x-line-signature"]
body = event["body"]
# リターン値の設定
ok_json = {"isBase64Encoded": False,
"statusCode": 200,
"headers": {},
"body": ""}
error_json = {"isBase64Encoded": False,
"statusCode": 403,
"headers": {},
"body": "Error"}
# 画像保存(messageがImageMessageの場合の挙動)
@handler.add(MessageEvent, message=ImageMessage)
def message(line_event):
# メッセージIDを抽出
message_id = line_event.message.id
# 画像ファイルを抽出(画像が大きい場合分割する)
message_content = line_bot_api.get_message_content(message_id)
content = bytes()
for chunk in message_content.iter_content():
content += chunk
# 保存する画像ファイル名
key = "2021/" + message_id + '.jpg'
new_key = message_id[-3:]
s3.put_object(Bucket=bucket, Key=key, Body=content)
# 画像保存するとメッセージの返信
line_bot_api.reply_message(line_event.reply_token,TextSendMessage(text='写真の保存に成功!'))
# 例外処理としての動作
try:
handler.handle(body, signature)
except LineBotApiError as e:
logger.error("Got exception from LINE Messaging API: %s\n" % e.message)
for m in e.error.details:
logger.error(" %s: %s" % (m.property, m.message))
return error_json
except InvalidSignatureError:
return error_json
return ok_json
2.4 ファイルをzip化する
zipの名前は自分で管理しやすいようにネーミングする
3.S3の設定(AWSコンソール)
3.1 S3バケットの作成
1 S3コンソール画面よりバケットの作成を押下する
2 設定をしていく(今回はバケット名のみ記述してデフォルト)
3 バケットが作成されたのを確認する
※前後していますが『2.3 「lambda_function.py」ファイルを作成する』ファイル内のバケット名は、こちらの手順で作成したバケットのことです。
4.Lambdaの設定(AWSコンソール)
1.前回記事『AWS Lambdaを利用したLINEbotハンズオン〜鸚鵡返し〜における『3.Lambdaの設定(AWSコンソール)の登録』』
部分を参照ください
※LINEとの環境変数まで設定を終えている状態
5.AWS API Gatewayを設定する
LINEからのWebhook(イベント発生時、指定したURLにPOSTリクエストで写真データを送信する仕組み)を受け取るためにAPI Gatewayを設定する
5.1 トリガーを作成する
2.トリガーの設定__で「API Gateway」__を選択する
APIタイプは__REST API__で、セキュリティは__オープン__を選択する
※__「HTTP API」の方で構築したかったのですが、設定の方法が分からなかったため「REST API」__を選択しています
5.2 構築が完了したことを確認する
5.3 API Gatewayの設定
4.セットアップ
4-1.Lambdaプロキシにチェック(API Gateway側でパラメータをよしなにしてくれる程度の知識)
4-2.__Lambda関数__ですが遷移直後は、同リージョンにLambda関数がありませんとコメントが出たので、関数のARNから値をいれたら関数を選ぶことができるようになりました
5.4 API Gateway メソッドリクエストの設定
2.『メソッドリクエスト』の設定をしていく
2-1.Messaging APIリファレンスに従って、リクエストの検証をプルダウンから選択して保存する
2-2.『HTTPリクエストヘッダー』に署名の文言を入力
※保存等のボタンはなく左上『メソッドの実行』から戻ります
5.5 API のデプロイをする
3.自動的に『ステージ』へ遷移して、デプロイしていることが確認できる
6.Lambdaの詳細設定(AWSコンソール)
6.1 API Gateway作成中に作られたAnyのAPI Gateway削除
※きっと良い方法があるはずですが解決しなかったので、作ってから削除しました
6.2 APIからリソースを操作するためのロール作成
1.『設定』の『アクセス制限』から『ロール名』を押下してIAMへ遷移
3.API GatewayからAPI操作を呼び出すための制御に関しての権限をLambdaに付与する
4.LambdaからS3にオブジェクトを保存するための権限をLambdaに付与する
7.LINE DevelopersにAWSの設定を登録する
7.1 作成されたAPIエンドポイントへのURLをLINE Developersに登録をする
7.2 WebhookにURLをCOPYする
LINE Developersの「Messaging API設定」の「Webhook設定」にAPI Gatewayの「APIエンドポイント」のURLをCOPYする
COPYして__更新__後に表示される、__Webhookの利用__をON(緑色)にする
7.3「あいさつメッセージ」の「編集」を押下して、詳細設定の__
__「応答メッセージ」を「オフ」にして、「Webhook」を「オン」__にする
8 挙動の確認
8.1 QRコードから友達登録の遷移
QRコードを読み取ると、お友達登録の画面へ遷移して登録することができる
8.2 LINE画面挙動の確認
LINE画面でメッセージを送信して、写真の保存が成功すると『写真の保存に成功!』というメッセージが返される
8.3 S3に画面が保管されているかの確認
作成しているバケットに写真が保存されていることがタイムスタンプからも確認できます
8.4 CloudWatchロググループでも確認する
1.IAM権限の際に触れませんでしたが、LambdaにCloudWatchの権限も付与されているので、CloudWatchでログの確認をすることができます
2.ログイベントで2度LINE APIを利用して送付して保存されていることが確認できます
9.CloudWatchロググループでエラーの挙動を確認する
1.こちらは作成中に表示されたエラーです、『PutObject』などから権限がないのかなと考えIAM周辺を確認するきっかけとなりました
最後に
長丁場になりましたが、なんとかLINEから写真を送信してS3へ画像を保存することは出来ました。
『まだまだ分からない、なんとなくやってしまっている』部分も多々ありますが、作っていかなければ理解も出来ないかと思って構築しています。「ベストプラクティは「こうじゃ!」」「ちょっ、セキュアじゃねぇよ!」などありお時間があれば、なんとも厚顔無恥なお願いですがご指摘お願いします。
参照
Qiita記事
Boto3 で S3 のオブジェクトを操作する(高レベルAPIと低レベルAPI)
API Gateway + Lambda プロキシ結合の使用有無による違い
白いマスクから怪人マスクへ、AWSでサーバレスLINE写真処理アプリの開発記
ドキュメント
Boto3 ドキュメント