Coverage for agentos/channels/message.py: 0%
67 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-02 09:59 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-02 09:59 +0800
1"""
2AgentOS Channels — 统一消息模型与渠道协议。
4所有渠道(微信/企业微信/飞书/钉钉/QQ)的消息
5均归一化为 ChannelMessage,确保 Agent Engine 零差异处理。
6"""
8from __future__ import annotations
10import json
11import time
12from dataclasses import dataclass, field
13from enum import Enum
14from typing import Optional
17class ChannelType(str, Enum):
18 """渠道类型标识。"""
19 WECHAT_MP = "wechat-mp" # 微信公众号
20 WECOM = "wecom" # 企业微信
21 FEISHU = "feishu" # 飞书
22 DINGTALK = "dingtalk" # 钉钉
23 QQ = "qq" # QQ 机器人
24 SLACK = "slack" # Slack
25 DISCORD = "discord" # Discord
26 TELEGRAM = "telegram" # Telegram
27 WHATSAPP = "whatsapp" # WhatsApp Business
28 LINE = "line" # LINE Messaging
29 WEB = "web" # Web 端
30 MOBILE = "mobile" # 移动端
33class MessageType(str, Enum):
34 """消息类型。"""
35 TEXT = "text" # 文本
36 IMAGE = "image" # 图片
37 VOICE = "voice" # 语音
38 VIDEO = "video" # 视频
39 FILE = "file" # 文件
40 LOCATION = "location" # 位置
41 LINK = "link" # 链接
42 EVENT = "event" # 事件(关注/点击菜单等)
43 MINIPROGRAM = "miniprogram" # 小程序卡片
46@dataclass
47class ConversationContext:
48 """会话上下文 — 跨渠道统一。"""
49 channel: ChannelType
50 user_id: str # 渠道内用户唯一标识
51 session_id: str # AgentOS 内会话 ID
52 channel_config: dict = field(default_factory=dict)
53 metadata: dict = field(default_factory=dict)
54 history: list[dict] = field(default_factory=list) # 最近 N 轮对话
57@dataclass
58class ChannelMessage:
59 """统一消息模型 — 所有渠道归一化为此格式。
61 设计原则:
62 - 字段命名中性,不偏袒任何渠道
63 - 渠道特有数据塞入 extra
64 - Agent Engine 只看本模型,不感知渠道差异
65 """
67 msg_id: str # 渠道内消息唯一 ID
68 channel: ChannelType # 来源渠道
69 msg_type: MessageType = MessageType.TEXT
70 content: str = "" # 文本内容(image/voice 等为 media_url)
71 sender_id: str = "" # 发送者 ID
72 sender_name: str = "" # 发送者昵称
73 timestamp: float = field(default_factory=time.time)
74 conversation_id: str = "" # 渠道内会话/群 ID
75 reply_token: str = "" # 渠道回复令牌(用于被动回复)
76 session_id: str = "" # AgentOS 会话 ID
77 media_url: str = "" # 多媒体 URL
78 media_id: str = "" # 渠道媒体 ID(用于下载)
79 extra: dict = field(default_factory=dict) # 渠道特有字段
81 @property
82 def is_from_mobile(self) -> bool:
83 """是否来自移动端渠道。"""
84 return self.channel in (ChannelType.MOBILE, ChannelType.WECHAT_MP, ChannelType.QQ)
86 @property
87 def is_group_chat(self) -> bool:
88 """是否群聊消息。"""
89 return self.extra.get("is_group", False)
91 @property
92 def display_source(self) -> str:
93 """人类可读的来源标识。"""
94 labels = {
95 ChannelType.WECHAT_MP: "微信",
96 ChannelType.WECOM: "企业微信",
97 ChannelType.FEISHU: "飞书",
98 ChannelType.DINGTALK: "钉钉",
99 ChannelType.QQ: "QQ",
100 ChannelType.WEB: "Web",
101 ChannelType.MOBILE: "手机",
102 }
103 return labels.get(self.channel, self.channel.value)
105 def to_dict(self) -> dict:
106 return {
107 "msg_id": self.msg_id,
108 "channel": self.channel.value,
109 "msg_type": self.msg_type.value,
110 "content": self.content,
111 "sender_id": self.sender_id,
112 "sender_name": self.sender_name,
113 "timestamp": self.timestamp,
114 "conversation_id": self.conversation_id,
115 "session_id": self.session_id,
116 "media_url": self.media_url,
117 "extra": self.extra,
118 }
120 @classmethod
121 def from_dict(cls, d: dict) -> "ChannelMessage":
122 return cls(
123 msg_id=d.get("msg_id", ""),
124 channel=ChannelType(d.get("channel", "web")),
125 msg_type=MessageType(d.get("msg_type", "text")),
126 content=d.get("content", ""),
127 sender_id=d.get("sender_id", ""),
128 sender_name=d.get("sender_name", ""),
129 timestamp=d.get("timestamp", time.time()),
130 conversation_id=d.get("conversation_id", ""),
131 reply_token=d.get("reply_token", ""),
132 session_id=d.get("session_id", ""),
133 media_url=d.get("media_url", ""),
134 media_id=d.get("media_id", ""),
135 extra=d.get("extra", {}),
136 )