{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreignn354nansbv4gzet6zcckj6uk5l5vkfmpvtgpojth6y4k74cgp4",
    "uri": "at://did:plc:ctnrfotecqqhvo7kl5cgtpwt/app.bsky.feed.post/3miasdba6ax72"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreid7ygf7dm77vh265hffbwmfqf2pqnq64qrd6nrg5rp6464dmqmqlm"
    },
    "mimeType": "image/png",
    "size": 774991
  },
  "path": "/entry/2026/03/30/120000",
  "publishedAt": "2026-03-30T03:00:00.000Z",
  "site": "https://toranoana-lab.hatenablog.com",
  "tags": [
    "前回の記事",
    "https://api.slack.com/apps",
    "toranoana-lab.co.jp"
  ],
  "textContent": "こんにちは!虎の穴ラボの鷺山です。\n\n前回の記事で、**アプリケーションのエラーログをSlackに転送する方法** をご紹介しました。\n\n日々流れてくるログには「原因が判明しているもの」や「静観してよいもの」が少なからず混ざっています。本来はログレベルを見直したり問題そのものを修正すべきですが、すぐに対応できないケースも現実的には多いと思います。しかし、大量のログを一件ずつ判読するのは骨が折れます。\n\nそこで今回は、**Slackに投稿されるエラーログが既知のものだった場合に自動返信するボット** を作ってみたのでご紹介します。事前に「既知のパターン」だと把握できるだけで、確認時の心理的・時間的コストはぐっと下がります。\n\n## 前提環境\n\n項目  |  バージョン・値\n---|---\nPython  |  3.14\nAWSリージョン  |  `ap-northeast-1`\n\n## Slack Appのセットアップ\n\n「Slackのメッセージを読み取る」「読み取ったスレッドに返信する」ためのSlack Appをセットアップします。\n\n  1. Slackアプリの管理ページ https://api.slack.com/apps に移動します。\n  2. **Create New App** または**Create an App** を押し、**From scratch** を選択します。\n  3. **App Name** にアプリ名を入力し、**Pick a workspace to develop your app in:** にアプリの作成先のワークスペースを選択します。\n     * 今回は**既知のアラートです** というアプリ名とします。\n     * **Create App** を押すとアプリが作成されます。\n  4. アプリが作成されたら**OAuth & Permissions**メニューを開き、**ボットトークンのスコープ** に以下を追加します。\n     * **chat:write** … チャンネルへのメッセージ送信を許可\n     * **channels:history** … パブリックチャンネルの読み取りを許可(パブリックチャンネルの場合に追加)\n     * **groups:history** … プライベートチャンネルの読み取りを許可(プライベートチャンネルの場合に追加)\n  5. 次に**App Home** メニューを開き、**Your App's Presence in Slack** の**App Display Name** を設定します。\n\n     * **Display Name (Bot Name):** ボットの表示名を入力します。\n     * **Default username:** ボットのユーザー名を入力します。\n  6. **Install App** メニューを開き、**Install to Workspace** を押してこのアプリのSlackへのアクセスを許可します。  ボット用のOAuthトークン`xoxb-...`が生成されます。このトークンは次のセクションで使用します。\n\n  7. 最後に、以下のcURLコマンドでボットのIDを取得します。`Bearer`には上記のトークン`xoxb-...`を指定します。\n\n\n\n\n\n    curl -H \"Authorization: Bearer xoxb-...\" https://slack.com/api/auth.test\n\nリクエストに成功するとJSONが返ります。`bot_id`がボットのIDです。このIDも次のセクションで使用します。\n\n\n    { \"ok\": true, ... \"bot_id\": \"BXXXXXXXXXX\", ... }\n\n## Lambda関数の作成\n\nSlackからイベントを受け取り、特定のキーワードを含む場合に返信するAWS Lambda関数を作成します。\n\n今回は**replyToKnownIssueOnSlack** という関数名にしました。ランタイムは**Python** を使用します。\n\n今回はSlackからのイベントを受け取れるようにするため、**その他の設定** から**関数URL** を**有効化** します。\n**認証タイプ** はLambda側で認証を実装するため**NONE** を選択します。\n\nLambda関数が作成されたら、「設定」タブから関数URLを控えておいてください(次のセクションで使用します)。\n\nコードは以下のようになります。\n\n\n    import json import os from urllib.request import Request, urlopen # `when` にマッチするワードを検出したら `then` のテキストを返信します rules = [ {\"when\": \"Ping\", \"then\": \"Pong\"}, {\"when\": \"The cat sat on the keyboard.\", \"then\": \"一時的なエラーです\"}, ] API_KEY = os.environ[\"API_KEY\"] SLACK_BOT_TOKEN = os.environ[\"SLACK_BOT_TOKEN\"] SLACK_BOT_ID = os.environ[\"SLACK_BOT_ID\"] def lambda_handler(payload, context): # クエリによる簡易認証 api_key = payload.get(\"queryStringParameters\", {}).get(\"apiKey\", None) if api_key != API_KEY: return {\"statusCode\": 401, \"body\": \"Unauthorized\"} body = json.loads(payload.get(\"body\", \"{}\")) # Slackイベントへのサブスクリプション用 if \"challenge\" in body: return {\"statusCode\": 200, \"body\": body[\"challenge\"]} # パラメータを抽出 event = body.get(\"event\", {}) bot_id = event.get(\"bot_id\", \"\") text = event.get(\"text\", \"\") channel = event.get(\"channel\", \"\") ts = event.get(\"ts\", \"\") print(f\"bot_id={bot_id}, text={text}, channel={channel}, ts={ts}\") # 自分自身のメッセージはスキップ (無限ループ防止) if bot_id and bot_id == SLACK_BOT_ID: return {\"statusCode\": 200, \"body\": \"SKIPPED\"} # ルールにマッチするワードを検出したらスレッドに返信 for rule in rules: if rule[\"when\"] in text: when = rule[\"when\"].replace(\"`\", \"'\") # バッククォートをサニタイズ message = f\"{rule['then']}> `{when}`\" send_reply_to_slack(channel=channel, thread_ts=ts, text=message) return {\"statusCode\": 200, \"body\": \"OK\"} def send_reply_to_slack(channel, thread_ts, text): url = \"https://slack.com/api/chat.postMessage\" payload = {\"channel\": channel, \"thread_ts\": thread_ts, \"text\": text} headers = { \"Content-Type\": \"application/json; charset=utf-8\", \"Authorization\": f\"Bearer {SLACK_BOT_TOKEN}\", } req = Request(url, json.dumps(payload).encode(\"utf-8\"), headers) urlopen(req).read()\n\n### 💡ポイント\n\n上記のコードの各ポイントを解説します。\n\n\n    # `when` にマッチするワードを検出したら `then` のテキストを返信します rules = [ {\"when\": \"Ping\", \"then\": \"Pong\"}, {\"when\": \"The cat sat on the keyboard.\", \"then\": \"一時的なエラーです\"}, ]\n\n「マッチさせるキーワード」と「マッチした場合に返信する文章」がペアになった「ルール辞書」です。\n自動返信させたい内容をこの辞書を追加していきます。\n\n\n    API_KEY = os.environ[\"API_KEY\"] SLACK_BOT_TOKEN = os.environ[\"SLACK_BOT_TOKEN\"] SLACK_BOT_ID = os.environ[\"SLACK_BOT_ID\"]\n\nこのLambda関数で使用する環境変数です。\n\nキー  |  値\n---|---\n**API_KEY** |  この関数の認証キーです。十分な長さのランダムな文字列を設定してください。\n**SLACK_BOT_TOKEN** |  前のセクションで生成したSlackボットのトークンです。`xoxb-...`\n**SLACK_BOT_ID** |  前のセクションで生成したSlackボットのIDです。`B...`\n\n環境変数は「設定」タブから事前に設定してください。\n\n\n     # クエリによる簡易認証 api_key = payload.get(\"queryStringParameters\", {}).get(\"apiKey\", None) if api_key != API_KEY: return {\"statusCode\": 401, \"body\": \"Unauthorized\"}\n\n認証キー `?apiKey=xxxxxxxxxx` が一致しない場合は認証エラーとしています。\n\n\n     # Slackイベントへのサブスクリプション用 if \"challenge\" in body: return {\"statusCode\": 200, \"body\": body[\"challenge\"]}\n\nSlackイベントへのサブスクリプションに必要なコードです。次のセクションで使用します。\nリクエストに`challenge`が含まれていたらそのままレスポンスで返します。\n\n\n     # 自分自身のメッセージはスキップ (無限ループ防止) if bot_id and bot_id == SLACK_BOT_ID: return {\"statusCode\": 200, \"body\": \"SKIPPED\"}\n\nボットが返信したメッセージにボット自身が反応しないようにしています。\n**⚠️環境変数`SLACK_BOT_ID`を必ず設定してください。**\n\n\n     # ルールにマッチするワードを検出したらスレッドに返信 for rule in rules: if rule[\"when\"] in text: when = rule[\"when\"].replace(\"`\", \"'\") # バッククォートをサニタイズ message = f\"{rule['then']}> `{when}`\" send_reply_to_slack(channel=channel, thread_ts=ts, text=message)\n\n前述の「ルール辞書」とメッセージを照らし合わせて、キーワードがマッチした場合にそのスレッドに返信しています。\n\n## Slackボットのイベント受信設定\n\nSlackアプリの設定画面の**Event Subscriptions** メニューから、ボットがメッセージを受け取れるように設定します。\n\n  1. **Enable Events** を**On** にします。\n  2. **Request URL** に、**Lambdaの関数URL** に**APIキーを付与したもの** を入力します。\n     * 例: `https://xxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/?apiKey=xxxxxxxxxx`\n     * チャレンジ認証に成功すると**Verified** と表示されます。\n  3. **Subscribe to bot events** にサブスクライブするイベントを追加します。\n\n     * パブリックチャンネルの場合: **message.channels**\n     * プライベートチャンネルの場合: **message.groups**\n  4. 最後に**Save Changes** で設定を保存します。\n\n\n\n\n## Slackチャンネルにインテグレーションを追加\n\nアプリケーションログの転送先(今回の自動返信先)であるSlackチャンネルのインテグレーションに、今回セットアップしたアプリを追加します。\n\n## 動作確認\n\nチャンネルに「Ping」と投稿したら、ボットが「Pong」と返してくるはずです。\n\nルール辞書の`when`にマッチするメッセージが投稿された場合に、`then`の内容が返信されるようになっています。\n\n\n     rules = [ ... {\"when\": \"The cat sat on the keyboard.\", \"then\": \"一時的なエラーです\"}, ]\n\n## まとめ\n\n今回は、Slackに投稿されるエラーログが既知のものだった場合に自動返信するボットを作成しました。\nルール辞書にキーワードと返信内容を追加するだけで簡単に拡張できるので、ぜひ活用してみてください。\n本来はログそのものの改善が望ましいですが、すぐに対応できない場面での運用の手助けになれば幸いです!\n\n# 採用情報\n\n虎の穴ラボでは一緒に働く仲間を募集中です!\nこの記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧ください。\ntoranoana-lab.co.jp",
  "title": "Slackのエラーログに自動返信するBotをAWS Lambdaで作ってみた",
  "updatedAt": "2026-03-30T03:00:02.000Z"
}