# 2026-06-03 变更记录

## [fix][test] 修复事件反序列化时 extra_body 混入已声明字段的问题

### 变更内容
- 将 `src/event.rs` 中四类外层事件结构体改为手写 `Deserialize`，先反序列化完整事件对象，再依据已解析出的事件类型筛除已声明字段
- 保留 `extra_body` 对未知字段的兜底收集能力，同时避免 `time`、`self_id` 以及各类事件的判别字段、业务字段重复出现在 `extra_body`
- 新增消息事件与通知事件的回归测试，覆盖 `extra_body` 仅保留未知字段的预期行为

### 涉及文件
- `src/event.rs` [Changed] 重写事件外层反序列化逻辑并新增针对 `extra_body` 的单元测试
- `changelogs/2026-06-03.md` [Added] 记录本次事件反序列化修复内容

### 影响
- 调用方读取 `EventMessage`、`EventNotice`、`EventRequest`、`EventMetaEvent` 的 `extra_body` 时，将只看到协议中未声明的额外字段，不再混入已定义字段
- 现有通过结构体字段访问标准事件内容的用法不受影响，仅修正 `extra_body` 的内容边界

### 原因
- `serde(flatten)` 与内部标签枚举组合使用时，外层 `HashMap` 兜底字段收集会把已经参与事件分派和字段映射的键再次留下，导致 `extra_body` 语义失真
- 需要让 `extra_body` 更准确地承担“保留未知字段”职责，避免调用方重复判断或误用标准字段

## [refactor][BREAKING] 将消息段枚举改为匿名字段变体

### 变更内容
- 将 `ReceiveSegment` 与 `SendSegment` 的所有变体从 `Variant { data: ... }` 改为 `Variant(...)` 匿名字段形式
- 将两个枚举的 serde 标注调整为邻接标签形式 `#[serde(tag = "type", content = "data")]`，保持 OneBot 消息段在线上仍使用 `type + data` 结构
- 调整 `SendSegmentData::into_send_segment` 的全部构造逻辑，并修正 `src/event/message.rs` 中 `At` 消息段的直接构造写法
- 新增消息段序列化与反序列化回归测试，覆盖文本段、嵌套 `node` 段和空数据段

### 涉及文件
- `src/message/receive_segment.rs` [Changed] 将接收消息段枚举改为匿名字段变体并新增反序列化测试
- `src/message/send_segment.rs` [Changed] 将发送消息段枚举改为匿名字段变体并新增序列化测试
- `src/event/message.rs` [Changed] 同步更新 `QuickReplyAt` 中 `SendSegment::At` 的构造方式
- `changelogs/2026-06-03.md` [Changed] 追加记录本次消息段枚举重构

### 影响
- Rust 调用方若手写构造或模式匹配消息段枚举，需要从 `SendSegment::Text { data }` / `ReceiveSegment::Text { data }` 迁移到 `SendSegment::Text(data)` / `ReceiveSegment::Text(data)`
- OneBot 协议层的 JSON 结构保持不变，发送与接收仍使用 `{"type":"...","data":{...}}` 形式
- 启用 `selector` feature 时，`ReceiveSegment` 现在符合过程宏针对单匿名字段枚举的选择器生成形态

### 原因
- 匿名字段变体更贴合“枚举包装数据体”的语义，也让构造与匹配写法更简洁
- 需要在不改变协议线上数据结构的前提下，完成 Rust 层公开 API 的枚举写法重构

## [refactor][build][BREAKING] 调整 selector 宏导入路径

### 变更内容
- 将事件模块和消息段枚举上的 `Selector` derive 改为直接使用 `onebot_api_macros::Selector`，不再依赖 crate 根的中转导出
- 删除 `src/lib.rs` 中仅供过程宏 derive 使用的 `pub use onebot_api_macros::Selector;`，统一宏引用入口
- 保持 `selector` feature 下的宏展开行为不变，仅收敛公开 API 表面

### 涉及文件
- `src/event.rs` [Changed] 将事件类型上的 `Selector` derive 导入切换为过程宏 crate 直引
- `src/event/meta.rs` [Changed] 将 meta 事件上的 `Selector` derive 导入切换为过程宏 crate 直引
- `src/event/notice.rs` [Changed] 将 notice 事件上的 `Selector` derive 导入切换为过程宏 crate 直引
- `src/event/request.rs` [Changed] 将 request 事件上的 `Selector` derive 导入切换为过程宏 crate 直引
- `src/message/receive_segment.rs` [Changed] 将消息段枚举上的 `Selector` derive 路径切换为过程宏 crate 直引
- `src/lib.rs` [Changed] 移除 crate 根对 `Selector` 过程宏的公开 re-export
- `changelogs/2026-06-03.md` [Changed] 追加记录 selector 宏导入路径调整

### 影响
- 依赖 `onebot-api::Selector` 路径手动引用该过程宏的调用方需要迁移到 `onebot_api_macros::Selector`
- crate 内部启用 `selector` feature 的派生逻辑保持不变，不影响既有事件筛选功能

