Agentive
自動化ラボ

AIでWebhook統合 — イベント駆動型自動化の構築

約5分で読めます

外部サービスで何かが起きたら、Webhookで通知を受け取り、AIが判断して自動アクションを実行する。Discord Bot、GitHub Webhook、Stripeの支払い通知など、イベント駆動型自動化の実装方法を解説する。

Webhook受信サーバーの基本構成

FastAPIで軽量なWebhook受信サーバーを立てる。

from fastapi import FastAPI, Request, HTTPException
import hmac, hashlib

app = FastAPI()

@app.post("/webhook/{service}")
async def receive_webhook(service: str, request: Request):
    body = await request.body()
    headers = request.headers

    if service == "github":
        verify_github_signature(body, headers)
    elif service == "discord":
        verify_discord_signature(body, headers)

    payload = await request.json()
    await process_event(service, payload)
    return {"status": "ok"}

GitHub Webhookの実装

署名検証

GitHub WebhookはX-Hub-Signature-256ヘッダーでHMAC-SHA256署名を送る。必ず検証する。

import os

GITHUB_WEBHOOK_SECRET = os.environ["GITHUB_WEBHOOK_SECRET"]

def verify_github_signature(body, headers):
    signature = headers.get("X-Hub-Signature-256", "")
    expected = "sha256=" + hmac.new(
        GITHUB_WEBHOOK_SECRET.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(signature, expected):
        raise HTTPException(status_code=401, detail="Invalid signature")

イベント処理

async def handle_github_event(payload):
    action = payload.get("action")

    if "pull_request" in payload:
        pr = payload["pull_request"]
        if action == "opened":
            await ai_review_pr(pr["number"], pr["diff_url"])
        elif action == "closed" and pr.get("merged"):
            await update_changelog(pr["title"], pr["body"])

    elif "issue" in payload and action == "opened":
        await auto_label_issue(payload["issue"])

Discord Webhookの実装

Discord Botからのメッセージ受信

from nacl.signing import VerifyKey
from nacl.exceptions import BadSignatureError

DISCORD_PUBLIC_KEY = os.environ["DISCORD_PUBLIC_KEY"]

def verify_discord_signature(body, headers):
    signature = headers.get("X-Signature-Ed25519", "")
    timestamp = headers.get("X-Signature-Timestamp", "")
    verify_key = VerifyKey(bytes.fromhex(DISCORD_PUBLIC_KEY))
    try:
        verify_key.verify(timestamp.encode() + body, bytes.fromhex(signature))
    except BadSignatureError:
        raise HTTPException(status_code=401, detail="Invalid signature")

Discordへの通知送信

import httpx

DISCORD_WEBHOOK_URL = os.environ["DISCORD_WEBHOOK_URL"]

async def send_discord_notification(title, description, color=0x00ff00):
    embed = {
        "embeds": [{
            "title": title,
            "description": description,
            "color": color
        }]
    }
    async with httpx.AsyncClient() as client:
        await client.post(DISCORD_WEBHOOK_URL, json=embed)

セキュリティの必須事項

対策理由
署名検証を必ず実装する偽造リクエストを排除
HTTPS必須通信内容の盗聴防止
レート制限を設けるDDoS攻撃の緩和
ペイロードサイズを制限するメモリ枯渇攻撃の防止
シークレットは環境変数に格納ソースコードへの漏洩防止
from slowapi import Limiter
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)

@app.post("/webhook/{service}")
@limiter.limit("60/minute")
async def receive_webhook(service, request: Request):
    body = await request.body()
    if len(body) > 16384:
        raise HTTPException(status_code=413, detail="Payload too large")

AI処理パイプラインとの接続

Webhook受信後、AIに判断させてアクションを自動実行する。

import subprocess

async def ai_review_pr(pr_number, diff_url):
    result = subprocess.run(
        ["claude", "-p", f"PR #{pr_number}のdiffをレビューして。{diff_url}"],
        capture_output=True, text=True, timeout=120
    )
    await post_github_comment(pr_number, result.stdout)

Webhook統合により、外部イベントをトリガーとしたAI自動化パイプラインが完成する。署名検証を省略せず、セキュリティを最優先で設計すること。

関連記事

A

Agentive 編集部

AIエージェントを実際に使い倒す個人開発者。サイト制作の自動化を実践しながら、その知見を発信しています。