from datetime import datetime from flask import jsonify, request from app.infrastructure.service.backend.ai import call_ai from app.infrastructure.service.backend.db import find_rule_reply, get_conn, get_setting from app.infrastructure.service.logging.log_service import log_event, new_trace_id def register_message_routes(app): @app.route("/api/messages/receive", methods=["POST"]) def api_receive_message(): trace_id = new_trace_id("api") data = request.get_json(silent=True) or request.form.to_dict() or {} content = (data.get("content") or "").strip() wx_user_id = (data.get("wx_user_id") or "").strip() wx_nickname = (data.get("wx_nickname") or "").strip() is_friend_request = int(data.get("is_friend_request") or 0) ocr_confidence = str(data.get("ocr_confidence") or "").strip() ocr_bubble_side = str(data.get("ocr_bubble_side") or "").strip() if not content and not is_friend_request: log_event("WARNING", "api", "api.messages.receive", trace_id, "validate", "failed", "消息内容为空且非好友请求", reason="content_empty") return jsonify({"error": "content is empty"}), 400 conn = get_conn() now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") try: with conn.cursor() as cur: cur.execute("INSERT INTO messages (wx_user_id, wx_nickname, direction, content, is_friend_request, reply_strategy, reply_reason, ocr_confidence, ocr_bubble_side, created_at) VALUES (%s, %s, 'in', %s, %s, %s, %s, %s, %s, %s)", (wx_user_id, wx_nickname, content, is_friend_request, "none", "received", ocr_confidence, ocr_bubble_side, now)) in_msg_id = cur.lastrowid auto_on = str(get_setting("auto_reply_enabled", "1") or "1").strip().lower() in ["1", "true", "yes", "on"] full_auto_on = str(get_setting("full_auto_reply_enabled", "0") or "0").strip().lower() in ["1", "true", "yes", "on"] reply_text = "" used_rule_id = None reply_strategy = "none" reply_reason = "" if not auto_on: reply_reason = "auto_reply_disabled" else: rule = find_rule_reply(content) if rule: reply_text = (rule.get("reply_text") or "").strip() used_rule_id = rule.get("id") reply_strategy = "rule" reply_reason = f"rule_hit:{used_rule_id}" elif full_auto_on: reply_strategy = "full_auto" reply_reason = "full_auto_enabled" reply_text = call_ai(content, wx_user_id) else: reply_reason = "rule_miss" should_reply = auto_on and bool(reply_text) reply_msg_id = None if should_reply: is_ai_reply = 0 if used_rule_id else 1 cur.execute("INSERT INTO messages (wx_user_id, wx_nickname, direction, content, is_ai_reply, rule_id, reply_strategy, reply_reason, created_at) VALUES (%s, %s, 'out', %s, %s, %s, %s, %s, %s)", (wx_user_id, wx_nickname, reply_text, is_ai_reply, used_rule_id, reply_strategy, reply_reason, now)) reply_msg_id = cur.lastrowid conn.commit() except Exception as exc: conn.rollback() log_event("ERROR", "api", "api.messages.receive", trace_id, "persist", "failed", "消息入库或回复处理失败", reason="db_error", extra={"error": str(exc)}) raise finally: conn.close() log_event("INFO", "audit", "audit.decision", trace_id, "decision", "ok", "回复决策完成", reason=reply_reason or "none", extra={"wx_user_id": wx_user_id, "strategy": reply_strategy, "should_reply": should_reply, "rule_id": used_rule_id or ""}) log_event("INFO", "api", "api.messages.receive", trace_id, "done", "ok", "消息处理完成", extra={"in_message_id": in_msg_id, "reply_message_id": reply_msg_id or "", "should_reply": should_reply}) return jsonify({"success": True, "should_reply": should_reply, "reply_text": reply_text, "in_message_id": in_msg_id, "reply_message_id": reply_msg_id})