修复MaiBot适配器群聊误判为私聊并补充路由诊断日志

变更项:\n1. 新增统一会话路由解析函数 _resolve_chat_route,按 plugin_roomid -> wx_roomid -> wx_to_user -> 原始报文 From/ToUserName 逐级兜底识别群聊。\n2. can_process 与转发构包统一使用同一套路由判定,避免采集判定和发送判定不一致。\n3. 在 additional_config 中新增 abot_route_type 与 abot_route_source,便于服务端链路排查。\n4. 补充详细路由日志,记录 route_type、route_source、plugin_roomid、wx_roomid、wx_to_user、sender、message_id。\n5. 入队日志增加 route_type/route_source 字段,便于快速筛查是否持续发生私聊误判。
This commit is contained in:
liuwei
2026-04-29 10:36:00 +08:00
parent 64e9e620b8
commit 84992bcd6c

View File

@@ -166,8 +166,8 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
if isinstance(full_msg, WxMessage) and full_msg.from_self():
return False
roomid = str(message.get("roomid", "") or "").strip()
if roomid:
chat_route = self._resolve_chat_route(message)
if chat_route["is_group"]:
return self._collect_group_messages
return self._collect_private_messages
@@ -193,7 +193,8 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
try:
outbound_queue.put_nowait(payload)
self.LOG.info(
f"[{self.name}] 消息已入队: roomid={payload['roomid']}, sender={payload['sender']}, "
f"[{self.name}] 消息已入队: route_type={payload['route_type']}, roomid={payload['roomid']}, "
f"route_source={payload['route_source']}, sender={payload['sender']}, "
f"msg_type={payload['message_type']}, queue_size={outbound_queue.qsize()}, "
f"content_preview={normalized_content[:120]}"
)
@@ -491,7 +492,11 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
def _build_outbound_payload(self, message: Dict[str, Any], normalized_content: str) -> Dict[str, Any]:
"""把微信消息包装成 MaiBot 官方 API Server 的 APIMessageBase 结构。"""
full_msg = message.get("full_wx_msg")
roomid = str(message.get("roomid", "") or "").strip()
# 会话路由统一在这里解析避免“can_process 一套、转发又一套”导致判定漂移。
chat_route = self._resolve_chat_route(message)
roomid = chat_route["roomid"]
route_type = chat_route["route_type"]
route_source = chat_route["route_source"]
sender = str(message.get("sender", "") or "").strip()
msg_type = self._resolve_message_type(message)
timestamp = self._resolve_message_timestamp(message, full_msg)
@@ -500,6 +505,15 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
sender_name = self._resolve_sender_name(message, sender)
group_name = self._resolve_group_name(message, roomid)
# 路由诊断日志:这里把关键字段一次性打全,便于定位“群消息为何被识别成私聊”。
self.LOG.info(
f"[{self.name}] 路由判定: route_type={route_type}, route_source={route_source}, "
f"plugin_roomid={str(message.get('roomid', '') or '').strip()}, "
f"wx_roomid={str(getattr(full_msg, 'roomid', '') or '').strip() if isinstance(full_msg, WxMessage) else ''}, "
f"wx_to_user={str(getattr(full_msg, 'to_user', '') or '').strip() if isinstance(full_msg, WxMessage) else ''}, "
f"sender={sender}, message_id={message_id}"
)
sender_info: Dict[str, Any] = {
"user_info": {
"platform": self._platform_name,
@@ -545,6 +559,9 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
"is_at": bool(message.get("is_at", False)),
"wx_message_type": msg_type,
"collect_only": True,
# 额外把本地路由判定结果透传给 MaiBot便于服务端/日志排查“为何被识别成私聊”。
"abot_route_type": route_type,
"abot_route_source": route_source,
},
"sender_info": sender_info,
"receiver_info": receiver_info,
@@ -560,6 +577,8 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
}
return {
"route_type": route_type,
"route_source": route_source,
"roomid": roomid,
"sender": sender,
"message_type": msg_type,
@@ -568,6 +587,51 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
"api_message": api_message,
}
def _resolve_chat_route(self, message: Dict[str, Any]) -> Dict[str, str]:
"""解析消息的群聊/私聊路由,兼容上游字段缺失场景。"""
full_msg = message.get("full_wx_msg")
# 先看插件消息里已经带好的 roomid这是最直接、最便宜的一跳。
plugin_roomid = str(message.get("roomid", "") or "").strip()
if plugin_roomid.endswith("@chatroom"):
return {"is_group": True, "route_type": "group", "roomid": plugin_roomid, "route_source": "plugin_roomid"}
# 再看 WxMessage 里计算过的 roomid。
wx_roomid = ""
wx_to_user = ""
wx_from_group = False
if isinstance(full_msg, WxMessage):
wx_roomid = str(getattr(full_msg, "roomid", "") or "").strip()
wx_to_user = str(getattr(full_msg, "to_user", "") or "").strip()
try:
wx_from_group = bool(full_msg.from_group())
except Exception:
wx_from_group = False
if wx_roomid.endswith("@chatroom"):
return {"is_group": True, "route_type": "group", "roomid": wx_roomid, "route_source": "wx_roomid"}
# 某些边界消息里roomid 可能没落下来,但 to_user 仍是 chatroom。
if wx_to_user.endswith("@chatroom"):
return {"is_group": True, "route_type": "group", "roomid": wx_to_user, "route_source": "wx_to_user"}
# 最后兜底原始报文,避免上游字段偶发缺失时把群消息误判成私聊。
raw_data = getattr(full_msg, "raw_data", {}) if isinstance(full_msg, WxMessage) else {}
raw_from = ""
raw_to = ""
if isinstance(raw_data, dict):
raw_from = str(((raw_data.get("FromUserName") or {}).get("string")) or "").strip()
raw_to = str(((raw_data.get("ToUserName") or {}).get("string")) or "").strip()
if raw_from.endswith("@chatroom"):
return {"is_group": True, "route_type": "group", "roomid": raw_from, "route_source": "raw_from_user"}
if raw_to.endswith("@chatroom"):
return {"is_group": True, "route_type": "group", "roomid": raw_to, "route_source": "raw_to_user"}
# 到这里仍不是群聊,就按私聊处理,但保留详细来源,方便日志反查。
private_source = "wx_from_group_flag" if wx_from_group else "private_default"
return {"is_group": False, "route_type": "private", "roomid": "", "route_source": private_source}
def _resolve_reply_route(self, message_info: Dict[str, Any]) -> Optional[Dict[str, str]]:
"""从 MaiBot 返回的 message_info 中解析微信路由。"""
receiver_info = message_info.get("receiver_info") or {}
@@ -794,4 +858,3 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
self.LOG.warning(f"[{self.name}] 关闭 ClientSession 失败: {exc}")
finally:
self._client_session = None