Files
qihuaiflow/backend/python/app/api/channel_webhook.py
T

47 lines
1.7 KiB
Python

"""
Channel Webhook 入口 — /app/api/channel/<channel_id>
任务 #935 · 2026-08-09
- Telegram 走 POST JSON
- secret_token 校验(头部 X-Telegram-Bot-Api-Secret-Token)
- 其他渠道后续扩展(飞书 / 钉钉加密回调 / 企业微信 AES 解密各不同,放各 adapter 自己)
"""
from __future__ import annotations
import hashlib
import hmac
import logging
from typing import Any
from fastapi import APIRouter, HTTPException, Request
from app.core.channel_adapter import ChannelRouter
log = logging.getLogger(__name__)
router = APIRouter(prefix="/app/api/channel", tags=["channel-webhook"])
@router.post("/{channel_id}")
async def channel_webhook(channel_id: str, request: Request) -> dict:
r = ChannelRouter.shared()
if channel_id not in r.channels:
raise HTTPException(404, f"channel not registered: {channel_id}")
adapter = r._adapters.get(channel_id) # noqa: SLF001 (内部访问 OK)
raw = await request.body()
# secret 校验(各家 header 名不同,Telegram: X-Telegram-Bot-Api-Secret-Token)
expected_secret = getattr(adapter, "webhook_secret", "")
if expected_secret:
secret_header = request.headers.get("x-telegram-bot-api-secret-token", "")
if not secret_header or not hmac.compare_digest(secret_header, expected_secret):
raise HTTPException(401, "invalid secret")
try:
payload: Any = await request.json()
except Exception:
raise HTTPException(400, "invalid json")
# 调对应 adapter 的 receive_update(Telegram 是同步 await;其他渠道可异步)
if hasattr(adapter, "receive_update"):
import asyncio
await adapter.receive_update(payload)
else:
raise HTTPException(501, "channel has no receive_update impl")
return {"ok": True}