"""状态 DTO。 定义消息状态事件的枚举与不可变值对象,包括事件类型、消息状态与状态 负载。所有枚举继承 ``str, Enum`` 以支持 JSON 序列化,DTO 均为 ``dataclass(frozen=True)``,仅依赖标准库,用于渠道侧消息状态事件 (送达 / 已读 / 编辑 / 撤回等)的回传与状态机推进。 """ from __future__ import annotations from dataclasses import dataclass from datetime import datetime from enum import StrEnum from typing import Any from yuxi.channels.contract.errors import ValidationError class EventType(StrEnum): """事件类型。 标识渠道侧回传的消息状态事件类型,用于状态事件解析与状态机推进。 继承 ``str, Enum`` 以支持 JSON 序列化与字符串比较。 取值: MESSAGE: 消息事件。 DELIVERED: 已送达。 READ: 已读。 EDITED: 已编辑。 RECALLED: 已撤回。 UNKNOWN: 未知事件。 AGENT_MENTION: Agent 提及事件(多 Agent 协作,标识 @Agent 提及)。 """ MESSAGE = "message" DELIVERED = "delivered" READ = "read" EDITED = "edited" RECALLED = "recalled" UNKNOWN = "unknown" AGENT_MENTION = "agent_mention" class MessageStatus(StrEnum): """消息状态。 标识出站消息在渠道侧的状态,用于出站状态机推进与持久化。继承 ``str, Enum`` 以支持 JSON 序列化与字符串比较。 取值: PENDING: 待发送。 SENT: 已发送。 DELIVERED: 已送达。 READ: 已读。 FAILED: 已失败。 """ PENDING = "pending" SENT = "sent" DELIVERED = "delivered" READ = "read" FAILED = "failed" @dataclass(frozen=True) class StatusPayload: """状态负载。 描述渠道侧回传的消息状态事件负载,包括事件类型、关联的渠道消息 ID 与事件时间戳,用于状态事件解析与状态机推进。 字段: event_type: 事件类型。 ref_channel_msg_id: 关联的渠道消息 ID(通讯录变更等无消息 ID 的 事件可为空字符串,调用方按 ``event_type`` 区分)。 timestamp: 事件时间戳。 metadata: 渠道侧扩展信息(可选,如通讯录变更的 ``change_type``、 客服事件的 ``external_userid`` 等)。 """ event_type: EventType ref_channel_msg_id: str timestamp: datetime metadata: dict[str, Any] | None = None def __post_init__(self) -> None: """校验必填字段。 ``event_type`` 与 ``timestamp`` 必填;``ref_channel_msg_id`` 对 ``MESSAGE`` 事件必须非空,对通讯录变更等非消息事件允许为空 (由调用方按 ``event_type`` 区分)。空 ``ref_channel_msg_id`` 仅在 ``event_type != MESSAGE`` 时允许。 """ if not self.ref_channel_msg_id and self.event_type == EventType.MESSAGE: raise ValidationError("ref_channel_msg_id", "must not be empty for MESSAGE event")