この投稿は別サイトからのクロス投稿です(クロス元:https://qiita.com/jagijagijag1/i...
  • 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イメージを利用)
    • 関連する設定は以下
...
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の修正
  • 環境変数部分は後で埋めるのでひとまずブランク
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)
...
functions:
  hello:
    ...
    environment:
      OAUTH_TOKEN: <your-token>
      BOT_TOKEN: <your-token>
      MOVIE_DB_API_TOKEN: <your-token>
...

参考

関連記事

この記事へのコメント

まだコメントはありません
+1
18
@jagijagijag1の技術ブログ
このエントリーをはてなブックマークに追加