ForcePilot/backend/package/yuxi/channel/extensions/kuaishou/webhook.py

67 lines
2.1 KiB
Python
Raw Normal View History

from __future__ import annotations
import json
import logging
import time
from typing import Any
from fastapi import APIRouter, Request, Response
from .signature import kwaisign_verify
from .types import InboundKuaishouMessage
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/webhook/kuaishou", tags=["kuaishou"])
# TODO(P1-2): 当快手开放 IM API 后,替换为官方实际事件类型
KUAISHOU_IM_EVENT_TYPE = "im_message_receive"
def parse_inbound_message(raw: dict[str, Any]) -> InboundKuaishouMessage | None:
try:
event = raw.get("event", "")
if event != KUAISHOU_IM_EVENT_TYPE:
return None
msg_data = raw.get("message", raw)
return InboundKuaishouMessage(
msg_id=str(msg_data.get("msg_id", "")),
open_id=str(msg_data.get("open_id", "")),
msg_type=msg_data.get("msg_type", "text"),
content=msg_data.get("content", ""),
create_time=msg_data.get("create_time", int(time.time() * 1000)),
conversation_type=msg_data.get("conversation_type", "1"),
nickname=msg_data.get("nickname", ""),
avatar_url=msg_data.get("avatar", ""),
raw_event=raw,
)
except Exception:
logger.exception("解析快手入站消息失败")
return None
def create_webhook_handler(app_secret: str):
@router.post("/callback")
async def kuaishou_webhook(request: Request):
body_bytes = await request.body()
# 快手官方 WebHook 签名通过 kwaisign header 传递算法MD5(body + appsecret)
kwaisign = request.headers.get("kwaisign", "")
if not kwaisign_verify(body_bytes, app_secret, kwaisign):
return Response(content='{"status":"signature_invalid"}', status_code=403)
event_data = json.loads(body_bytes.decode("utf-8"))
if event_data.get("event") != KUAISHOU_IM_EVENT_TYPE:
return {"status": "ok"}
inbound = parse_inbound_message(event_data)
if inbound is None:
return {"status": "ok"}
return {"status": "ok", "msg_id": inbound.msg_id}
return kuaishou_webhook