nagisa/lib.rs
1//! Nagisa —— 统一在 OneBot v11 与 Milky 之上的 Rust 聊天机器人框架(门面 crate)。
2//!
3//! 这是面向使用者的唯一入口。bot 作者只依赖 `nagisa`(按需开 `onebot` / `milky`
4//! feature),写 `use nagisa::prelude::*;`,从不直接点名底层的 engine / types /
5//! macros / adapter 这些 crate:
6//!
7//! ```rust,ignore
8//! use nagisa::prelude::*;
9//!
10//! #[command("ping", mention_me)]
11//! async fn ping(reply: Reply) -> HandlerResult {
12//! reply.text("pong").await?;
13//! Ok(())
14//! }
15//!
16//! #[tokio::main]
17//! async fn main() -> Result<()> {
18//! // ctrl_c_shutdown() 起一个后台任务,收到 SIGINT/Ctrl-C 时取消 shutdown
19//! // token,让 bot 停机。
20//! let shutdown = ctrl_c_shutdown();
21//! App::new()
22//! .run_onebot(OneBotConfig::new("ws://127.0.0.1:8080/onebot/v11/ws"), shutdown)
23//! .await
24//! }
25//! ```
26//!
27//! # 门面做了什么
28//!
29//! - 把 engine([`nagisa_core`])、统一领域模型([`nagisa_types`])、宏
30//! ([`nagisa_macros`])在 crate 根重新导出,业务代码只点 `nagisa::…`。
31//! - 精选一个 [`prelude`] —— `use nagisa::prelude::*;` 这一行 glob 导入,带进宏、
32//! [`App`] 构建器、[`Bot`] 句柄、常用 `FromContext` 提取器、统一的
33//! `Segment` / `Message` / `Peer` / `Uin` / `Result` / `Error`,以及门控/规则
34//! 组合子。
35//! - 协议适配器(`OneBotConfig` / `MilkyConfig`)挂在 `onebot` / `milky` feature
36//! 后面,日志工具箱(`nagisa::log`)挂在 `log` feature 后面,排版引擎
37//! (`nagisa::render` —— 文档排版成图片)挂在 `render` feature 后面。
38//! - 提供 [`App`] 构建器([`app`]),把适配器、分发循环、自动登记的 handler 和一个
39//! [`ShutdownToken`] 接到一起。
40//!
41//! 适配器作者用的管线(新协议适配器要实现的出站动作 trait)在 [`adapter`];
42//! engine 内部的对象安全机制在 [`engine`]。业务代码两者都不点名。
43#![forbid(unsafe_code)]
44
45pub mod app;
46
47pub use app::App;
48
49// ── Engine 层(nagisa-core) ────────────────────────────────────────────────────
50// 分发引擎、`Bot` 句柄、两个传输 trait、`FromContext` 提取器、matcher DSL、注册表
51// 和服务生命周期。在 crate 根重新导出,业务代码不直接点 `nagisa_core` 路径。
52pub use nagisa_core::{
53 // 出站消息日志器:每条 bot 发出的消息都会喂给已注册的日志器(多订阅)。
54 add_outgoing_logger,
55 awake,
56 awake_silent,
57 // 注册表(inventory)+ 插件元数据。`registered_triggers` 报的是已解析到插件的点分 key
58 // (宏先发空 key,解析期按 module_path 关联补上)。
59 collect_into,
60 from_user,
61 group_admin,
62 group_only,
63 group_owner,
64 in_group,
65 keyword,
66 private,
67 registered_plugins,
68 registered_triggers_resolved as registered_triggers,
69 replying,
70 // 分发引擎入口。
71 run_dispatch,
72 superuser,
73 // 总闸 / 休眠的叶子构造器 + 否决时的回复通道。
74 switch,
75 to_me,
76 // 事件触发器(`#[event(Kind)]`):EventKind 选择器 + 细粒度的带类型事件提取器(只有
77 // Nudge 做自身事件过滤 —— Recall 原样投递,让 handler 自己决定防撤回策略)。
78 AdminChange,
79 // 带类型的命令参数解析(Args<T> 类型;#[derive(Args)] 是宏)。
80 ArgError,
81 // 参数元数据(#[derive(Args)] 生成,供 help 自动生成用法)。
82 ArgKind,
83 ArgSpec,
84 ArgText,
85 ArgToken,
86 Args,
87 ArgsMeta,
88 // 在匹配链上取元素的提取器,与 waiter 共用同一套类型:同一个 `Image` 既读内联图片槽,
89 // 也是 `recv::<Image>` 的返回(「内联或追问是同一类型」)。`Option<At>`/`Option<Image>`
90 // 即可选。
91 At,
92 // `Bot` 句柄。它的固有方法就是业务调用的动作面;支撑这些方法、由适配器实现的 trait
93 // 在 [`adapter`]。
94 Bot,
95 BotOffline,
96 BotOnline,
97 Captures,
98 Category,
99 // 冷却门控(默认 UserGlobal、窗口内 max_exec)+ 命令式的 Cd 注入句柄。
100 Cd,
101 CdKey,
102 Command,
103 CommandArg,
104 // 生命周期事件提取器(`#[event(Connect/Disconnect/Ready/BotOnline/BotOffline/Heartbeat)]`)。
105 Connect,
106 Cooldown,
107 CooldownScope,
108 CooldownStore,
109 // 每事件上下文 + 提取器(FromContext)。
110 Ctx,
111 // 开发/诊断标记(配 `App::debug()`):把静默跳过变成 `[dev]` WARN,并对 `Args<T>`
112 // 解析失败自动回一条用法提示。
113 DevMode,
114 Disconnect,
115 // 运行期命令开关。
116 EnabledOverrides,
117 EnabledSet,
118 EventKind,
119 EventPeer,
120 EventSource,
121 Extracted,
122 // 带类型的具名槽 matcher:HEAD 侧,与 TAIL 侧的 Args<T> 对称(#[derive(Slots)] 是宏)。
123 Flank,
124 // 中断引擎(Session/Waiter/Scope)。
125 FlightGuard,
126 // 洋葱中间件(链路)。
127 Flow,
128 FriendAdd,
129 // 带类型的目标选择器(bot.group(g).member(u).mute(..))。
130 FriendRef,
131 FriendRequest,
132 FromArg,
133 FromContext,
134 FromSlots,
135 FromTailText,
136 GateReply,
137 GroupCardChange,
138 GroupJoinRequest,
139 GroupMessage,
140 GroupRef,
141 // Handler 抽象。
142 Handler,
143 HandlerOutcome,
144 HandlerResult,
145 Heartbeat,
146 Honor,
147 Image,
148 KillSwitch,
149 LuckyKing,
150 // Matcher(一个 regex 支撑的触发器;command([..]) 是字面量糖,slots(..) 是带类型的头部)。
151 Matcher,
152 MemberJoin,
153 MemberLeave,
154 MemberRef,
155 Middleware,
156 Mute,
157 NamedCaptures,
158 Next,
159 Nudge,
160 ParseArgs,
161 ParsedCommand,
162 PluginMeta,
163 PrivateMessage,
164 // 内置限流器(经 App::layer 选用)。
165 RateLimit,
166 RateLimitScope,
167 Ready,
168 Recall,
169 Reject,
170 // 跨会话 rendezvous(token/bind:此处签发,彼处认领)。
171 Rendezvous,
172 RendezvousSnapshot,
173 Replied,
174 Reply,
175 ReplyMsg,
176 Router,
177 // 规则/权限组合代数(& | !)+ 内置规则。
178 Rule,
179 Scope,
180 Sender,
181 // 服务生命周期(Supervisor + DAG)。
182 Service,
183 ServiceBus,
184 Session,
185 // 根取消/停机信号。
186 ShutdownToken,
187 SleepState,
188 SlotSpec,
189 SlotValue,
190 Slots,
191 State,
192 Superusers,
193 Supervisor,
194 SwitchKey,
195 Tail,
196 ToMe,
197 TriggerId,
198 TriggerKind,
199 TriggerMeta,
200 TriggerSpec,
201 WaitFlow,
202 Waiter,
203 WaiterDepth,
204 WaiterStore,
205};
206
207/// 适配器作者用的管线:新协议适配器要实现的出站动作 trait —— `ActionInvoker`
208/// (两协议通用的根)、`OneBotActions`(OneBot v11 加上揉进来的 NapCat / LLOneBot /
209/// Lagrange 厂商扩展)、`MilkyActions`,由 `Actions` marker 合在一起。业务代码从不
210/// 点名这些 —— 它调 [`Bot`] 的固有方法,厂商专属动作走 `bot.actions().<m>()` ——
211/// 所以放在这个子模块而非精选的 crate 根。转自 [`nagisa_core::adapter`]。
212pub mod adapter {
213 pub use nagisa_core::adapter::{ActionInvoker, Actions, MilkyActions, OneBotActions};
214}
215
216/// Engine 内部管线:分发引擎据以搭建的对象安全 handler/rule/waiter 机制
217/// (`ErasedHandler`、`Check`、`Selector`、`WaiterBuilder`、`StateMap`)。handler
218/// 作者从不直接点名这些;不进精选的 crate 根。转自 [`nagisa_core::engine`]。
219pub mod engine {
220 pub use nagisa_core::engine::{Check, ErasedHandler, Selector, StateMap, WaiterBuilder};
221}
222
223// `inventory`由 `nagisa_core` 转出;这里再露一层,让 `#[command]` 宏生成的
224// `::nagisa_core::inventory::submit!` 经门面也能解析,插件 crate 无需直接依赖它。
225pub use nagisa_core::inventory;
226
227// `async_trait` 转出:实现自定义 `Service` / `ActionInvoker` 的作者要给 `impl` 标
228// `#[nagisa::async_trait]`,否则会撞上费解的 `E0195`。经门面露出,免去直接依赖
229// `async-trait`。
230pub use nagisa_core::async_trait;
231
232// ── 抽象层(nagisa-types) ──────────────────────────────────────────────────────
233// 跨协议的领域模型:事件、消息、段、实体、id、资源、统一的
234// `Error`/`Result`,以及 `Capability`。
235pub use nagisa_types::capability::{Capability, Protocol};
236pub use nagisa_types::vendor::Vendor;
237// 实体类型 —— `Bot` 方法返回的统一领域结构。handler 若要写自己的 `Result<T>`
238// (如 `nagisa::Result<nagisa::UserInfo>`)需要它们在根可见,所以全部精选到这里。
239pub use nagisa_types::entity::{
240 AiCharacter, AiCharacterGroup, Announcement, Business, EssenceMessage, FileFetch, FileMeta, FriendCategory,
241 FriendCategoryList, FriendGroup, FriendInfo, FriendStatus, GroupFileList, GroupFolder, GroupInfo, HonorList,
242 HonorMember, ImplStatus, MemberInfo, ProfileLiker, Rkey, Role, Sex, UserInfo,
243};
244
245/// `ImplInfo`:`get_impl_info()` 的返回 —— 已连接实现的身份(app 名/版本、QQ 协议
246/// 信息)。定义在 engine crate。厂商判定走 `bot.vendor()`(OneBot 专用轴),不在此类型上。
247pub use nagisa_core::ImplInfo;
248pub use nagisa_types::context::Context;
249pub use nagisa_types::error::{ActionErrorKind, Error, Result, TransportError};
250
251/// `bail!("...")` / `bail!(kind, "...")`:从 handler 里提前返回一个 `nagisa`
252/// [`Error`],对标 `anyhow::bail!`。见 [`Error::action`]。
253pub use nagisa_types::bail;
254pub use nagisa_types::event::{
255 Event, HonorKind, MessageEvent, Meta, Notice, RawEvent, ReactionKind, Request, RequestToken,
256};
257pub use nagisa_types::id::{MessageId, Peer, Scene, Uin};
258pub use nagisa_types::message::{Message, MessageExt, Msg};
259pub use nagisa_types::resource::{Media, ResourceRef, ResourceSource};
260pub use nagisa_types::segment::{Forward, ForwardNode, ImageSubType, Segment};
261
262// 同时把 `nagisa_types` crate 本身露出来,作为罕见的逃生口:业务偶尔需要 prelude
263// 没暴露的名字。
264pub use nagisa_types;
265
266// 把支撑用的 engine crate 以自己的名字露出,好让 `#[command]` / `#[event]` /
267// `plugin!` / `#[derive(Args)]` / `#[derive(ArgEnum)]` 这些宏发出的全限定路径仅经门面
268// 就能解析 —— 守住「bot 作者只依赖 `nagisa`」的承诺。当门面是被依赖的 crate 时,
269// `nagisa-macros` 用 `proc-macro-crate` 把它发出的 `nagisa_core::…` 根改写成
270// `::nagisa::nagisa_core::…`;正是这条重新导出让那些路径点到 engine(`plugin`/`registry`/
271// `args` 子模块本就是 `pub`,深层尾巴也能解析)。与上面 `pub use nagisa_types;` 逃生口对称。
272#[doc(hidden)]
273pub use nagisa_core;
274
275// ── SIGINT 便捷封装 ─────────────────────────────────────────────────────────────
276
277/// 起一个后台任务,收到 SIGINT(Ctrl-C)时取消返回的 [`ShutdownToken`]。
278///
279/// 这是让 bot 可停机最省事的办法:不必自己 new 一个 [`ShutdownToken`] 再接信号
280/// 处理,直接 `ctrl_c_shutdown()` 把结果传给 `run_onebot`/`run_milky` 即可。
281///
282/// ```ignore
283/// #[tokio::main]
284/// async fn main() -> nagisa::Result<()> {
285/// let shutdown = nagisa::ctrl_c_shutdown();
286/// App::new().run_onebot(cfg, shutdown).await
287/// }
288/// ```
289///
290/// 任务会忽略 `tokio::signal::ctrl_c` 的错误(比如平台不支持)—— 那种平台上 token
291/// 只是不会从这个来源触发,你仍可手动取消它。
292pub fn ctrl_c_shutdown() -> ShutdownToken {
293 let token = ShutdownToken::new();
294 let child = token.clone();
295 tokio::spawn(async move {
296 if tokio::signal::ctrl_c().await.is_err() {
297 tracing::warn!("ctrl_c signal listener failed; shutdown will not fire on Ctrl-C");
298 }
299 child.cancel();
300 });
301 token
302}
303
304// ── 宏(nagisa-macros) ─────────────────────────────────────────────────────────
305/// `#[command(...)]` 属性宏:声明式登记一个 matcher 门控的 handler,在 [`App::new`]
306/// 时经 `inventory` 自动收集。
307pub use nagisa_macros::command;
308
309/// `plugin!{ .. }` 宏:把当前模块声明为一个插件。同模块(及更下层)的触发器按
310/// `module_path!()` 关联到它,其分层开关继承插件的 `default_enable`/`can_disable`。
311/// 见插件模型。
312pub use nagisa_macros::plugin;
313
314/// `#[event(Kind, id="..")]` 属性宏:声明式登记一个事件触发的 handler(与
315/// `#[command]` 对等),在给定 [`EventKind`] 的事件上触发,受同一套分层插件开关门控。
316pub use nagisa_macros::event;
317
318/// `#[derive(Args)]`:为结构体派生 [`ParseArgs`],使其可经 [`struct@Args`]`<T>` 从命令
319/// 参数里提取。派生宏与 `Args<T>` 提取器刻意共用 `Args` 这个名字(跨宏/类型命名空间),
320/// 同 serde 的 `Serialize`。
321pub use nagisa_macros::Args;
322
323/// `#[derive(ArgEnum)]`:为无字段枚举派生 [`FromArg`](变体名转小写;支持
324/// `#[arg(rename="..")]`/`#[arg(alias="..")]`),使其可作受限的 `Args<T>` 字段类型
325/// (如 `on|off`)。
326pub use nagisa_macros::ArgEnum;
327
328/// `#[derive(Slots)]`:命令头 = **有序区块序列**——结构体级 `#[slots(lit("查看"), board, lit("榜"),
329/// scope)]`(`lit("..")` 固定块、裸标识符引用同名字段、`sep=`/`usage=` 键),字段各自 `#[slot(re=..)]`
330/// /`#[slot(union=[..])]`/`#[slot(tail)]` 定来源/类型(`Option<T>` 表可选块)。派生 [`FromSlots`],可经
331/// [`struct@Slots`]`<T>` 提取。HEAD 侧,与持有 TAIL 的 `#[derive(Args)]` 对称;还自动生成
332/// `COMMAND_WORDS`(字面 × 各必填 union 块的笛卡尔积)供 help 像字面命令一样枚举。
333pub use nagisa_macros::Slots;
334
335/// `matcher! { lit(".."), <field>: <ty> = re("..")|union("a","b")|tail, sep="..", usage=".." }`:对同一套
336/// `#[derive(Slots)]` codegen 的函数式糖(同样的区块序列),用于写一个内联头部 matcher;求值为
337/// [`Matcher`]。派生宏才是规范形式(它还顺带产出 `Slots<T>` 提取器类型)。
338pub use nagisa_macros::matcher;
339
340// ── 协议适配器(feature 门控) ─────────────────────────────────────────────────
341#[cfg(feature = "onebot")]
342pub use nagisa_onebot;
343#[cfg(feature = "onebot")]
344pub use nagisa_onebot::{OneBotAdapter, OneBotConfig};
345
346#[cfg(feature = "milky")]
347pub use nagisa_milky;
348#[cfg(feature = "milky")]
349pub use nagisa_milky::{MilkyAdapter, MilkyConfig, MilkyMode};
350
351// ── 日志门面(feature 门控) ────────────────────────────────────────────────────
352/// 可选的日志工具箱(`use nagisa::log::*;`),挂在 `log` feature 后面,好让 bot 作者只
353/// 依赖 `nagisa`。转出 [`nagisa_log`]:
354///
355/// - [`render`](nagisa_log::render)`(&Event) -> String` —— 纯函数的可读事件渲染器;
356/// - [`EventLog`](nagisa_log::EventLog) —— 给 `App::on_top` 用的可选 top 观察者(运行期
357/// 开关 + 按 kind 过滤);
358/// - [`init`](nagisa_log::init) —— 统一记录器(控制台 + 可选滚动文件 + 按 source/`target`
359/// 过滤),同时返回「日志即事件」的 [`LogBus`](nagisa_log::LogBus);
360/// - [`on_record`](nagisa_log::on_record) —— 把 bus 抽到一个异步 sink 里,让业务持久化日志,
361/// 与消息 Event/handler 路径分开(防自激)。
362#[cfg(feature = "log")]
363pub use nagisa_log as log;
364
365/// 可选排版引擎(`use nagisa::render::*;`),挂在 `render` feature 后面,好让 bot 作者只
366/// 依赖 `nagisa`。转出 [`nagisa_render`]:把标记文本 / 构建器文档排版成图片字节(PNG / WebP),
367/// 再经既有的 `Segment::image_bytes` 出图。
368#[cfg(feature = "render")]
369pub use nagisa_render as render;
370
371/// bot 作者唯一需要的一行 glob 导入:`use nagisa::prelude::*;`。
372///
373/// 带进:
374/// - 宏 —— `#[command]` / `#[event]` / `plugin!{}` / `#[derive(Args)]` /
375/// `#[derive(ArgEnum)]` / `#[derive(Slots)]` / `matcher!{}` / `bail!`;
376/// - [`App`] 构建器、[`Bot`] 句柄、[`Router`],以及
377/// [`ctrl_c_shutdown`] / [`ShutdownToken`];
378/// - handler 签名按类型点名的常用 `FromContext` 提取器
379/// (`Reply` / `GroupMessage` / `Command` / `ArgText` / `State<T>` / `Session` /
380/// `Sender` / 各 `#[event]` 提取器 …);
381/// - 门控/规则组合子(`to_me` / `group_admin` / `superuser` / … 以及
382/// `switch` / `awake` / `Cooldown`)、中断引擎句柄
383/// (`Session` / `Waiter` / `Scope` / `Rendezvous`)、带类型目标选择器
384/// (`GroupRef` / `MemberRef` / `FriendRef`);
385/// - 统一领域模型(`Segment` / `Message` / `Peer` / `Uin` /
386/// `MessageId` / `Event` / `Result` / `Error` …)。
387///
388/// 适配器配置(`OneBotConfig` / `MilkyConfig` / `MilkyMode`)在对应 feature 开启时
389/// 一并带入。
390pub mod prelude {
391 // 宏:#[command]/#[event] 属性、plugin!{} 声明、
392 // #[derive(Args)]/#[derive(ArgEnum)]/#[derive(Slots)] 派生、matcher!{} 糖,
393 // 以及让 handler 提前失败的 `bail!`。
394 pub use crate::{bail, command, event, matcher, plugin, ArgEnum, Args, Slots};
395
396 // 自定义参数解析:`ParseArgs` trait 及其 token/error 类型,加上字段类型 trait
397 // `FromArg` —— 给手写 `Args<T>` impl(而非 `#[derive(Args)]`)的 handler 用。
398 // `ArgSpec`/`ArgKind`/`ArgsMeta`:参数元数据,help 据此自动生成用法。
399 pub use crate::{ArgError, ArgKind, ArgSpec, ArgToken, ArgsMeta, FromArg, ParseArgs};
400
401 // 带类型的具名槽 matcher(HEAD 侧,与 Args<T> 对称):`Tail` 通配载荷,以及手写头部用的
402 // `FromSlots`/`SlotValue` trait。(`Slots<T>` 本身随上面 `#[derive(Slots)]` 宏一起带入 ——
403 // 类型与派生共用名字,同 `Args`。)
404 pub use crate::{FromSlots, FromTailText, SlotValue, Tail};
405
406 // App + engine 句柄。
407 pub use crate::{ctrl_c_shutdown, App, Bot, Router, ShutdownToken};
408
409 // `#[async_trait]` —— `FromContext`/自定义 async trait 绕不开的搭档
410 // (`#[async_trait] impl FromContext for MyExtractor { async fn from_context(ctx: &Ctx) .. }`)。
411 // 经 glob 露出,自定义提取器作者无需再写一行 import,也无需冗长的 `#[nagisa::async_trait]`。
412 // (crate 根也仍有这条转出。)
413 pub use crate::async_trait;
414
415 // 服务生命周期(业务偶尔会用到)。
416 pub use crate::{Service, ServiceBus, Supervisor};
417
418 // handler 签名按类型点名的常用提取器,加上自定义 `FromContext` impl 作者会用到的
419 // 提取结果类型(`Extracted`/`Reject`)。
420 pub use crate::{
421 ArgText, At, Captures, Command, CommandArg, Ctx, EventPeer, Extracted, FromContext, GroupMessage,
422 HandlerResult, Image, PrivateMessage, Reject, Reply, ReplyMsg, Sender, State, ToMe,
423 };
424
425 // 规则/权限组合子 + 内置规则(用 `&` / `|` / `!` 组合)。`Superusers` 是 `superuser()`
426 // 背后注入的集合 —— 需要手动判断(比如按是否 superuser 分支而非直接门控)的 handler 经
427 // `State<Superusers>` 点名它。
428 pub use crate::{
429 awake, awake_silent, from_user, group_admin, group_only, group_owner, in_group, keyword, private, replying,
430 superuser, switch, to_me, GateReply, KillSwitch, Rule, SleepState, Superusers,
431 };
432
433 // 冷却门控(经 `Cooldown::into_rule` 变成 `Rule`)+ 命令式的 `Cd` 注入句柄。
434 pub use crate::{Cd, CdKey, Cooldown, CooldownScope, CooldownStore, TriggerId};
435
436 // 洋葱中间件。
437 pub use crate::{Flow, Middleware, Next, RateLimit, RateLimitScope};
438
439 // 中断引擎(Session/Waiter/Scope)。
440 pub use crate::{Rendezvous, Replied, Scope, Session, WaitFlow, Waiter};
441
442 // 带类型的目标选择器。
443 pub use crate::{FriendRef, GroupRef, MemberRef};
444
445 // 插件元数据(给 help/菜单插件用)+ 运行期开关。
446 pub use crate::{
447 collect_into, registered_plugins, registered_triggers, Category, EnabledSet, EventKind, PluginMeta, SwitchKey,
448 TriggerKind, TriggerMeta, TriggerSpec,
449 };
450
451 // 事件触发提取器(`#[event(Kind)]` handler 签名按类型点名),含生命周期:
452 // Connect/Disconnect(传输)、Ready(框架)、BotOnline/BotOffline(账号)、Heartbeat。
453 pub use crate::{
454 AdminChange, BotOffline, BotOnline, Connect, Disconnect, FriendAdd, FriendRequest, GroupCardChange,
455 GroupJoinRequest, Heartbeat, Honor, LuckyKing, MemberJoin, MemberLeave, Mute, Nudge, Ready, Recall,
456 };
457
458 // 统一领域模型。
459 pub use crate::{
460 ActionErrorKind, Capability, Context, Error, Event, Forward, ForwardNode, FriendInfo, GroupInfo, Media,
461 MemberInfo, Message, MessageEvent, MessageExt, MessageId, Msg, Notice, Peer, Protocol, ReactionKind, Request,
462 ResourceSource, Result, Scene, Segment, Uin,
463 };
464
465 // 适配器配置(feature 门控)。
466 #[cfg(feature = "onebot")]
467 pub use crate::OneBotConfig;
468 #[cfg(feature = "milky")]
469 pub use crate::{MilkyConfig, MilkyMode};
470
471 // 协议/厂商专属动作 trait:让 `bot.actions().<method>()` 直达口的方法在作用域内可解析
472 // (`Actions` 是组合 marker,`OneBotActions`/`MilkyActions` 提供实际方法)。
473 pub use crate::adapter::{Actions, MilkyActions, OneBotActions};
474}