Skip to main content

command

Attribute Macro command 

Source
#[command]
Expand description

#[command(...)] 属性宏:声明式登记一个 matcher 门控的 handler,在 App::new 时经 inventory 自动收集。 把一个 async fn 注册成消息命令触发器

保留原函数,并发出 <FN>_KEY 开关键、<FN>__nagisa_register 注册函数与一条 TriggerSpec 登记(详见 crate 级文档「触发器宏的共同展开形状」)。

§属性键

匹配器(三选一,互斥,必给其一):

  • 前导位置参数 "签到", "sign", ..:字面量命令词(写在属性最前),编译成正则;至少一个。
  • regex = "^..$":原始正则字面量(展开期 Matcher::regex(..).expect(..), 非法正则在该消费 crate 编译/启动时即暴露)。
  • slots = <Type>:取一个 #[derive(Slots)] 类型的 <Type as FromSlots>::matcher() 作头匹配器。

行为:

  • mention_me(裸旗标):要求消息 @ 本机器人才触发。
  • top(裸旗标):一级 top 观察者,永不被 waiter 拦截。
  • priority = N:整数优先级(支持负数),越大越先。
  • usage = "...":parse-miss 时回贴给用户的用法串(命令专属;#[event] 拒绝此键)。

门控(原样搬运的表达式,宏对其内容零知识):

  • gate = <Rule expr>:任意 Rule 表达式,原样拼进门控槽。
  • cooldown = <expr>:经 Cooldown::from(<expr>).into_rule(..) 物化,AND 进门控链最右 (权限/开关先判,冷却最后才盖戳)。

元数据(→ TriggerMeta,缺省由展开期回填):

  • id = "..."(缺省取函数名)、name = "..."(缺省取函数名)、description = "..."
  • can_disable = <bool>(缺省 true)、 default_enable = <bool>(缺省 true)、hidden = <bool>(缺省 false)。

命令(参数)始终声明在 handler 形参上(args: Args<MyArgs>),而非属性里: 头写在属性、体写在形参,二者就近落在同一 handler。

§触发语义(命令互斥)

dispatch 对每条消息逐个试所有命令的匹配器、命中即跑,首中即停、做「最具体优先」—— 命令型 handler 之间互不阻断(只有 top 观察者与中断 waiterblock 能拦)。故两条匹配范围 重叠的命令会都触发。约定:命令作者自行保证匹配不重叠

字面量命令词天然不必担心「前缀吃后缀」:它们编成 ^(?:词…)(?:\s|$),末尾的 (?:\s|$) 边界让 签到 不会命中 签到日历(CJK 无词边界,靠这个显式边界)。重叠风险主要来自 regex / 多命令 词人为撞车。

§约束

handler 必须是非泛型、无 self 接收者的 async fn(否则编译报错)。

§示例

// 字面量命令词 + 结构化参数:
#[command("echo", "复读", description = "复读一句话")]
async fn echo(ctx: &Context, args: Args<EchoArgs>) -> Result<()> { /* .. */ }

// 正则头 + @机器人 + 优先级 + 声明式门控:
#[command(regex = "^ping$", mention_me, priority = 10, gate = is_admin())]
async fn ping(ctx: &Context) -> Result<()> { /* .. */ }

// 用 #[derive(Slots)] 类型作头匹配器 + 冷却:
#[command(slots = ViewBoard, cooldown = 30)]
async fn view(ctx: &Context, slots: Slots<ViewBoard>) -> Result<()> { /* .. */ }