mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-05 05:16:03 +00:00
562 lines
15 KiB
Markdown
562 lines
15 KiB
Markdown
# 统一事件体系
|
||
|
||
## 1. 设计原则
|
||
|
||
- **命名空间分类**:事件类型采用 `{namespace}.{action}` 格式,如 `message.received`
|
||
- **通用优先**:大部分平台都支持的事件抽象为通用事件,定义统一的字段格式
|
||
- **平台特有事件标准化**:各适配器的独有事件通过 `PlatformSpecificEvent` 承载,保留原始数据
|
||
- **向后兼容**:现有 `FriendMessage` / `GroupMessage` 通过兼容层映射到新的 `message.received` 事件
|
||
|
||
## 2. 事件基类层次
|
||
|
||
```
|
||
Event (事件基类)
|
||
├── MessageEvent (消息相关事件)
|
||
│ ├── MessageReceivedEvent # message.received
|
||
│ ├── MessageEditedEvent # message.edited
|
||
│ ├── MessageDeletedEvent # message.deleted
|
||
│ └── MessageReactionEvent # message.reaction
|
||
├── FeedbackEvent (用户反馈事件)
|
||
│ └── FeedbackReceivedEvent # feedback.received
|
||
├── GroupEvent (群组相关事件)
|
||
│ ├── MemberJoinedEvent # group.member_joined
|
||
│ ├── MemberLeftEvent # group.member_left
|
||
│ ├── MemberBannedEvent # group.member_banned
|
||
│ ├── MemberUnbannedEvent # group.member_unbanned
|
||
│ └── GroupInfoUpdatedEvent # group.info_updated
|
||
├── FriendEvent (好友相关事件)
|
||
│ ├── FriendRequestReceivedEvent # friend.request_received
|
||
│ ├── FriendAddedEvent # friend.added
|
||
│ └── FriendRemovedEvent # friend.removed
|
||
├── BotEvent (Bot 状态事件)
|
||
│ ├── BotInvitedToGroupEvent # bot.invited_to_group
|
||
│ ├── BotRemovedFromGroupEvent # bot.removed_from_group
|
||
│ ├── BotMutedEvent # bot.muted
|
||
│ └── BotUnmutedEvent # bot.unmuted
|
||
└── PlatformSpecificEvent # platform.{adapter}.{action}
|
||
```
|
||
|
||
## 3. 通用事件定义
|
||
|
||
### 3.1 事件基类
|
||
|
||
```python
|
||
class Event(pydantic.BaseModel):
|
||
"""事件基类"""
|
||
|
||
type: str
|
||
"""事件类型标识,如 'message.received'"""
|
||
|
||
timestamp: float
|
||
"""事件发生的时间戳"""
|
||
|
||
bot_uuid: str
|
||
"""接收到此事件的 Bot UUID"""
|
||
|
||
adapter_name: str
|
||
"""产生此事件的适配器名称"""
|
||
|
||
source_platform_object: typing.Optional[typing.Any] = None
|
||
"""原始平台事件对象,供适配器内部使用"""
|
||
```
|
||
|
||
### 3.2 消息事件
|
||
|
||
#### MessageReceivedEvent (`message.received`)
|
||
|
||
收到新消息。这是最核心的事件,替代现有的 `FriendMessage` / `GroupMessage`。
|
||
|
||
```python
|
||
class MessageReceivedEvent(Event):
|
||
"""收到新消息"""
|
||
|
||
type: str = "message.received"
|
||
|
||
message_id: typing.Union[int, str]
|
||
"""消息 ID"""
|
||
|
||
message_chain: MessageChain
|
||
"""消息内容"""
|
||
|
||
sender: User
|
||
"""发送者"""
|
||
|
||
chat_type: ChatType # "private" | "group"
|
||
"""会话类型"""
|
||
|
||
chat_id: typing.Union[int, str]
|
||
"""会话 ID(私聊为对方用户 ID,群聊为群 ID)"""
|
||
|
||
group: typing.Optional[Group] = None
|
||
"""群信息(仅群聊时存在)"""
|
||
```
|
||
|
||
与现有类型的映射关系:
|
||
- `chat_type == "private"` → 等价于现有 `FriendMessage`
|
||
- `chat_type == "group"` → 等价于现有 `GroupMessage`
|
||
|
||
`ChatType` 枚举:
|
||
|
||
```python
|
||
class ChatType(str, Enum):
|
||
PRIVATE = "private"
|
||
GROUP = "group"
|
||
```
|
||
|
||
#### MessageEditedEvent (`message.edited`)
|
||
|
||
消息被编辑。
|
||
|
||
```python
|
||
class MessageEditedEvent(Event):
|
||
"""消息被编辑"""
|
||
|
||
type: str = "message.edited"
|
||
|
||
message_id: typing.Union[int, str]
|
||
"""被编辑的消息 ID"""
|
||
|
||
new_content: MessageChain
|
||
"""编辑后的新内容"""
|
||
|
||
editor: User
|
||
"""编辑者"""
|
||
|
||
chat_type: ChatType
|
||
chat_id: typing.Union[int, str]
|
||
group: typing.Optional[Group] = None
|
||
```
|
||
|
||
#### MessageDeletedEvent (`message.deleted`)
|
||
|
||
消息被删除/撤回。
|
||
|
||
```python
|
||
class MessageDeletedEvent(Event):
|
||
"""消息被删除/撤回"""
|
||
|
||
type: str = "message.deleted"
|
||
|
||
message_id: typing.Union[int, str]
|
||
"""被删除的消息 ID"""
|
||
|
||
operator: typing.Optional[User] = None
|
||
"""操作者(可能是发送者自己撤回,也可能是管理员删除)"""
|
||
|
||
chat_type: ChatType
|
||
chat_id: typing.Union[int, str]
|
||
group: typing.Optional[Group] = None
|
||
```
|
||
|
||
#### MessageReactionEvent (`message.reaction`)
|
||
|
||
消息收到表情回应。
|
||
|
||
```python
|
||
class MessageReactionEvent(Event):
|
||
"""消息收到表情回应"""
|
||
|
||
type: str = "message.reaction"
|
||
|
||
message_id: typing.Union[int, str]
|
||
"""被回应的消息 ID"""
|
||
|
||
user: User
|
||
"""回应者"""
|
||
|
||
reaction: str
|
||
"""回应的表情标识(emoji 或平台特定表情 ID)"""
|
||
|
||
is_add: bool
|
||
"""True 为添加回应,False 为移除回应"""
|
||
|
||
chat_type: ChatType
|
||
chat_id: typing.Union[int, str]
|
||
group: typing.Optional[Group] = None
|
||
```
|
||
|
||
### 3.3 用户反馈事件
|
||
|
||
#### FeedbackReceivedEvent (`feedback.received`)
|
||
|
||
用户对 Bot 回复提交反馈。该事件用于承载平台提供的点赞、点踩、取消反馈以及点踩原因等评价信息;典型来源包括企业微信 AI Bot 的 `feedback_event`、飞书卡片按钮回调、Web Embed 的反馈入口等。
|
||
|
||
```python
|
||
class FeedbackReceivedEvent(Event):
|
||
"""收到用户反馈"""
|
||
|
||
type: str = "feedback.received"
|
||
|
||
feedback_id: str
|
||
"""平台侧反馈 ID,用于幂等记录或取消反馈"""
|
||
|
||
feedback_type: int
|
||
"""1 = like, 2 = dislike, 3 = cancel/remove feedback"""
|
||
|
||
feedback_content: typing.Optional[str] = None
|
||
"""用户填写的自由文本反馈"""
|
||
|
||
inaccurate_reasons: typing.Optional[list[str]] = None
|
||
"""点踩时平台提供的预设不准确原因"""
|
||
|
||
user_id: typing.Optional[str] = None
|
||
"""提交反馈的用户 ID"""
|
||
|
||
session_id: typing.Optional[str] = None
|
||
"""会话 ID,例如 person_xxx 或 group_xxx"""
|
||
|
||
message_id: typing.Optional[str] = None
|
||
"""被评价的 Bot 回复消息 ID"""
|
||
|
||
stream_id: typing.Optional[str] = None
|
||
"""流式回复 ID,用于关联 streaming response"""
|
||
```
|
||
|
||
设计约定:
|
||
|
||
- `feedback_id` 是幂等键;同一个 `feedback_id` 的后续事件应更新已有记录。
|
||
- `feedback_type == 3` 表示用户取消/移除反馈,处理器可删除对应记录或标记为取消。
|
||
- 如果平台只能给出原始回调 payload,差异字段保留在 `source_platform_object` 或 `PlatformSpecificEvent.data` 中;通用字段仍优先映射到 `FeedbackReceivedEvent`。
|
||
- 该事件保留向后兼容映射:EBA 事件可转换为旧的 `FeedbackEvent`,字段语义保持一致。
|
||
|
||
### 3.4 群组事件
|
||
|
||
#### MemberJoinedEvent (`group.member_joined`)
|
||
|
||
新成员加入群组。
|
||
|
||
```python
|
||
class MemberJoinedEvent(Event):
|
||
"""新成员加入群组"""
|
||
|
||
type: str = "group.member_joined"
|
||
|
||
group: Group
|
||
"""群组"""
|
||
|
||
member: User
|
||
"""加入的成员"""
|
||
|
||
inviter: typing.Optional[User] = None
|
||
"""邀请者(如有)"""
|
||
|
||
join_type: typing.Optional[str] = None
|
||
"""加入方式:'invite' / 'request' / 'direct' / None"""
|
||
```
|
||
|
||
#### MemberLeftEvent (`group.member_left`)
|
||
|
||
成员离开群组。
|
||
|
||
```python
|
||
class MemberLeftEvent(Event):
|
||
"""成员离开群组"""
|
||
|
||
type: str = "group.member_left"
|
||
|
||
group: Group
|
||
member: User
|
||
|
||
is_kicked: bool = False
|
||
"""是否被踢出"""
|
||
|
||
operator: typing.Optional[User] = None
|
||
"""操作者(踢出时为管理员)"""
|
||
```
|
||
|
||
#### MemberBannedEvent (`group.member_banned`)
|
||
|
||
成员被禁言。
|
||
|
||
```python
|
||
class MemberBannedEvent(Event):
|
||
"""成员被禁言"""
|
||
|
||
type: str = "group.member_banned"
|
||
|
||
group: Group
|
||
member: User
|
||
operator: typing.Optional[User] = None
|
||
duration: typing.Optional[int] = None
|
||
"""禁言时长(秒),None 表示永久"""
|
||
```
|
||
|
||
#### MemberUnbannedEvent (`group.member_unbanned`)
|
||
|
||
成员被解除禁言。
|
||
|
||
```python
|
||
class MemberUnbannedEvent(Event):
|
||
"""成员被解除禁言"""
|
||
|
||
type: str = "group.member_unbanned"
|
||
|
||
group: Group
|
||
member: User
|
||
operator: typing.Optional[User] = None
|
||
```
|
||
|
||
#### GroupInfoUpdatedEvent (`group.info_updated`)
|
||
|
||
群组信息被修改。
|
||
|
||
```python
|
||
class GroupInfoUpdatedEvent(Event):
|
||
"""群组信息被修改"""
|
||
|
||
type: str = "group.info_updated"
|
||
|
||
group: Group
|
||
"""更新后的群组信息"""
|
||
|
||
operator: typing.Optional[User] = None
|
||
"""操作者"""
|
||
|
||
changed_fields: list[str] = []
|
||
"""发生变更的字段名列表,如 ['name', 'description']"""
|
||
```
|
||
|
||
### 3.5 好友事件
|
||
|
||
#### FriendRequestReceivedEvent (`friend.request_received`)
|
||
|
||
收到好友请求。
|
||
|
||
```python
|
||
class FriendRequestReceivedEvent(Event):
|
||
"""收到好友请求"""
|
||
|
||
type: str = "friend.request_received"
|
||
|
||
request_id: typing.Union[int, str]
|
||
"""请求 ID,用于后续 approve/reject 操作"""
|
||
|
||
user: User
|
||
"""请求者"""
|
||
|
||
message: typing.Optional[str] = None
|
||
"""验证消息"""
|
||
```
|
||
|
||
#### FriendAddedEvent (`friend.added`)
|
||
|
||
成功添加好友。
|
||
|
||
```python
|
||
class FriendAddedEvent(Event):
|
||
"""成功添加好友"""
|
||
|
||
type: str = "friend.added"
|
||
|
||
user: User
|
||
"""新好友"""
|
||
```
|
||
|
||
#### FriendRemovedEvent (`friend.removed`)
|
||
|
||
好友被移除。
|
||
|
||
```python
|
||
class FriendRemovedEvent(Event):
|
||
"""好友被移除"""
|
||
|
||
type: str = "friend.removed"
|
||
|
||
user: User
|
||
"""被移除的好友"""
|
||
```
|
||
|
||
### 3.6 Bot 状态事件
|
||
|
||
#### BotInvitedToGroupEvent (`bot.invited_to_group`)
|
||
|
||
Bot 被邀请加入群组。
|
||
|
||
```python
|
||
class BotInvitedToGroupEvent(Event):
|
||
"""Bot 被邀请加入群组"""
|
||
|
||
type: str = "bot.invited_to_group"
|
||
|
||
group: Group
|
||
inviter: typing.Optional[User] = None
|
||
|
||
request_id: typing.Optional[typing.Union[int, str]] = None
|
||
"""邀请请求 ID,某些平台需要 Bot 确认才加入"""
|
||
```
|
||
|
||
#### BotRemovedFromGroupEvent (`bot.removed_from_group`)
|
||
|
||
Bot 被移出群组。
|
||
|
||
```python
|
||
class BotRemovedFromGroupEvent(Event):
|
||
"""Bot 被移出群组"""
|
||
|
||
type: str = "bot.removed_from_group"
|
||
|
||
group: Group
|
||
operator: typing.Optional[User] = None
|
||
```
|
||
|
||
#### BotMutedEvent / BotUnmutedEvent (`bot.muted` / `bot.unmuted`)
|
||
|
||
Bot 被禁言/解除禁言。
|
||
|
||
```python
|
||
class BotMutedEvent(Event):
|
||
"""Bot 被禁言"""
|
||
|
||
type: str = "bot.muted"
|
||
|
||
group: Group
|
||
operator: typing.Optional[User] = None
|
||
duration: typing.Optional[int] = None
|
||
|
||
|
||
class BotUnmutedEvent(Event):
|
||
"""Bot 被解除禁言"""
|
||
|
||
type: str = "bot.unmuted"
|
||
|
||
group: Group
|
||
operator: typing.Optional[User] = None
|
||
```
|
||
|
||
### 3.7 平台特有事件
|
||
|
||
对于无法抽象为通用事件的平台特有事件,使用统一的 `PlatformSpecificEvent` 承载:
|
||
|
||
```python
|
||
class PlatformSpecificEvent(Event):
|
||
"""平台特有事件
|
||
|
||
适配器无法映射到通用事件类型时,使用此类型承载。
|
||
插件可以通过 adapter_name + action 来识别和处理。
|
||
"""
|
||
|
||
type: str = "platform.specific"
|
||
|
||
action: str
|
||
"""平台特有的事件动作标识,如 'channel_created', 'pin_message'"""
|
||
|
||
data: dict = {}
|
||
"""事件数据,结构由具体适配器定义"""
|
||
```
|
||
|
||
事件类型字符串格式为 `platform.{adapter_name}.{action}`,例如:
|
||
- `platform.telegram.chat_member_updated` — Telegram 的群成员信息更新
|
||
- `platform.discord.channel_created` — Discord 的频道创建
|
||
- `platform.discord.voice_state_update` — Discord 的语音状态变更
|
||
- `platform.slack.app_home_opened` — Slack 的 App Home 打开
|
||
|
||
## 4. 各平台事件支持矩阵
|
||
|
||
下表标注各通用事件在主要平台上的支持情况:
|
||
|
||
| 事件 | Telegram | Discord | OneBot(QQ) | 飞书 | 钉钉 | Slack | 微信 | LINE | KOOK |
|
||
|------|----------|---------|-----------|------|------|-------|------|------|------|
|
||
| `message.received` | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
||
| `message.edited` | Y | Y | N | Y | N | Y | N | N | Y |
|
||
| `message.deleted` | Y | Y | Y | Y | N | Y | Y | N | Y |
|
||
| `message.reaction` | Y | Y | Y | Y | Y | Y | N | N | Y |
|
||
| `feedback.received` | N | N | N | Y | N | N | Y | N | N |
|
||
| `group.member_joined` | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
||
| `group.member_left` | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
||
| `group.member_banned` | Y | Y | Y | N | N | N | N | N | N |
|
||
| `group.info_updated` | Y | Y | Y | Y | Y | Y | N | N | Y |
|
||
| `friend.request_received` | N | Y | Y | N | N | N | Y | Y | Y |
|
||
| `friend.added` | N | Y | Y | N | N | N | Y | Y | N |
|
||
| `bot.invited_to_group` | Y | Y | Y | Y | Y | Y | Y | N | Y |
|
||
| `bot.removed_from_group` | Y | Y | Y | Y | N | N | Y | N | Y |
|
||
| `bot.muted` | Y | N | Y | N | N | N | N | N | N |
|
||
| `bot.unmuted` | Y | N | Y | N | N | N | N | N | N |
|
||
| `platform.specific` | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
||
|
||
> 注:此表为初步评估,具体以各平台 SDK/API 文档为准,实施时逐个确认。
|
||
|
||
## 5. 事件生命周期
|
||
|
||
```
|
||
1. 平台 SDK 回调触发
|
||
│
|
||
2. 适配器 EventConverter.target2yiri(raw_event)
|
||
│ 将平台原生事件转换为统一 Event 对象
|
||
│ 无法映射的事件 → PlatformSpecificEvent
|
||
│
|
||
3. 适配器回调注册的 listener(event, adapter)
|
||
│
|
||
4. RuntimeBot 接收事件
|
||
│
|
||
5. EventBus 分发
|
||
│
|
||
6. EventRouter 查询 Bot 的 event_handlers 配置
|
||
│ 匹配事件类型 → 找到对应的 Handler
|
||
│ 支持通配符:'message.*' 匹配所有消息事件
|
||
│ 未匹配到 → 走默认 Handler(plugin,保持向后兼容)
|
||
│
|
||
7. Handler 处理事件
|
||
│ PipelineHandler → 进入 Pipeline 流水线
|
||
│ AgentHandler → 调用 RequestRunner
|
||
│ WebhookHandler → POST 到外部 URL
|
||
│ PluginHandler → 分发给插件 EventListener
|
||
│
|
||
8. Handler 执行完毕,可能通过 API 执行响应动作
|
||
(发消息、编辑消息、踢人、同意好友请求等)
|
||
```
|
||
|
||
## 6. 与现有事件类型的兼容映射
|
||
|
||
为保证现有插件不受影响,建立以下映射关系:
|
||
|
||
| 新事件 | 条件 | 旧事件 |
|
||
|--------|------|--------|
|
||
| `MessageReceivedEvent` (chat_type=private) | — | `FriendMessage` |
|
||
| `MessageReceivedEvent` (chat_type=group) | — | `GroupMessage` |
|
||
|
||
在插件 SDK 层面:
|
||
|
||
| 新事件 | 旧插件事件 |
|
||
|--------|-----------|
|
||
| `MessageReceivedEvent` (chat_type=private, 非命令) | `PersonNormalMessageReceived` |
|
||
| `MessageReceivedEvent` (chat_type=group, 非命令) | `GroupNormalMessageReceived` |
|
||
| `MessageReceivedEvent` (chat_type=private, 命令) | `PersonCommandSent` |
|
||
| `MessageReceivedEvent` (chat_type=group, 命令) | `GroupCommandSent` |
|
||
| `MessageReceivedEvent` (处理完毕后) | `NormalMessageResponded` |
|
||
|
||
兼容层在事件分发给插件 EventListener 时自动生成旧格式事件,确保监听旧事件类型的插件仍能正常工作。
|
||
|
||
## 7. 事件类型注册表
|
||
|
||
适配器在 manifest.yaml 中声明自己支持的事件类型:
|
||
|
||
```yaml
|
||
kind: MessagePlatformAdapter
|
||
metadata:
|
||
name: telegram
|
||
spec:
|
||
supported_events:
|
||
- message.received
|
||
- message.edited
|
||
- message.deleted
|
||
- message.reaction
|
||
- feedback.received
|
||
- group.member_joined
|
||
- group.member_left
|
||
- group.member_banned
|
||
- group.info_updated
|
||
- bot.invited_to_group
|
||
- bot.removed_from_group
|
||
- bot.muted
|
||
- bot.unmuted
|
||
- platform.specific
|
||
platform_specific_events:
|
||
- chat_member_updated
|
||
- chat_join_request
|
||
```
|
||
|
||
这份声明用于:
|
||
1. WebUI 在配置事件处理器时,只显示当前 Bot 的适配器支持的事件类型
|
||
2. EventRouter 在路由时校验事件类型有效性
|
||
3. 文档自动生成
|