### 原因
- 过程宏直接从定义 crate 引入更清晰，也避免库根为内部 derive 场景维持额外的公开导出
- 配合消息段与事件类型的重构，统一 selector 宏使用方式，减少后续维护分叉

## [build] 收紧 tokio 依赖的启用 features

### 变更内容
- 根据主 crate 中实际使用的 `tokio` API，将根 `Cargo.toml` 的 `tokio` features 从 `full` 收紧为 `macros`、`rt-multi-thread`、`sync`、`time`、`net`、`io-util`
- 保留示例与文档示例所需的 `#[tokio::main]`、`tokio::runtime::Runtime::new()` 支持，同时覆盖通信层对 `spawn`、`JoinHandle`、`oneshot`、`broadcast`、`TcpListener`、`TcpStream`、`sleep`、`timeout` 和 `AsyncRead/AsyncWrite` 的依赖
- 将 `macros/Cargo.toml` 中 dev-dependencies 的 `tokio` 也从 `full` 调整为显式 features，避免过程宏 crate 的测试环境继续拉入整套运行时能力

### 涉及文件
- `Cargo.toml` [Changed] 将主 crate 的 `tokio` 依赖改为显式 features 列表，替代 `full`
- `macros/Cargo.toml` [Changed] 将过程宏 crate 的 dev-dependency `tokio` 改为显式 features 列表
- `changelogs/2026-06-03.md` [Changed] 追加记录本次 `tokio` feature 收紧调整

### 影响
- 构建时不再默认启用 `tokio` 的全部能力，依赖面更清晰，也有助于控制编译开销和潜在特性耦合
- 对外 API 与现有通信实现行为保持不变；若后续新增 `tokio` API，需要同步补充对应 feature

### 原因
- 当前 manifest 使用 `full` 过于宽泛，而项目实际只依赖少量明确的 runtime、同步、网络和时间相关能力
- 将 `tokio` features 显式化后，更便于维护者审查依赖边界，并避免无意中依赖未声明能力

## [build][chore] 更新 crate 版本与发布元数据

### 变更内容
- 将主 crate `onebot-api` 版本从 `1.2.4` 提升到 `1.2.5`，并同步将过程宏 crate `onebot-api-macros` 版本从 `0.1.1` 提升到 `0.1.2`
- 将根 `Cargo.toml` 中对过程宏 crate 的路径依赖版本要求同步更新到 `0.1.2`，保持包内版本声明一致
- 更新主 crate 的作者邮箱与描述文案，并移除重复声明的 `license-file` 字段，仅保留 SPDX `license = "MIT"`

### 涉及文件
- `Cargo.toml` [Changed] 更新主 crate 版本、过程宏依赖版本以及发布元数据字段
- `macros/Cargo.toml` [Changed] 更新过程宏 crate 版本号
- `changelogs/2026-06-03.md` [Changed] 追加记录本次版本与 manifest 元数据调整

### 影响
- 后续发布时主 crate 与过程宏 crate 的版本号将保持同步递增，便于 crates.io 与依赖方识别本次发布内容
- crate 首页展示信息与作者联系方式会更新为新的发布元数据；对运行时行为无直接影响

### 原因
- 本次依赖与元数据调整需要通过新版本号对外发布，避免包版本与实际 manifest 内容不一致
- 清理与完善发布元数据可以减少重复声明，提升包信息的准确性与可维护性

## [fix][ci] 修复主 crate 与宏 crate 同步升版本时的发布脚本失败问题

### 变更内容
- 修正 `scripts/publish-release.ts` 中对子进程 `stdout`/`stderr` 的 `pipeTo` 用法，为标准输出启用 `preventClose` 并等待转发任务结束，避免发布 `macros` 后关闭 Deno 自身输出资源
- 保留 `macros` crate 先发布并等待 crates.io 建索引的流程，确保主 crate 在依赖新宏版本时仍能通过 `cargo publish` 校验
- 将 GitHub release 创建调整到主 crate 发布完成之后，并在脚本中补充按 tag 查询的幂等处理，避免重跑 CI 时因 release 已存在再次失败

### 涉及文件
- `scripts/publish-release.ts` [Changed] 修复子进程输出转发导致的 `BadResource` 异常，并让 release 创建顺序与重试行为更稳健
- `changelogs/2026-06-03.md` [Changed] 追加记录本次发布脚本与 CI 流程修复

### 影响
- 当 `onebot-api-macros` 与主 crate 在同一次提交中同时升版本时，CI 发布流程会先稳定发布宏 crate、等待索引，再继续发布主 crate，不会再在等待阶段因为输出资源被关闭而失败
- 若主 crate 已发布但 release/tag 已存在，后续重跑 workflow 会跳过重复创建，减少人工清理失败状态的需要

### 原因
- 现有脚本在转发 `cargo publish` 输出时默认关闭了 Deno 的标准输出资源，导致后续 `console.log` 在等待宏 crate 建索引阶段触发 `BadResource`
- 发布 release 的时机早于主 crate 成功发布，失败重跑时也缺少已存在 release 的容错，容易把一次暂时性失败放大成持续性 CI 故障
