Files
inquiry_robot/inquiry-agent/agent/channel/wshoto/parse.py
T

114 lines
3.8 KiB
Python

"""
微盛群消息 → InboundMessage。
本文件职责:从管家 records 抽出 sender_id / chat_id / 正文 / 附件名。
禁止:把存档席「询价机器人」当成发言人入业务;禁止再向企微换 ID。
"""
from __future__ import annotations
import json
from typing import Any, Mapping, Optional, Sequence
from agent.channel.wecom.models import InboundMessage
# 存档席在群里的显示名;from 偶发不是 userid 时也要丢掉
SEAT_DISPLAY_NAMES = frozenset({"询价机器人"})
_SKIP_TYPES = frozenset({"revoke", "recall", "event", "unknown"})
_FILE_TYPES = frozenset({"file", "image", "video", "emotion", "voice"})
def _as_mapping(raw: Any) -> dict[str, Any]:
if isinstance(raw, Mapping):
return dict(raw)
if isinstance(raw, str) and raw.strip().startswith("{"):
try:
parsed = json.loads(raw)
except json.JSONDecodeError:
return {}
return dict(parsed) if isinstance(parsed, Mapping) else {}
return {}
def _create_time(msgtime: Any) -> int:
"""微盛 msgtime 常是毫秒;InboundMessage.create_time 用秒。"""
try:
n = int(msgtime or 0)
except (TypeError, ValueError):
return 0
if n > 10**12:
return n // 1000
return n
def _filename(msg_type: str, content: str, payload: Mapping[str, Any]) -> str:
for key in ("filename", "file_name", "FileName", "title", "name"):
val = str(payload.get(key) or "").strip()
if val:
return val
if msg_type in _FILE_TYPES:
return str(content or "").strip()
return ""
def archive_record_to_inbound(
record: Mapping[str, Any],
*,
seat_userids: Sequence[str] = (),
) -> Optional[InboundMessage]:
"""
一条管家记录变成群入站。
返回 None:不是群、没有发言人、发言人是存档席、或撤回类消息。
message_id 带 wshoto: 前缀,避免和回调 MsgId 撞车。
"""
room_id = str(record.get("room_id") or record.get("roomid") or "").strip()
sender_id = str(record.get("from") or record.get("from_id") or "").strip()
if not room_id or not sender_id:
return None
seats = {str(x).strip() for x in seat_userids if str(x).strip()}
if sender_id in seats or sender_id in SEAT_DISPLAY_NAMES:
return None
msg_type = str(record.get("msgtype") or record.get("msg_type") or "text").strip().lower()
if msg_type in _SKIP_TYPES:
return None
payload = _as_mapping(record.get("msg_content") or record.get("msgcontent"))
content = str(record.get("content") or payload.get("content") or "").strip()
msgid = str(record.get("msgid") or record.get("msg_id") or "").strip()
seq = record.get("seq")
if not msgid:
msgid = f"{room_id}:{seq or 0}:{sender_id}"
media: dict[str, Any] = {}
inbound_type = "text"
if msg_type in _FILE_TYPES:
inbound_type = "file" if msg_type == "file" else msg_type
media = {
"filename": _filename(msg_type, content, payload),
"media_id": str(payload.get("sdkfileid") or payload.get("sdk_file_id") or ""),
"file_ext": str(payload.get("fileext") or payload.get("file_ext") or ""),
"content_type": "",
"filesize": payload.get("filesize") or payload.get("file_size") or 0,
}
if not content:
content = str(media.get("filename") or "")
return InboundMessage(
sender_id=sender_id,
message_id=f"wshoto:{msgid}",
content=content,
msg_type=inbound_type,
chat_id=room_id,
chat_type="group",
create_time=_create_time(record.get("msgtime") or record.get("msg_time")),
media=media,
raw={
"source": "wshoto_archive",
"seq": seq,
"msgtype": msg_type,
"direction": record.get("direction") or "",
},
)