| import json |
| from datetime import datetime, timezone |
| from fastapi import APIRouter, Depends, Header, HTTPException, Request |
| from sqlmodel import Session |
| from ..config import get_settings |
| from ..db import get_session |
| from ..models import InboundEvent |
| from ..services.event_normalizer import normalize_meta_payload |
| from ..services.meta_signature import verify_meta_signature |
| from ..services.openclaw_client import send_event_to_openclaw |
|
|
| router = APIRouter(prefix="/meta", tags=["meta-webhooks"]) |
|
|
| @router.get("/webhook") |
| async def verify_webhook(request: Request): |
| settings = get_settings() |
| params = dict(request.query_params) |
| if params.get("hub.mode") == "subscribe" and params.get("hub.verify_token") == settings.meta_webhook_verify_token: |
| challenge = params.get("hub.challenge") |
| return int(challenge) if challenge and challenge.isdigit() else challenge |
| raise HTTPException(status_code=403, detail="Webhook verification failed") |
|
|
| @router.post("/webhook") |
| async def receive_webhook(request: Request, session: Session = Depends(get_session), x_hub_signature_256: str | None = Header(default=None)): |
| raw = await request.body() |
| verify_meta_signature(raw, x_hub_signature_256) |
| try: |
| payload = json.loads(raw.decode("utf-8")) |
| normalized_events = normalize_meta_payload(payload) |
| except HTTPException: |
| raise |
| except Exception as exc: |
| raise HTTPException(status_code=400, detail=f"Could not normalize payload: {exc}") |
| stored = [] |
| for event in normalized_events: |
| db_event = InboundEvent(**event.model_dump(), raw_payload=payload) |
| session.add(db_event) |
| session.commit() |
| session.refresh(db_event) |
| try: |
| await send_event_to_openclaw(event, db_event.id or 0) |
| db_event.forwarded_at = datetime.now(timezone.utc) |
| except Exception as exc: |
| db_event.forward_error = str(exc)[:1000] |
| session.add(db_event) |
| session.commit() |
| stored.append(db_event.id) |
| return {"status": "ok", "stored_event_ids": stored} |
|
|