serverless framworkで画像認識して関係する映画を推薦するSlack Botを作ったまとめ (No Server November Challenge)

serverless framworkで画像認識して関係する映画を推薦するSlack Botを作ったまとめ (No Server November Challenge):

  • 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経由で取得


animal-recog-slack-bot.png



結果

  • 猫の画像を送ると,猫が写っていることと,関連映画として魔女の宅急便を教えてくれた��


screenshot.png



環境

  • 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で公開


  1. serverless.yml, handler.pyを編集し,ひとまずBot処理を作成
  2. Slack,Movie DBで作業し,API Token取得

  3. 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から申請可能

  • 申請完了後も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> 
... 


参考

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)