Skip to content

指令(Command)

引言

  • 指令是当一条消息满足一定条件时,约定机器人执行指定一个函数函数
  • 在大多数机器人中,都是这样实现这个功能的
js
// ...
bot.on("message", event => {
  if (event.raw_message === "foo") {
    // 执行foo函数
    foo();
  } else if (event.raw_message.startsWith("bar")) {
    event.raw_message = event.raw_message.replace("bar", "");
    // 根据参数执行bar函数
    bar(...event.raw_message.split(" "));
    // do sth
  } else if (condition) {
    // ...
  } // ...
});
  • 这无疑是及其混乱的,而且还不利于维护。
  • 为此,Zhin 参考市面上的指令实现后,实现了自己的指令系统
  • 上边的代码在 Zhin 中,可以这么优雅的实现
ts
// ...
export function install(ctx: Context) {
  ctx.command("foo").action(foo);
  ctx.command("bar <...args>").action((argv, ...args) => bar(...args));
}

如此定义后: Zhin 会在用户发送的消息为 foo 时,自动执行 foo 函数。当用户发送的消息为 bar 开头时,Zhin 自动执行bar函数,并将后续的参数按空格分隔,传递给 bar 函数

并且还有更多的使用方式,让我们接着往下看...

参数定义

如你所见,使用 ctx.command(desc) 方法可以定义一个指令,其中 desc 是一个字符串,包含了指令名参数列表

  • 指令名可以包含数字、字母、下划线、短横线甚至中文字符,但不应该包含空格、小数点 . 或斜杠 /
  • 一个指令可以含有任意个参数。其中 必选参数尖括号包裹,可选参数方括号包裹
  • 有时我们需要传入未知数量的参数,这时我们可以使用 变长参数,它可以通过在括号中前置**...**来实现。如:
ts
ctx.command("echo <arg1> [...rest]").action((_, arg1, ...rest) => {
  /* do something */
});

上面一行代码声明了一个echo指令,并且该指令接收1到多个参数

参数类型

知音默认参数类型为消息段,若你需要指定类型,仅需在参数名后跟上 :type 即可,Zhin 内置的数据类型有:

  • string: string 字符串
  • integer: number 整数
  • number: number 数值
  • boolean: boolean 布尔值
  • user_id: number | string 用户id
  • regexp: RegExp 正则表达式
  • date: Date 日期
  • json: Dict | List JSON对象
  • function: Function 函数用例:
ts
ctx.command("send <arg1:face> [...rest:number]"); // 声明第一个参数为一个表情,剩下的参数均为数值

上面一行代码声明了一个send指令,并且该指令接收一个表情多个数值,作为参数

可选项定义

使用 cmd.option(name, desc) 函数可以给指令定义参数。这个函数也是可以链式调用的,例如:

ts
ctx
  .command("music <keyword:string>")
  .option("-o [origin:boolean]") // 是否原声输出
  .option("-p [platform:string]") // 选用音乐平台
  .option("-s [singer:number]") // 指定歌手id
  .action(({ options }, keyword) => JSON.stringify(options));
聊天记录
master
music 烟雨行舟 -o -p qq -s 82329
知音
{"options":true,"platform":"qq","singer":82329}

同样,可选项的参数也可以声明类型,声明方式同上

快捷方式

Zhin 的指令机制虽然能够尽可能避免冲突和误触发,但是也带来了一些麻烦。一方面,一些常用指令的调用会受到指令前缀的限制;另一方面,一些指令可能有较长的选项和参数,但它们调用时却往往是相同的。面对这些情况,快捷方式 (Shortcut) 能有效地解决你的问题接下来我们将刚刚上边的 music 指令稍微进行一下改造

ts
ctx
  .command("music <keyword:string>")
  .option("-o [origin:boolean]") // 是否原声输出
  .option("-p [platform:string]") // 选用音乐平台
  .option("-s [singer:number]") // 指定歌手id
  .sugar("qq点歌", { options: { platform: "qq", origin: true } }) 
  .action(({ options }, keyword) => JSON.stringify(options));
  • 这儿的fuzzy标识指令可以带参数
聊天记录
master
qq点歌 烟雨行舟
知音
{"options":true,"platform":"qq"}

除此以外,你还可以使用正则表达式作为快捷方式:

ts
ctx
  .command("music <keyword:string>")
  .option("-o [origin:boolean]") // 是否原声输出
  .option("-p [platform:string]") // 选用音乐平台
  .option("-s [singer:number]") // 指定歌手id
  .sugar("qq点歌", { options: { platform: "qq", origin: true } })
  .sugar(/^来一首(.+)$/, {
    args: ["$1"],
    options: { platform: "qq", origin: true },
  }) 
  .action(({ options }, keyword) => [keyword, JSON.stringify(options)]);

这样一来,输入来一首烟雨行舟就等价于输入music 烟雨行舟 -p qq -o了。

不难看出,使用快捷方式会让你的输入方式更加接近自然语言,也会让你的机器人显得更平易近人。