diff --git a/app/routers/chatbot.py b/app/routers/chatbot.py index 693d7dc..a29b15c 100644 --- a/app/routers/chatbot.py +++ b/app/routers/chatbot.py @@ -7,6 +7,7 @@ import time import xml.etree.ElementTree as ET from datetime import datetime, timezone from typing import Optional +from urllib.parse import parse_qs import httpx from Crypto.Cipher import AES @@ -329,22 +330,43 @@ async def handle_business(data: dict) -> str: async def _handle_wechat_request(request: Request, app_id: Optional[str], background_tasks: BackgroundTasks) -> str: if app_id and app_id != APP_ID: logger.warning("chatbot app_id mismatch expected=%s actual=%s", APP_ID, app_id) - raw_body = (await request.body()).decode("utf-8").strip() + raw_body = (await request.body()).decode("utf-8", errors="ignore").strip() if not raw_body: raise HTTPException(status_code=400, detail="empty body") + cipher_b64 = raw_body - if "encrypted" in raw_body: - try: - outer = json.loads(raw_body) - if isinstance(outer, dict) and "encrypted" in outer: - cipher_b64 = "".join(str(outer["encrypted"]).split()).strip() - except Exception: - pass + # 兼容三种请求体: + # 1) 纯密文 base64 + # 2) JSON: {"encrypted":"..."} / {"encrypt":"..."} / {"Encrypt":"..."} + # 3) x-www-form-urlencoded: encrypted=... / encrypt=... + try: + outer = json.loads(raw_body) + if isinstance(outer, dict): + for key in ("encrypted", "encrypt", "Encrypt"): + val = outer.get(key) + if isinstance(val, str) and val.strip(): + cipher_b64 = val + break + except Exception: + form = parse_qs(raw_body, keep_blank_values=True) + for key in ("encrypted", "encrypt", "Encrypt"): + vals = form.get(key) or [] + if vals and str(vals[0]).strip(): + cipher_b64 = str(vals[0]) + break + + cipher_b64 = "".join(str(cipher_b64).split()).strip() try: decrypted = aes_decrypt_base64(cipher_b64, ENCODING_AES_KEY) data = _parse_decrypted_payload(decrypted) - except Exception: - logger.error("chatbot decrypt failed") + except Exception as error: + logger.error( + "chatbot decrypt failed app_id=%s content_type=%s body_prefix=%s err=%s", + app_id or "", + request.headers.get("content-type", ""), + raw_body[:160], + error, + ) raise HTTPException(status_code=400, detail="decrypt error") answer_text = await handle_business(data)