1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Amazon SES の Bounce などを SNS+Lambda 経由で Slack 通知した際のコード(メモ)

1
Posted at

もしかしたら役に立つ人がいるかもしれないので、ちょっと昔に書いたコードをメモとして公開。

SES → SNS → Lambda 連携の設定方法やテスト方法などは

といったところを参考にしていただけたら。
(他にも多数の方がまとめていますが、とりあえず検索して一番に見つかった記事を挙げてみました)

注意点

動作させるためには Lambda の関数の環境変数に Slack の URL を設定する必要があります

Lambda の関数の 設定 > 環境変数 の「編集」から環境変数を追加して

を指定して下さい。

Lambda のランタイムは Node.js 18.x 以上を想定。

/* global fetch */
console.log('Loading function');

// Slack の incoming webhook の設定を環境変数から取り込む
const slackUrl = process.env.SLACK_INCOMING_WEBHOOK_URL;
if (!slackUrl) {
  const errMsg = 'Error: SLACK_INCOMING_WEBHOOK_URL is not defined.';
  console.error(errMsg);
  throw new Error(errMsg);
}

export const handler = async (event) => {

  // for debug: log event entirely
  // console.log(JSON.stringify(event, null, 2));
  
  const results = [];
  
  // SNS のイベントにある全 Records を処理
  for (const record of (event.Records || [])) {
    if (!record.Sns) continue;

    try {
      // Slack へイベントを通知
      console.log('--- Call fetch start');
      const response = await fetch(slackUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(makeSlackMessage(record.Sns))
      });
      
      console.log(`Response status: ${response.status}`);
      results.push(response.status);
      console.log('--- Call fetch end');
    } catch (e) {
      console.error('--- Fetch error:', e);
      results.push(500);
    }
  }
  return results;
};

// Slack 用のメッセージ作成
function makeSlackMessage(sns) {
  let sesNotify;
  try {
    sesNotify = JSON.parse(sns.Message);
  } catch (e) {
    return { text: makeJsonCodeBlock('Amazon SES からの不明な通知', sns) };
  }

  if (!sesNotify.notificationType) {
    return { text: makeJsonCodeBlock('Amazon SES からの不明な通知', sns) };
  }

  // SES の通知を notificationType に応じた処理分け
  // reference: https://docs.aws.amazon.com/ja_jp/ses/latest/dg/notification-contents.html
  let type = sesNotify.notificationType;
  let details = "";
  
  switch (type) {
    case 'Bounce':
      type = `メールの不達(${sesNotify.bounce.bounceType})`;
      details = makeJsonCodeBlock('不達先の詳細情報', sesNotify.bounce.bouncedRecipients);
      break;
      
    case 'Complaint':
      type = `メールへの苦情(${sesNotify.complaint.complaintFeedbackType})`;
      details = makeJsonCodeBlock('苦情元の詳細情報', sesNotify.complaint.complainedRecipients);
      break;
      
    case 'Delivery':
      type = "メールの送信成功";
      details = makeJsonCodeBlock('送信先の詳細情報', sesNotify.delivery.recipients);
      break;

    default:
      // Nothing to do
  }

  // SES 通知の元になったメール情報を取得
  const from = sesNotify.mail?.commonHeaders?.from?.join(',') || sesNotify.mail?.source || "-";
  const subject = sesNotify.mail?.commonHeaders?.subject || "-";

  const notificationAll = makeJsonCodeBlock('通知の全情報', sesNotify);

  // 最終的な Slack 用メッセージ作成
  return {
    text: `【${type}】SES通知: ${subject}`, // 通知ポップアップ用
    blocks: [
      {
        type: "section",
        text: {
          type: "mrkdwn",
          text: `Amazon SES からの通知(common)\n種類:${type}\nメール送信元:${from}\nメール件名:${subject}\n${details}`
        }
      },
      {
        type: "section",
        text: {
          type: "mrkdwn",
          text: notificationAll
        }
      }
    ]
  };
}

function makeJsonCodeBlock(title, data) {
  // 全情報のダンプ(Slackの3000文字制限対策付き)
  const fullJson = JSON.stringify(data, null, 2);
  const displayJson = fullJson.length > 2900 ? `${fullJson.slice(0, 2900)}...` : fullJson;

  return `${title}:\n\`\`\`json\n${displayJson}\n\`\`\``;
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?