Commander
WARNING
虽然现在还有一大堆没写,但是还要扯一句,现阶段 Commander 并不怎么适合给 Ariadne 使用,即强制缝合不可取 desu
Commander
是什么
在看完了前面的章节后,想必你立马把 Commander 拖到翻译软件里面,看看结果是什么, 但当你扔到翻译软件一看:
翻译 | 反向翻译 |
---|---|
指挥官 | commander |
司令 | commander, commanding officer |
指挥员 | commander |
指挥 | conductor, commander, director, dictate |
舰长 | captain, commander, commodore, warship's captain |
事实上,这个词由 command
(命令)和 -er
(的东西[1])组成,说白了他就是一个命令解析器
。
与前文介绍的 基础消息链处理器
及 Twilight
与下文介绍的 Alconna
相比,Commander 特点是基于 pydantic 进行命令处理。
TIP
说实话,有一点点像 Twilight.from_command
。
使用 Commander
首先,我们来创建一个 Commander 实例:
from creart import create
from graia.ariadne.message.commander import Commander
cmd = create(Commander)
2
3
4
然后使用装饰器注册命令:
在 Saya 模块中使用
在 main.py 中:
from graia.ariadne.message.commander.saya import CommanderBehaviour, CommandSchema
saya.install_behaviours(CommanderBehaviour(cmd))
2
3
在模块中:
@channel.use(CommandSchema(".涩图 {tag}"))
async def setu(tag: str):
...
2
3
在 Broadcast 模块中使用
@cmd.command(".涩图 {tag}")
async def setu(tag: str):
...
2
3
命令样式
这里先举一个简单的例子,这是一个 Commander:
[.涩图 | .涩涩] pixiv {tag}
他可以接受下面形式的命令:
.涩图 pixiv A60
.涩涩 pixiv 野兽先辈
.涩涩 pixiv "野兽 先辈"
TIP
对于含有空格的字符串,需要像 shell 那样使用引号包裹,否则不满足匹配规则,例如下方的文本将不会被接受
.涩涩 pixiv 野兽 先辈
通过上面的例子可以看出来,以下3种规则是有效的:
[.涩图 | .涩涩]
这种使用 中括号,其中使用|
分隔的代表 任选其一匹配pixiv
这种纯文本代表 完全匹配{tag}
这种使用 大括号 括住的代表 参数定义,用于在函数中获取这里的值
使用大括号还可以使用像 PEP 484 的类型标注和默认值,我们将在下文讲解。
参数分派,标注与默认值
上面的例子中,函数形参 tag
的值会自动填充为命令中 {tag}
位置的值,这叫做函数的参数分派。
在参数定义中,还可以进行类型标注和指定默认值,例如下面的例子:
@cmd.command(".涩图 {pid: int = 114514}")
async def setu(pid: int): ...
2
这样就会将 pid
自动转换为 int
,无法转换的将不会执行此函数,不存在时则使用默认值 114514
。
WARNING
指定默认值,或者通过 Slot
指定 default
/ default_factory
即认为此项是可选项。
可选项后 不可跟随 非可选的文本与参数,但是 wildcard
除外。
例如下面的命令是非法的:
.涩图 {pid: int = 114514} {foo} bar
WARNING
需要使用 \\
对 []{}
进行转义,或使用 r''
,只需要 \
@cmd.command(r".command {content: List\[str\]}")
# 或
@cmd.command(".command {content: List\\[str\\]}")
2
3
wildcard
wildcard 模式类似于 def func(*args)
中的 *args
。
此模式下将接收任意数量的参数,并通过类型标注逐个处理,最后会将以 Tuple 形式进行分派。
例如 {...targets: At}
分派类型为 Tuple[At, ...]
,而 {...pid: int}
分派类型为 Tuple[int, ...]
。
除此之外,类型标注还可以使用 raw
,这种格式的分派类型为 MessageChain
,即获取原来的单个消息链,例如下面的例子:
from typing import Tuple
# 在命令中进行类型标注需要在全局作用域中被定义
from graia.ariadne.message.element import At
@cmd.command(".record start {title: str} {...targets: At}")
def parser(title: str, targets: Tuple[At, ...]):
...
@cmd.command(".record append {title: str} {...chain: raw}")
def parser(title: str, chain: MessageChain):
..
2
3
4
5
6
7
8
9
10
11
12
13
Slot
和 Arg
setting
setting
是装饰器的参数,为一个 str
至 Slot | Arg
映射, str
为实际参数名,例如:
@cmd.command(
".cmd {placeholder}",
setting={"param": Slot("placeholder", str, "")},
)
async def func(param: str):
...
2
3
4
5
6
Slot
Slot
用作为于参数重定向,他需要指定一个占位符、类型,以及可选的默认值(default
)或默认值工厂函数(default_factory
),例如:
from graia.ariadne.message.commander import Slot
# 注意函数参数的名称
@cmd.command(
".涩图 {pid}",
{"dio": Slot("pid", type=int, default=114514)},
)
async def setu(dio: int):
...
2
3
4
5
6
7
8
9
10
Arg
Arg
没有拓展语法,且函数标注推断对其无效,因此 Arg
必须指定 default
/ default_factory
作为没有此参数时的默认值。
而 Arg
的 type
在单参数/无参数时可以省略, 但在多参数时必须指定,且必须为 pytantic.BaseModel
的子类。
该子类需要接受所有多参数的占位符的字段,且必须拥有 chain_validator
这个 validator
,例如:
import pydantic
from graia.ariadne.message.commander import Arg, chain_validator
class ExampleModel(pydantic.BaseModel):
_ = pydantic.validator("*", pre=True, allow_reuse=True)(chain_validator)
name: str = ""
tag: str = ""
Arg(
"[.info|--info|+I] {name} {tag}",
type=ExampleModel,
default=ExampleModel(),
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
无参数时, Arg
的 type
默认为 bool
类型, 而 default
默认为 False
,即:
Arg("--option") # 出现时结果为 True
Arg("--option", default=True) # 出现时结果为 False
2
单参数时 Arg
的 type
可以是 pydantic.BaseModel
的子类或者任意类型, 默认为 MessageChain
。
Arg
用法示例
# 出现 --help 时,help 为 True
@cmd.command(
".record {...el: raw}",
{"help": Arg("--help", default=False)},
)
def parse_help(help: bool):
...
2
3
4
5
6
7
添加转换类型
对于一些特殊的类型 (以及一些特殊的用户) ,你需要使用 add_type_cast
自定义转换函数。
例如下面将以 .
分隔的字符串或 MessageChain
转换为对应的列表类型:
from pydantic.fields import ModelField
def cast_to_list(value: MessageChain, field: ModelField):
if field.outer_type_ is List[str]:
return value.display.split(".")
if field.outer_type_ is List[MessageChain]:
return value.split(".")
return value
cmd.add_type_cast(cast_to_list)
2
3
4
5
6
7
8
9
10
11
12
这样就添加了 List[str]
与 List[MessageChain]
两种类型支持。