Nagisa 框架运行时核心:统一 [Bot] 句柄、[EventSource]/ActionInvoker
两个传输 trait,以及把二者串起来的 dispatch 引擎。
adapter(nagisa-onebot / nagisa-milky)实现这两个 trait,把协议 wire 翻译进出
nagisa-types 的统一类型;业务 / 上层门面(nagisa crate 的 App)只面向本 crate 的抽象。
模块地图
传输与运行:
- [
source]:入站事件源 trait(EventSource)。 - [
bot] / [invoker] / [refs] / [impl_info]:出站动作。[Bot] 是廉价克隆的句柄, 内含Arc<dyn Actions>;[invoker] 把动作分三层(ActionInvoker两协议通用、OneBotActionsOneBot 独有 + 厂商扩展、MilkyActionsMilky 独有),[refs] 是按目标分组的句柄糖。 - [
dispatch]:消费事件流、逐个构造 [Ctx]、并发分发给 [Router]。 - [
reconnect]:带上限指数退避的重连循环 helper(adapter 用)。 - [
sse]:纯字节级 SSE 解析器(adapter 用)。 - [
wire]:两适配器共用的 wire 基建——log_wire协议帧日志、零依赖 base64、HTTP 动作通道 骨架(adapter 用)。 - [
framesource]:连接后的公共泵骨架——FrameSource/Frame/pump(adapter 用)。 - [
service]:服务生命周期Supervisor+ 依赖 DAG +ServiceBus,门面据此搭线。
分发与匹配(路1:声明式 handler):
- [
ctx]:每事件上下文Ctx(提取器从它取料)。 - [
router]:handler 注册表 + app 共享状态 + 洋葱中间件 + 三层分发。 - [
handler] / [extract] / [args] / [slots]:handler 抽象 +FromContext提取器 + 声明式参数(TAIL 段流)/ 命名 slot(HEAD 捕获组)解析。 - [
matcher]:命令触发匹配器(字面量 / 正则 / 有序 slot)+ MentionMe 预处理。 - [
rule]:可组合门控Rule(&/|/!代数)+ 权限 / 场景 / kill-switch / 休眠叶子。 - [
middleware] / [ratelimit]:洋葱中间件 + 内置限流。 - [
cooldown]:冷却门控(声明式Rule+ 命令式Cd句柄)。
插件与开关:
- [
plugin] / [registry]:inventory编译期收集的插件 / 触发器静态模型。 - [
enabled]:分层启用 / 禁用状态(插件总开关 + 触发器子开关,按会话覆盖)。 - [
event_trigger]:EventKind判别式 + 细粒度事件提取器(#[event(Kind)]用)。
交互(路2:命令式 waiter / session):
- [
session]:Session/Waiter中断引擎 +single_flight。 - [
rendezvous]:跨会话「留 token、稍后取」的 TTL 存储。
派发模型:事件 → router → gate → handler
[dispatch::run_dispatch] 从 [EventSource] 收事件,每个事件在独立 tokio 任务里
构造一个 [Ctx](共享 router 状态 + bot),交给 [Router::dispatch]。router 先穿过
洋葱中间件链,末端跑三层 handler(见 [Router]):
- top observer(
on_top):永远先跑、不被 waiter 拦。 - waiter 检查:若有挂起 waiter 命中本事件且其
block为真,吞掉事件、不再下传。 - default handler:按优先级(小者先)逐个跑。
每个命令 / 事件型 handler 内部依次过:事件种类预筛(#[event])→ 启用门
([EnabledSet])→ 匹配器([Matcher],命中产出 ParsedCommand)→ 门控
[Rule](权限 / 场景 / 冷却)→ handler 调用。任一步不过即跳过本 handler,同层
handler 互不阻断。门控否决时可经 reply-on-veto 通道回贴一句(如休眠的「Zzz」)。
交互模型:Waiter / Session
处理一个事件时,handler 可从 [Ctx] 提取一个 [Session],再 .waiter() 挂起一个
[Waiter]:它把后续命中事件经 mpsc 投递回来(多轮、可嵌套,深者优先),handler
借此实现「追问 → 等回答」的多步对话,无需为每步注册全局 handler。需要跨两个独立
事件(可能相距很远、在不同会话)的流程则用 [Rendezvous]。