ForcePilot/backend/package/yuxi/channels/contract/dtos/status.py

96 lines
3.0 KiB
Python
Raw Normal View History

"""状态 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")