47 lines
1.7 KiB
Python
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} |