命令树
读完这一页,你能用节点自己搭出命令分支,让每条输入都在正确的位置结束。
命令树直接用节点组织分支,和 SimpleCommandBuilder 使用同一套解析器。
从 Literal 开始
命令根使用 Literal。then(...) 添加子节点,runs(...) 指定在当前节点结束时执行的回调:
from botqraft.api.command import CommandContext, GreedyText, Literal, QQCommandSource
from botqraft.api.types import QQPluginServerInterface
def echo(source: QQCommandSource, context: CommandContext) -> None:
source.reply(context['text'])
def on_load(server: QQPluginServerInterface, prev_module) -> None:
root = Literal('/echo')
root.runs(lambda source: source.reply('Usage: /echo <text>'))
root.then(GreedyText('text').runs(echo))
server.register_command(root)这棵树有两个结束位置:
/echo在根节点结束,回复使用说明。/echo Hello world在text参数节点结束,回复Hello world。
Literal 匹配固定词。参数节点把解析结果放进 CommandContext,你用节点名取值。
用 then 组织分支
固定子命令也用 Literal。例如,在注册前给上例加一个帮助分支:
root.then(
Literal('help').runs(lambda source: source.reply('Usage: /echo <text>'))
)这时 /echo help 匹配到固定子命令,执行帮助回调。
then(...) 返回当前的父节点。所以连续的 root.then(a).then(b) 添加的是两个并列子节点。要嵌套下一层,就在子节点上继续调用 then(...):
root.then(
Literal('repeat').then(GreedyText('text').runs(echo))
)这条分支对应 /echo repeat Hello world。
把 runs 放在输入可以结束的位置
runs(...) 设置当前节点的回调。请把它放在用户输入可以结束的位置:
- 放在根上,处理只有命令根的输入。
- 放在固定子命令上,处理不带参数的子命令。
- 放在最后一个参数节点上,处理完整参数。
回调支持零到两个参数,依次是 source、context。要读取参数就用两个参数。只回复固定内容时,可以只接收 source。
用 requires 检查条件
requires(...) 在进入节点时检查条件。返回 True 就继续解析,返回 False 会产生 RequirementNotMet 命令错误。
下面只允许权限达到 2 的用户执行 /admin ping:
from botqraft.api.command import Literal
root = Literal('/admin').requires(lambda source: source.has_permission(2))
root.then(Literal('ping').runs(lambda source: source.reply('pong')))
server.register_command(root, scope=('group',))把这段注册代码放进 on_load。根上的条件覆盖它下面的所有子分支。只需要保护单个操作时,把条件放在对应的子节点上。
多次调用 requires(...) 会把条件组合起来,全部满足才继续。条件可以读取来源,也可以读取已经解析出的上下文。权限查询在真正执行的那一刻解析当前有效值,具体规则见权限。
登记帮助时,permission 和 scope 要分别填写。帮助可见性和节点执行检查各自生效。
接下来,为命令添加帮助消息与指令面板。