# 事件模型

## 概述

SDK Core 提供发布-订阅事件模型，用于接收服务端推送的实时通知。事件通过 WebSocket 以 JSON-RPC 2.0 Notification 格式送达，SDK 自动分发到注册的处理器。

## 订阅与取消

```python
# 订阅
sub = client.on("message.received", handler)

# 取消订阅
sub.unsubscribe()
```

处理器支持同步和异步函数：

```python
# 同步处理器
def on_message(payload):
    print(payload)

# 异步处理器
async def on_message(payload):
    await process(payload)
```

同一事件可注册多个处理器，按注册顺序依次调用。

## 事件类型

### 连接事件（SDK Core 内部发布）

| 事件 | 触发时机 | payload |
|------|---------|---------|
| `connection.challenge` | 建立 WebSocket 后收到网关 challenge | `{"nonce": str, ...}` |
| `connection.state` | 连接状态变化 | `{"state": str, "gateway"?: str, "error"?: Exception, "attempt"?: int}` |
| `connection.error` | 连接错误 | `{"error": Exception, "attempt"?: int}` |
| `token.refreshed` | 令牌刷新成功 | `{"aid": str, "expires_at": int}` |

### 消息事件（服务端推送）

| 事件 | 触发时机 | payload |
|------|---------|---------|
| `message.received` | 收到新消息 | `{"message_id", "from", "to", "type", "payload", "seq", "timestamp", "persist", "encrypted"}` |
| `message.recalled` | 消息被撤回 | `{"from", "to", "message_ids": [...], "timestamp"}` |
| `message.ack` | 消息被对方确认 | `{"to", "ack_seq", "timestamp"}` |

### 群组事件（服务端推送）

| 事件 | 触发时机 | payload |
|------|---------|---------|
| `group.created` | 群组创建 | `{"module_id", "group_id", "owner_aid", "visibility"}` |
| `group.changed` | 群组状态变化 | `{"module_id", "action", "group_id"}` |
| `group.message_created` | 群消息创建 | `{"module_id", "group_id", "seq", "message_id", "sender_aid", "type"}` |

### 存储事件（服务端推送）

| 事件 | 触发时机 | payload |
|------|---------|---------|
| `storage.object_changed` | 对象变更 | `{"module_id", "action", "owner_aid", "object_key"}` |

### 其他通知

除上表外，`RPCTransport` 会将任意 `event/...` 通知按“去掉 `event/` 前缀后的名字”直接分发。

例如：
- `event/group.created` → `client.on("group.created", ...)`
- `event/group.message_created` → `client.on("group.message_created", ...)`
- `event/storage.object_changed` → `client.on("storage.object_changed", ...)`

当前 Python SDK 内部只对少数高频事件做了显式映射：
- `message.received`
- `message.recalled`
- `message.ack`
- `group.changed`

其余事件虽然没有单独的映射表项，但仍可按原始协议事件名去掉 `event/` 前缀后订阅。

## 事件传输格式

服务端通过 WebSocket 发送 JSON-RPC 2.0 Notification：

```json
{
    "jsonrpc": "2.0",
    "method": "event/message.received",
    "params": {
        "message_id": "uuid",
        "from": "my-agent.aid.com",
        "to": "peer-agent.aid.com",
        "type": "text",
        "payload": {"text": "Hello!"},
        "seq": 42,
        "timestamp": 1234567890000
    }
}
```

SDK Core 会将 `event/message.received` 映射为事件名 `message.received`，并将 `params` 作为 payload 分发。对未显式映射的 `event/...`，SDK 也会直接使用去前缀后的名称进行分发。

## 典型用法

### 实时消息接收

```python
async def on_message(msg):
    print(f"来自 {msg['from']}: {msg['payload']}")

client.on("message.received", on_message)
```

### 连接状态监控

```python
def on_state(ev):
    print(f"连接状态: {ev['state']}")
    if ev.get("error"):
        print(f"错误: {ev['error']}")

client.on("connection.state", on_state)
```

### Challenge 监听

```python
def on_challenge(ev):
    print(f"收到网关 challenge: {ev['nonce']}")

client.on("connection.challenge", on_challenge)
```

### 令牌刷新感知

```python
def on_token(ev):
    print(f"令牌已刷新，新过期时间: {ev['expires_at']}")

client.on("token.refreshed", on_token)
```

## 注意事项

- 事件处理器中的异常不会中断事件分发，但当前实现会静默吞掉，不会自动记录日志
- 事件分发是异步的，处理器执行顺序按注册顺序，但不阻塞后续事件
- 连接断开期间的事件会丢失，重连后需通过 `pull` 方法补拉丢失的消息
- 未识别的普通通知不会丢弃，而是通过 `notification` 事件原样分发
