AWS SES / AWS Lambda メール受信エンドポイントコード?

@dltj さん、こんにちは。Node/JavaScript についてはお手伝いできませんが、私がこれを行うために使用している Python の疑似コードを提供できます。

""" AWS Lambda to receive message from SES and post it to Discourse """
import json
import logging
import boto3
import requests
from base64 import b64decode

LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.INFO)

# pylint: disable=C0301
ENCRYPTED_DISCOURSE_KEY = "{cyper-text}"
DISCOURSE_KEY = boto3.client("kms").decrypt(
    CiphertextBlob=b64decode(ENCRYPTED_DISCOURSE_KEY)
)["Plaintext"]
DISCOURSE_SITE = "discuss.folio.org"
EMAIL_S3_BUCKET = "openlibraryfoundation-discourse-mail"

DISCOURSE_URL = f"https://{DISCOURSE_SITE}/admin/email/handle_mail"
DISCOURSE_API_HEADERS = {
    "Api-Key": DISCOURSE_KEY,
    "Api-Username": "system",
}


def log_lambda_context(context):
    """Log the attributes of the context object received from the Lambda invoker
    """
    LOGGER.info(
        "Time remaining (ms): %s.  Log stream: %s.  Log group: %s.  Mem. limits(MB): %s",
        context.get_remaining_time_in_millis(),
        context.log_stream_name,
        context.log_group_name,
        context.memory_limit_in_mb,
    )


def lambda_handler(event, context):
    """ Handle SES delivery event """
    log_lambda_context(context)
    LOGGER.info(json.dumps(event))

    processed = False

    for record in event["Records"]:
        mail = record["ses"]["mail"]
        mailMessageId = mail["commonHeaders"]["messageId"]
        sesMessageId = mail["messageId"]
        mailSender = mail["source"]

        LOGGER.info(
            "Processing an SES with mID %s (%s) from %s",
            mailMessageId,
            sesMessageId,
            mailSender,
        )

        deliveryRecipients = mail["destination"]
        for recipientEmailAddress in deliveryRecipients:
            LOGGER.info("Delivery recipient: %s", recipientEmailAddress)

            s3resource = boto3.resource("s3")
            bucket = s3resource.Bucket(EMAIL_S3_BUCKET)
            obj = bucket.Object(sesMessageId)
            LOGGER.info("Getting %s/%s", EMAIL_S3_BUCKET, sesMessageId)
            post_data = {"email": obj.get()["Body"].read()}

            LOGGER.info("Posting to %s", DISCOURSE_SITE)
            r = requests.post(
                DISCOURSE_URL, headers=DISCOURSE_API_HEADERS, data=post_data
            )
            if r.status_code == 200:
                LOGGER.info("Mail accepted by Discourse: %s", DISCOURSE_SITE)
                obj.delete()
                processed = True
            else:
                LOGGER.error(
                    "Mail rejected by %s (%s): %s",
                    DISCOURSE_SITE,
                    r.status_code,
                    r.content,
                )

    return processed

デプロイと保守には Serverless を使用しています。同様の方法を検討される場合は、この serverless.yml ファイルから始めることができます。

service: ses-post-to-discourse

frameworkVersion: "=1.36.1"

provider:
  name: aws
  runtime: python3.7
  onError: arn:aws:sns:us-east-1:{AWS_ACCOUNT}:ses-discourse-DLQ
  region: us-east-1
  iamRoleStatements:
  - Effect: 'Allow'
    Action:
      - 'ses:SendBounce'
    Resource:
      - '*'
  - Effect: 'Allow'
    Action:
      - 's3:*'
    Resource:
      - 'arn:aws:s3:::openlibraryfoundation-discourse-mail/*'
  - Effect: 'Allow'
    Action:
      - 'kms:decrypt'
    Resource:
      - 'arn:aws:kms:us-east-1:{AWS_ACCOUNT}:key/{UUID-of-key}'

package:
  include:
    # - something
  exclude:
    - node_modules/**
    - .venv/**
    - __pycache__
    - secrets.yml

functions:
  emailDispatcher:
    handler: post_ses_delivery_lambda.lambda_handler
    events:
      - sns: ses-discourse-inbound

# CloudFormation resource templates
resources:
  Resources:
    EmailDispatcherLogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        RetentionInDays: "30"

plugins:
  - serverless-python-requirements
custom:
  pythonRequirements:
    pythonBin: .venv/bin/python
    dockerizePip: false