serverless framworkで画像認識して関係する映画を推薦するSlack Botを作ったまとめ (No Server November Challenge)
serverless framworkで画像認識して関係する映画を推薦するSlack Botを作ったまとめ (No Server November Challenge):
つくったものはGitHub - jagijagijag1/animal-recog-slack-botで公開
- serverless frameworkを開発しているServerless, Inc.の企画で,No Server Novemberが開催中
- 11月中に毎週課題が出される
Challenges that are designed to help experienced users level up, and brand new users get started
- githubリポジトリのリンクを#noServerNovemberをつぶやくとなにか(official Serverless swag)もらえる?
-
Nov 12の課題であるAnimalBotとNov 19の課題であるSlack botを組み合わせて開発してみたので内容の紹介
- Nov 12 AnimalBot: 画像URLをメンションすると写っている動物を返信するTwitter Botを作る課題
- Nov 19 Slack bot:
/action
とすると80年台アクション映画をランダムに教えてくれるSlack Botを作る課題
作ったもの
- Slack上でBotに画像URLをメンションすると,写っている内容と,関連する映画を教えてくれるシステム
- 画像認識にはAmazon Rekognitionを利用
- 映画情報はThe Movie DatabaseからAPI経由で取得
結果
- 猫の画像を送ると,猫が写っていることと,関連映画として魔女の宅急便を教えてくれた��
環境
- MacOS Mojave
- Python 3.6.5
- Serverless Framework 1.32.0
つまづきメモ
- [Lambdaプロキシ] POSTリクエスト本体はevent.bodyの中にStringではいる(JSONじゃない!)ので,event.body配下を再度JSONとして読み込み直す必要あり
def main(event, context): body_str = event['body'] ## これだとただの文字列 body_json = json.loads(event['body']) ## これでJSONとして扱える ...
- Lambda + Pythonで画像処理ライブラリPillow(PIL)を使う場合,ローカルがMac,実行環境はLinuxベースのため,ローカルでライブラリを同梱しても動作しない
- よって,serverless-python-requirementsを用いてライブラリ管理をする (Amazon LinuxのDockerイメージを利用)
- 関連する設定は以下
serverless.yml
... provider: name: aws runtime: python3.6 ... plugins: - serverless-python-requirements custom: pythonRequirements: dockerizePip: true ...
-
Lambda上で一時的にファイルを作成したい場合は必ず/tmp
配下を指定する
- 今回はURLで指定された画像を一旦Lambdaローカルに保存し処理
- その際,保存先は
/tmp
以外は不可 (権限なし)
OSError: [Errno 30] Read-only file system
-
Slack APIでメッセージをPostする際,画像などの付属情報を設定可能なattachments
は,JSON内でStringとして格納しなければならない
- つまり,
attachments
の値はjson.dums
する必要あり
- つまり,
# これはOK data_correct = { ... "attachments": json.dumps([ { "title": movie_info['title'], "image_url": movie_img_url } ]) } # これは駄目 data_err = { .... "attachments": [ { "title": movie_info['title'], "image_url": movie_img_url } ] }
開発詳細
つくったものはGitHub - jagijagijag1/animal-recog-slack-botで公開-
serverless.yml
,handler.py
を編集し,ひとまずBot処理を作成 - Slack,Movie DBで作業し,API Token取得
-
serverless.yml
を再度編集し,Lambda環境変数にAPI Token情報
1. Serverless framework + Pythonで開始
$ sls create -t aws-python3 -p <project-name>
serverless.yml
の修正
- 環境変数部分は後で埋めるのでひとまずブランク
serverless.yml
service: animal-recog-slack-bot provider: name: aws runtime: python3.6 region: ap-northeast-1 iamRoleStatements: - Effect: "Allow" Action: - "rekognition:DetectLabels" Resource: "*" plugins: - serverless-python-requirements custom: pythonRequirements: dockerizePip: true functions: hello: handler: handler.main events: - http: path: / method: POST environment: OAUTH_TOKEN: '' BOT_TOKEN: '' MOVIE_DB_API_TOKEN: '' timeout: 20
関数本体を作成
- やや長いのでソースコードはこちら参照
- 画像はURLで受付,一旦Lambda関数ローカルに保存し,RekognitionにByteとして受け渡す
- Rekognitionではかなり一般的な単語(e.g. Animal, Pet)をラベル候補の上位に出してくるため,暫定処理としてNGワード(
ignore_word
)を設定し回避 -
関連映画を取得する処理の概要は以下
- Rekognitionのラベル検出結果から一語を選択
- 選択した後がMovie DBでキーワード登録されているか確認(API: /search/keyword)し,登録ありの場合はID取得 (なしの場合は終了)
- 獲得したキーワードIDを用いて映画検索(API: /discover/movie?with_keyword=)
- 検索結果からランダムに選択した映画をSlackに返す
デプロイ
$ sls plugin install -n serverless-python-requirements $ docker pull lambci``/lambda``:build-python3.6 $ sls deploy -v
2. Slack,Movie DBで作業し,API Token取得
Slack app準備
-
Building Slack apps | SlackでCreate - その後の画面で左側メニューの"Bot User"を選び,Bot Userを追加
- 左メニュー"Event Subscriptions"からイベントを有効にし,"Request URL"にAPI GatewayのURLを指定し,challengeが帰ってくることを確認 (Lambdaコードに処理を埋め込み済)
- challenge成功後,"Subscribe to Bot Events"で"app_mention"イベントを追加
- 左メニュー"Installed App"からアプリをWorkspaceにインストール
- 左メニュ「OAuth & Permissions」にてWorkspaceにAppをInstallすると"OAuth Access Token"と"Bot User OAuth Access Token"が発行される (あとで
serverless.yml
に追記)
Movie DB準備
- 登録し,Settingから申請可能
- 参考:API Docs
- 参考:API Docs
- 申請完了後もSetting->APIでAPIキーを確認可能
- 本アプリではv3 auth利用
3. serverless.yml
を再度編集し,Lambda環境変数にAPI Token情報を追記
- OAUTH_TOKEN: SlackのOAuth Access Token
- BOT_TOKEN: SlackのBot User OAuth Access Token
- MOVIE_DB_API_TOKEN: The Movie DatabaseのAPIキー (v3 auth)
serverless.yml
... functions: hello: ... environment: OAUTH_TOKEN: <your-token> BOT_TOKEN: <your-token> MOVIE_DB_API_TOKEN: <your-token> ...
コメント
コメントを投稿