ForcePilot/backend/package/yuxi/channel/extensions/messenger/webhook.py
Kris 895602f774 feat(channel): 添加 Facebook Messenger 渠道扩展
新增 Facebook Messenger 渠道扩展,支持在 Yuxi 平台中集成 Messenger 即时通讯渠道。

包含以下功能模块:
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- outbound: 外发消息管理
- streaming: 流式消息处理
- pairing: 用户配对与绑定
- security: 安全校验
- dedupe: 消息去重
- monitor: 渠道状态监控
- status: 会话状态管理
- actions: 动作处理
- template: 消息模板
- quick_reply: 快捷回复
- private_reply: 私密回复
- handover: 转人工切换
- persona: 人设管理
- profile: 主页配置
- user: 用户信息
- insights: 数据洞察
- notification: 通知推送
- media: 媒体资源处理
- types: 类型定义
2026-05-21 11:25:47 +08:00

74 lines
2.5 KiB
Python

import hashlib
import hmac
import json
import logging
from fastapi import APIRouter, Request, HTTPException, Query
from fastapi.responses import PlainTextResponse, JSONResponse
router = APIRouter(prefix="/api/channel/messenger", tags=["messenger"])
logger = logging.getLogger(__name__)
@router.get("/webhook")
async def verify_webhook(
hub_mode: str = Query(None, alias="hub.mode"),
hub_challenge: str = Query(None, alias="hub.challenge"),
hub_verify_token: str = Query(None, alias="hub.verify_token"),
):
from yuxi.channel.extensions.messenger.config import MessengerConfigAdapter
adapter = MessengerConfigAdapter()
account = adapter._build_account("default", {})
verify_token = account.get("verify_token", "")
if not verify_token:
raise HTTPException(status_code=500, detail="verify_token not configured")
if hub_mode == "subscribe" and hub_verify_token == verify_token:
logger.info("messenger webhook verified successfully")
return PlainTextResponse(hub_challenge or "", status_code=200)
logger.warning("messenger webhook verification failed: token mismatch")
raise HTTPException(status_code=403, detail="Verification failed")
@router.post("/webhook")
async def receive_webhook(request: Request):
body = await request.body()
if not _verify_signature(request.headers, body):
logger.warning("messenger webhook signature verification failed")
raise HTTPException(status_code=403, detail="Invalid signature")
payload = json.loads(body)
logger.debug(f"messenger webhook received: entries={len(payload.get('entry', []))}")
from yuxi.channel.extensions.messenger.gateway import _get_webhook_queue
queue = _get_webhook_queue()
if queue is not None:
await queue.put(payload)
return JSONResponse({"status": "ok"})
def _verify_signature(headers, body: bytes) -> bool:
from yuxi.channel.extensions.messenger.config import MessengerConfigAdapter
adapter = MessengerConfigAdapter()
account = adapter._build_account("default", {})
app_secret = account.get("app_secret", "")
if not app_secret:
logger.warning("messenger app_secret not set, skipping signature verification")
return True
expected = headers.get("X-Hub-Signature-256", "")
if not expected.startswith("sha256="):
return False
actual = hmac.new(app_secret.encode("utf-8"), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={actual}", expected)