""" 企微入站归一模型。 本文件职责:定义 InboundMessage,作为 channel → handler 的唯一入站结构。 禁止:在本文件调企微换 ID、写六态、调 LLM。 """ from __future__ import annotations from dataclasses import dataclass, field from typing import Any @dataclass(frozen=True) class InboundMessage: """ 归一后的入站消息(只读)。 sender_id:必须来自入站报文 FromUserName / AIBOT 发送人,禁止事后向企微拉取。 sender_id 为空时不得入业务队列。 message_id:全局去重键;缺省时由调用方用 msg_id/CreateTime 拼。 """ sender_id: str message_id: str content: str msg_type: str = "text" chat_id: str = "" chat_type: str = "single" # single | group agent_id: str = "" create_time: int = 0 media: dict[str, Any] = field(default_factory=dict) raw: dict[str, Any] = field(default_factory=dict) def has_sender(self) -> bool: """是否具备可进入业务的发送人身份。""" return bool(self.sender_id and self.sender_id.strip()) def to_dict(self) -> dict[str, Any]: """序列化进 PG JSONB;不含密钥。""" return { "sender_id": self.sender_id, "message_id": self.message_id, "content": self.content, "msg_type": self.msg_type, "chat_id": self.chat_id, "chat_type": self.chat_type, "agent_id": self.agent_id, "create_time": self.create_time, "media": self.media, "raw": self.raw, } @classmethod def from_dict(cls, data: dict[str, Any]) -> "InboundMessage": """从 JSONB / dict 还原。""" return cls( sender_id=str(data.get("sender_id") or ""), message_id=str(data.get("message_id") or ""), content=str(data.get("content") or ""), msg_type=str(data.get("msg_type") or "text"), chat_id=str(data.get("chat_id") or ""), chat_type=str(data.get("chat_type") or "single"), agent_id=str(data.get("agent_id") or ""), create_time=int(data.get("create_time") or 0), media=dict(data.get("media") or {}), raw=dict(data.get("raw") or {}), )