python-wechat-kf/app/routes/webhook.py

61 lines
2.1 KiB
Python

from fastapi import APIRouter, Request, Query, Depends
from fastapi.responses import PlainTextResponse
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.services.crypto import verify_url, decrypt_message
from app.services.wechat_client import sync_msg
from app.services.message_service import save_messages
from app.config import settings
import logging
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("/webhook")
async def verify_callback(
msg_signature: str = Query(..., alias="msg_signature"),
timestamp: str = Query(...),
nonce: str = Query(...),
echostr: str = Query(...),
):
"""URL 验证:解密 echostr 并返回明文"""
try:
plain = verify_url(msg_signature, timestamp, nonce, echostr)
return PlainTextResponse(plain)
except Exception as e:
logger.error(f"URL 验证失败: {e}")
return PlainTextResponse("verification failed", status_code=400)
@router.post("/webhook")
async def receive_callback(
request: Request,
msg_signature: str = Query("", alias="msg_signature"),
timestamp: str = Query(""),
nonce: str = Query(""),
db: AsyncSession = Depends(get_db),
):
"""接收消息回调:验证签名 → 解密 XML → 拉取消息 → 入库"""
try:
xml_body = await request.body()
xml_str = xml_body.decode("utf-8")
logger.info(f"收到回调, encrypt 前100字符: {xml_str[:100]}")
token, open_kfid = decrypt_message(xml_str, msg_signature, timestamp, nonce)
if not token or not open_kfid:
logger.warning("解密后 Token 或 OpenKfId 为空")
return PlainTextResponse("fail")
# 拉取消息
result = await sync_msg(open_kfid, token=token, limit=100)
msg_list = result.get("msg_list", [])
if msg_list:
saved = await save_messages(db, msg_list)
logger.info(f"回调拉取消息 {len(msg_list)} 条,新增入库 {saved}")
return PlainTextResponse("success")
except Exception as e:
logger.error(f"回调处理失败: {e}")
return PlainTextResponse("fail")