🗃️

AWS Lambda で S3 にアップロードされたファイルを Discord に通知する

2024/02/25に公開

AWS のお勉強がてらに書いておく。

やりたいこと

はじめに以下の背景がある。

  • AWS Lambda で遊んでみたい
  • Lambda 単体だけではなく、他の AWS サービスも絡む形でシステムを構成したい

実際ネタは何でも良いのだが、手軽かつ実用的な実装を考えたときに「AWS Lambda で S3 にアップロードされたファイルを Discord に通知する」というのを思いついたのでこれを実装する。

前提

以下すべてが済んでいれば、Python で型ヒントを用いつつコーディングができるようになる。逆に以下の前提がない場合、よくわからないデータを四苦八苦しながら解読することになりうる。

  1. Python 3.12.2 で実装する。
  2. 以下のパッケージが Lambda Layers で導入されていること

レイヤーの導入について多くは書かないが、追加するボタンは AWS Lambda 関数のページ最下部にある。分かりづらい場所にあるので念のため付記しておく。

Lambda Layers の追加ボタンの在り処

実装方針

  1. Discord の適当なチャンネルの Webhook を作成する
  2. S3 バケットをなんでもいいので作成する。
  3. Lambda 関数を作成する。
    • 関数名はなんでもよい。
    • Lambda Layers にライブラリを足しておくのを忘れないようにする。
    • 環境変数に (1) で控えた Webhook を書き込む。今回は DISCORD_WEBHOOK とした。
  4. Lambda のトリガーに S3 を追加する。バケット名とトリガーとなるイベントの指定が必要であるので、以下の画像の要領で設定する。

S3 トリガーの設定

  1. Python でロジック本体を書く。

Python による実装

ポイントは以下の通り。

  • Powertools for AWS Lambda のお陰で型ヒントを使えるようになる。ただ S3Event なんかはデータ構造の定義というよりラッパークラスなのでちょっと扱いに癖がある。
  • Discord の Webhook は AWS Lambda の環境変数から取ってくるようにする。これによりコード本体に事実上のシークレットである Webhook URI を書かずに済むのでより安全かつ可搬なコードになる。
import os

from aws_lambda_powertools.utilities.typing import LambdaContext
from aws_lambda_powertools.utilities.data_classes import S3Event
import requests

HEADER = {"Content-Type": "application/json"}


def lambda_handler(event, context: LambdaContext):
    if (webhook := os.environ.get("DISCORD_WEBHOOK")) is None:
        return {"statusCode": 500}

    event = S3Event(event)

    message = f"New Item: s3://{event.bucket_name}/{event.object_key}"
    body = {"content": message}

    response = requests.post(webhook, json=body, headers=HEADER)

    try:
        response.raise_for_status()
        return {"statusCode": 200}

    except requests.HTTPError:
        return {"statusCore": 500}

これで S3 にファイルをアップロードすると Discord のチャンネルに通知が飛ぶようにできる。

メモ

  • Lambda 関数を作るときに関数専属(?)の CloudWatch ロググループが追加される。print() なんかで出力したログは CloudWatch で見られる。

Discussion