From dc4ac6d8d7122f4a605931c5150d278223f61a43 Mon Sep 17 00:00:00 2001 From: Tarrailt <3165388245@qq.com> Date: Sat, 26 Aug 2023 21:49:28 +0800 Subject: [PATCH] =?UTF-8?q?:memo:=20Docs:=20=E6=9B=B4=E6=96=B0=E6=9C=80?= =?UTF-8?q?=E4=BD=B3=E5=AE=9E=E8=B7=B5=E9=83=A8=E5=88=86=E7=9A=84=20Alconn?= =?UTF-8?q?a=20=E7=AB=A0=E8=8A=82=20(#2303)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- website/docs/best-practice/alconna/alconna.md | 134 +++++++++++-- website/docs/best-practice/alconna/config.md | 7 + website/docs/best-practice/alconna/matcher.md | 145 ++++++++++---- website/docs/best-practice/alconna/utils.md | 180 ++++++++++++++++++ website/static/plugins.json | 50 ++--- 5 files changed, 440 insertions(+), 76 deletions(-) create mode 100644 website/docs/best-practice/alconna/utils.md diff --git a/website/docs/best-practice/alconna/alconna.md b/website/docs/best-practice/alconna/alconna.md index 5f3d8805..4d7bf64a 100644 --- a/website/docs/best-practice/alconna/alconna.md +++ b/website/docs/best-practice/alconna/alconna.md @@ -64,6 +64,15 @@ alc = Alconna(".rd{roll:int}") assert alc.parse(".rd123").header["roll"] == 123 ``` +Bracket Header 类似 python 里的 f-string 写法,通过 "{}" 声明匹配类型 + +"{}" 中的内容为 "name:type or pat": + +- "{}", "{:}": 占位符,等价于 "(.+)" +- "{foo}": 等价于 "(?P<foo>.+)" +- "{:\d+}": 等价于 "(\d+)" +- "{foo:int}": 等价于 "(?P<foo>\d+)",其中 "int" 部分若能转为 `BasePattern` 则读取里面的表达式 + ### 组件 我们可以看到主要的两大组件:`Option` 与 `Subcommand`。 @@ -74,6 +83,27 @@ assert alc.parse(".rd123").header["roll"] == 123 `Subcommand` 则可以传入自己的 **Option** 与 **Subcommand**。 +```python +from arclet.alconna import Alconna, Option, Subcommand + +alc = Alconna( + "command_name", + Option("opt1"), + Option("--opt2"), + Subcommand( + "sub1", + Option("sub1_opt1"), + Option("-SO2"), + Subcommand( + "sub1_sub1" + ) + ), + Subcommand( + "sub2" + ) +) +``` + 他们拥有如下共同参数: - `help_text`: 传入该组件的帮助信息 @@ -237,8 +267,8 @@ assert alc.parse("test123 BARabc").matched ```python >>> from arclet.alconna import Alconna, Option, Args, append ->>> alc = Alconna("gcc", Option("--flag|-F", Args["content", str], action=append)) ->>> alc.parse("gcc -Fabc -Fdef -Fxyz").query("flag.content") +>>> alc = Alconna("gcc", Option("--flag|-F", Args["content", str], action=append, compact=True)) +>>> alc.parse("gcc -Fabc -Fdef -Fxyz").query[list[str]]("flag.content") ['abc', 'def', 'xyz'] ``` @@ -247,7 +277,7 @@ assert alc.parse("test123 BARabc").matched ```python >>> from arclet.alconna import Alconna, Option, Args, count >>> alc = Alconna("pp", Option("--verbose|-v", action=count, default=0)) ->>> alc.parse("pp -vvv").query("verbose.value") +>>> alc.parse("pp -vvv").query[int]("verbose.value") 3 ``` @@ -291,13 +321,13 @@ with namespace(config.default_namespace.name) as np: 半自动补全为用户提供了推荐后续输入的功能。 -补全默认通过 `--comp` 或 `-cp` 触发:(命名空间配置可修改名称) +补全默认通过 `--comp` 或 `-cp` 或 `?` 触发:(命名空间配置可修改名称) ```python from arclet.alconna import Alconna, Args, Option alc = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar") -alc.parse("test --comp") +alc.parse("test ?") ''' output @@ -323,7 +353,7 @@ output >>> from arclet.alconna import Alconna, Args >>> alc = Alconna("setu", Args["count", int]) >>> alc.shortcut("涩图(\d+)张", {"args": ["{0}"]}) -'Alconna::setu 的快截指令: "涩图(\\d+)张" 添加成功' +'Alconna::setu 的快捷指令: "涩图(\\d+)张" 添加成功' >>> alc.parse("涩图3张").query("count") 3 ``` @@ -331,32 +361,40 @@ output `shortcut` 的第一个参数为快捷指令名称,第二个参数为 `ShortcutArgs`,作为快捷指令的配置 ```python -class ShortcutArgs(TypedDict, Generic[TDC]): +class ShortcutArgs(TypedDict): """快捷指令参数""" - command: NotRequired[TDC] + command: NotRequired[DataCollection[Any]] """快捷指令的命令""" args: NotRequired[list[Any]] """快捷指令的附带参数""" fuzzy: NotRequired[bool] """是否允许命令后随参数""" + prefix: NotRequired[bool] + """是否调用时保留指令前缀""" ``` 当 `fuzzy` 为 False 时,传入 `"涩图1张 abc"` 之类的快捷指令将视为解析失败 快捷指令允许三类特殊的 placeholder: -- `{%X}`: 只用于 `command`, 如 `setu {%0}`,表示此处填入快截指令后随的第 X 个参数。 +- `{%X}`: 如 `setu {%0}`,表示此处填入快捷指令后随的第 X 个参数。 例如,若快捷指令为 `涩图`, 配置为 `{"command": "setu {%0}"}`, 则指令 `涩图 1` 相当于 `setu 1` -- `{*}`: 只用于 `command`, 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。 -- `{X}`: 用于 `command` 与 `args`, 表示此处填入可能的正则匹配的组: +- `{*}`: 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。 +- `{X}`: 表示此处填入可能的正则匹配的组: - 若 `command` 中存在匹配组 `(xxx)`,则 `{X}` 表示第 X 个匹配组的内容 - 若 `command` 中存储匹配组 `(?P...)`, 则 `{X}` 表示名字为 X 的匹配结果 除此之外, 通过内置选项 `--shortcut` 可以动态操作快捷指令。 +例如: + +- `cmd --shortcut ` 来增加一个快捷指令 +- `cmd --shortcut list` 来列出当前指令的所有快捷指令 +- `cmd --shortcut delete key` 来删除一个快捷指令 + ### 使用模糊匹配 模糊匹配通过在 Alconna 中设置其 CommandMeta 开启。 @@ -370,3 +408,77 @@ alc = Alconna("test_fuzzy", meta=CommandMeta(fuzzy_match=True)) alc.parse("test_fuzy") # output: test_fuzy is not matched. Do you mean "test_fuzzy"? ``` + +## 解析结果 + +`Alconna.parse` 会返回由 **Arparma** 承载的解析结果。 + +`Arpamar` 会有如下参数: + +- 调试类 + + - matched: 是否匹配成功 + - error_data: 解析失败时剩余的数据 + - error_info: 解析失败时的异常内容 + - origin: 原始命令,可以类型标注 + +- 分析类 + - header_match: 命令头部的解析结果,包括原始头部、解析后头部、解析结果与可能的正则匹配组 + - main_args: 命令的主参数的解析结果 + - options: 命令所有选项的解析结果 + - subcommands: 命令所有子命令的解析结果 + - other_args: 除主参数外的其他解析结果 + - all_matched_args: 所有 Args 的解析结果 + +`Arparma` 同时提供了便捷的查询方法 `query[type]()`,会根据传入的 `path` 查找参数并返回 + +`path` 支持如下: + +- `main_args`, `options`, ...: 返回对应的属性 +- `args`: 返回 all_matched_args +- `main_args.xxx`, `options.xxx`, ...: 返回字典中 `xxx`键对应的值 +- `args.xxx`: 返回 all_matched_args 中 `xxx`键对应的值 +- `options.foo`, `foo`: 返回选项 `foo` 的解析结果 (OptionResult) +- `options.foo.value`, `foo.value`: 返回选项 `foo` 的解析值 +- `options.foo.args`, `foo.args`: 返回选项 `foo` 的解析参数字典 +- `options.foo.args.bar`, `foo.bar`: 返回选项 `foo` 的参数字典中 `bar` 键对应的值 + ... + +同样, `Arparma["foo.bar"]` 的表现与 `query()` 一致 + +## Duplication + +**Duplication** 用来提供更好的自动补全,类似于 **ArgParse** 的 **Namespace**,经测试表现良好(好耶)。 + +普通情况下使用,需要利用到 **ArgsStub**、**OptionStub** 和 **SubcommandStub** 三个部分, + +以pip为例,其对应的 Duplication 应如下构造: + +```python +from arclet.alconna import OptionResult, Duplication, SubcommandStub + +class MyDup(Duplication): + verbose: OptionResult + install: SubcommandStub # 选项与子命令对应的stub的变量名必须与其名字相同 +``` + +并在解析时传入 Duplication: + +```python +result = alc.parse("pip -v install ...", duplication=MyDup) +>>> type(result) + +``` + +**Duplication** 也可以如 **Namespace** 一样直接标明参数名称和类型: + +```python +from typing import Optional +from arclet.alconna import Duplication + + +class MyDup(Duplication): + package: str + file: Optional[str] = None + url: Optional[str] = None +``` diff --git a/website/docs/best-practice/alconna/config.md b/website/docs/best-practice/alconna/config.md index 65595277..8b92ef23 100644 --- a/website/docs/best-practice/alconna/config.md +++ b/website/docs/best-practice/alconna/config.md @@ -32,3 +32,10 @@ description: 配置项 - **默认值**: `False` 是否全局使用原始消息 (即未经过 to_me 等处理的), 该选项会影响到 Alconna 的匹配行为。 + +## alconna_use_param + +- **类型**: `bool` +- **默认值**: `True` + +是否使用特制的 Param 提供更好的依赖注入,该选项不会对使用依赖注入函数形式造成影响 diff --git a/website/docs/best-practice/alconna/matcher.md b/website/docs/best-practice/alconna/matcher.md index 54f35c63..c2982a50 100644 --- a/website/docs/best-practice/alconna/matcher.md +++ b/website/docs/best-practice/alconna/matcher.md @@ -8,7 +8,7 @@ description: 响应规则的使用 以下为一个简单的使用示例: ```python -from nonebot_plugin_alconna.adapters import At +from nonebot_plugin_alconna import At from nonebot.adapters.onebot.v12 import Message from nonebot_plugin_alconna.adapters.onebot12 import Image from nonebot_plugin_alconna import AlconnaMatches, on_alconna @@ -36,8 +36,8 @@ async def _(result: Arparma = AlconnaMatches()): if result.find("add"): group = await create_role_group(result["add.name"]) if result.find("add.member"): - ats: tuple[Ob12MS, ...] = result["add.member.target"] - group.extend(member.data["user_id"] for member in ats) + ats: tuple[At, ...] = result["add.member.target"] + group.extend(member.target for member in ats) await rg.finish("添加成功") ``` @@ -52,12 +52,14 @@ async def _(result: Arparma = AlconnaMatches()): - `aliases: set[str | tuple[str, ...]] | None = None`: 命令别名, 作用类似于 `on_command` 中的 aliases - `comp_config: CompConfig | None = None`: 补全会话配置, 不传入则不启用补全会话 - `use_origin: bool = False`: 是否使用未经 to_me 等处理过的消息 +- `use_cmd_start`: 是否使用 COMMAND_START 作为命令前缀 -`on_alconna` 返回的是 `Matcher` 的子类 `AlconnaMatcher`,其拓展了四类方法: +`on_alconna` 返回的是 `Matcher` 的子类 `AlconnaMatcher`,其拓展了五类方法: - `.assign(path, value, or_not)`: 用于对包含多个选项/子命令的命令的分派处理 -- `.got_path(path, prompt)`: 在 `got` 方法的基础上,会以 path 对应的参数为准,读取传入 message 的最后一个消息段并验证转换 +- `.got_path(path, prompt, middleware)`: 在 `got` 方法的基础上,会以 path 对应的参数为准,读取传入 message 的最后一个消息段并验证转换 - `.set_path_arg(key, value)`, `.get_path_arg(key)`: 类似 `set_arg` 和 `got_arg`,为 `got_path` 的特化版本 +- `.reject_path(path[, prompt])`: 类似于 `reject_arg`,对应 `got_path` 用例: @@ -90,8 +92,8 @@ async def login_got(password: str = AlconnaArg("password")): - `AlconnaResult`: `CommandResult` 类型的依赖注入函数 - `AlconnaMatches`: `Arparma` 类型的依赖注入函数 - `AlconnaDuplication`: `Duplication` 类型的依赖注入函数 -- `AlconnaMatch`: `Match` 类型的依赖注入函数 -- `AlconnaQuery`: `Query` 类型的依赖注入函数 +- `AlconnaMatch`: `Match` 类型的依赖注入函数,其能够额外传入一个 middleware 函数来处理得到的参数 +- `AlconnaQuery`: `Query` 类型的依赖注入函数,其能够额外传入一个 middleware 函数来处理得到的参数 - `AlconnaExecResult`: 提供挂载在命令上的 callback 的返回结果 (`Dict[str, Any]`) 的依赖注入函数 可以看到,本插件提供了几类额外的模型: @@ -100,12 +102,29 @@ async def login_got(password: str = AlconnaArg("password")): - `Match`: 匹配项,表示参数是否存在于 `all_matched_args` 内,可用 `Match.available` 判断是否匹配,`Match.result` 获取匹配的值 - `Query`: 查询项,表示参数是否可由 `Arparma.query` 查询并获得结果,可用 `Query.available` 判断是否查询成功,`Query.result` 获取查询结果 -同时,基于 [`Annotated` 支持](https://github.com/nonebot/nonebot2/pull/1832), 添加了两类注解: +同时,基于 [`Annotated` 支持](https://github.com/nonebot/nonebot2/pull/1832), 添加了三类注解: - `AlcMatches`:同 `AlconnaMatches` - `AlcResult`:同 `AlconnaResult` - `AlcExecResult`: 同 `AlconnaExecResult` +而若设置配置项 **ALCONNA_USE_PARAM** (默认为 True) 为 True,则上述依赖注入的目标参数皆不需要使用依赖注入函数: + +```python +async def handle( + result: CommandResult, + arp: Arparma, + dup: Duplication, + source: Alconna, + abc: str, # 类似 Match, 但是若匹配结果不存在对应字段则跳过该 handler + foo: Match[str], + bar: Query[int] = Query("ttt.bar", 0) # Query 仍然需要一个默认值来传递 path 参数 +): + ... +``` + +该效果对于 `got_path` 下的 Arg 同样有效 + 实例: ```python @@ -118,9 +137,7 @@ from nonebot_plugin_alconna import ( on_alconna, Match, Query, - AlconnaMatch, AlconnaQuery, - AlconnaMatches, AlcResult ) from arclet.alconna import Alconna, Args, Option, Arparma @@ -141,12 +158,12 @@ async def handle_test1(result: AlcResult): await test.send(f"maybe output: {result.output}") @test.handle() -async def handle_test2(result: Arparma = AlconnaMatches()): +async def handle_test2(result: Arparma): await test.send(f"head result: {result.header_result}") await test.send(f"args: {result.all_matched_args}") @test.handle() -async def handle_test3(bar: Match[int] = AlconnaMatch("bar")): +async def handle_test3(bar: Match[int]): if bar.available: await test.send(f"foo={bar.result}") @@ -160,18 +177,10 @@ async def handle_test4(qux: Query[bool] = AlconnaQuery("baz.qux", False)): 示例中使用了消息段标注,其中 `At` 属于通用标注,而 `Image` 属于 `onebot12` 适配器下的标注。 -消息段标注会匹配特定的 `MessageSegment`: +适配器下的消息段标注会匹配特定的 `MessageSegment`: -```python -... -ats: tuple[Ob12MS, ...] = result["add.member.target"] -group.extend(member.data["user_id"] for member in ats) -``` - -:::tip -通用标注与适配器标注的区别在于,通用标注会匹配多个适配器中相似类型的消息段。 - -通用标注返回的是 `nonebot_plugin_alconna.adapters` 中定义的 `Segment` 模型: +而通用标注与适配器标注的区别在于,通用标注会匹配多个适配器中相似类型的消息段,并返回 +`nonebot_plugin_alconna.adapters` 中定义的 `Segment` 模型: ```python class Segment: @@ -180,8 +189,12 @@ class Segment: class At(Segment): """At对象, 表示一类提醒某用户的元素""" + type: Literal["user", "role", "channel"] target: str +class AtAll(Segment): + """AtAll对象, 表示一类提醒所有人的元素""" + class Emoji(Segment): """Emoji对象, 表示一类表情元素""" id: str @@ -206,33 +219,85 @@ class Video(Media): class File(Segment): """File对象, 表示一类文件元素""" id: str - name: Optional[str] = field(default=None) + name: Optional[str] + +class Reply(Segment): + """Reply对象,表示一类回复消息""" + origin: Any + id: str + msg: Optional[Union[Message, str]] + +class Other(Segment): + """其他 Segment""" ``` ::: -## 特殊装饰器 - -`nonebot_plugin_alconna` 提供 了一个 `funcommand` 装饰器, 其用于将一个接受任意参数, -返回 `str` 或 `Message` 或 `MessageSegment` 的函数转换为命令响应器。 +例如: ```python -from nonebot_plugin_alconna import funcommand - -@funcommand() -async def echo(msg: str): - return msg +... +ats: tuple[At, ...] = result["add.member.target"] +group.extend(member.target for member in ats) ``` -其等同于 +这样插件使用者就不用考虑平台之间字段的差异 + +## 条件控制 + +本插件可以通过 `handle(parameterless)` 来控制一个具体的响应函数是否在不满足条件时跳过响应。 ```python -from arclet.alconna import Alconna, Args -from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match +... +from nonebot import require +require("nonebot_plugin_alconna") +... -echo = on_alconna(Alconna("echo", Args["msg", str])) +from arclet.alconna import Alconna, Subcommand, Option, Args +from nonebot_plugin_alconna import assign, on_alconna, CommandResult, Check -@echo.handle() -async def echo_exit(msg: Match[str] = AlconnaMatch("msg")): - await echo.finish(msg.result) +pip = Alconna( + "pip", + Subcommand( + "install", Args["pak", str], + Option("--upgrade"), + Option("--force-reinstall") + ), + Subcommand("list", Option("--out-dated")) +) + +pip_cmd = on_alconna(pip) + +# 仅在命令为 `pip install` 并且 pak 为 `pip` 时响应 +@pip_cmd.handle([Check(assign("install.pak", "pip"))]) +async def update(arp: CommandResult): + ... + +# 仅在命令为 `pip list` 时响应 +@pip_cmd.handle([Check(assign("list"))]) +async def list_(arp: CommandResult): + ... + +# 仅在命令为 `pip install` 时响应 +@pip_cmd.handle([Check(assign("install"))]) +async def install(arp: CommandResult): + ... +``` + +或者使用 `AlconnaMatcher.assign`: + +```python +@pip_cmd.assign("install.pak", "pip") +async def update(arp: CommandResult): + ... + +# 仅在命令为 `pip list` 时响应 +@pip_cmd.assign("list") +async def list_(arp: CommandResult): + ... + +# 仅在命令为 `pip install` 时响应 +@pip_cmd.assign("install") +async def install(arp: CommandResult): + ... ``` diff --git a/website/docs/best-practice/alconna/utils.md b/website/docs/best-practice/alconna/utils.md new file mode 100644 index 00000000..9e30cffd --- /dev/null +++ b/website/docs/best-practice/alconna/utils.md @@ -0,0 +1,180 @@ +--- +sidebar_position: 5 +description: 杂项 +--- + +# 杂项 + +## 通用消息序列 + +除了之前提到的通用标注外,`nonebot_plugin_alconna` 还提供了一个类似于 `Message` 的 `UniMessage` 类型,其元素为经过通用标注转换后的 `Segment`。 + +你可以通过提供的 `UniversalMessage` 或 `UniMsg` 依赖注入器来获取 `UniMessage`。 + +```python +from nonebot_plugin_alconna import UniMsg, At, Reply + +matcher = on_xxx(...) + +@matcher.handle() +async def _(msg: UniMsg): + reply = msg[Reply, 0] + print(reply.origin) + if msg.has(At): + ats = msg.get(At) + print(ats) + ... +``` + +### 获取消息纯文本 + +类似于 `Message.extract_plain_text()`,用于获取通用消息的纯文本。 + +```python +from nonebot_plugin_alconna import UniMessage, At +# 提取消息纯文本字符串 +assert UniMessage( + [At("user", "1234"), "text"] +).extract_plain_text() == "text" +``` + +### 遍历 + +通用消息序列继承自 `List[Union[str, Segment]]` ,因此可以使用 `for` 循环遍历消息段。 + +```python +for segment in message: # type: Union[str, Segment] + ... +``` + +### 检查消息段 + +我们可以通过 `in` 运算符或消息序列的 `has` 方法来: + +```python +# 是否存在消息段 +At("user", "1234") in message +# 是否存在指定类型的消息段 +At in message +``` + +我们还可以使用 `only` 方法来检查消息中是否仅包含指定的消息段。 + +```python +# 是否都为 "test" +message.only("test") +# 是否仅包含指定类型的消息段 +message.only(str) +``` + +### 过滤、索引与切片 + +消息序列对列表的索引与切片进行了增强,在原有列表 `int` 索引与 `slice` 切片的基础上,支持 `type` 过滤索引与切片。 + +```python +from nonebot_plugin_alconna import UniMessage, At, Reply + +message = UniMessage( + [ + Reply(...), + "text1", + At("user", "1234"), + "text2" + ] +) +# 索引 +message[0] == Reply(...) +# 切片 +message[0:2] == UniMessage([Reply(...), "text1"]) +# 类型过滤 +message[At] == Message([At("user", "1234")]) +# 类型索引 +message[At, 0] == At("user", "1234") +# 类型切片 +message[str, 0:2] == UniMessage(["text1", "text2"]) +``` + +我们也可以通过消息序列的 `include`、`exclude` 方法进行类型过滤。 + +```python +message.include(str, At) +message.exclude(Reply) +``` + +同样的,消息序列对列表的 `index`、`count` 方法也进行了增强,可以用于索引指定类型的消息段。 + +```python +# 指定类型首个消息段索引 +message.index(str) == 1 +# 指定类型消息段数量 +message.count(str) == 2 +``` + +此外,消息序列添加了一个 `get` 方法,可以用于获取指定类型指定个数的消息段。 + +```python +# 获取指定类型指定个数的消息段 +message.get(str, 1) == UniMessage(["test1"]) +``` + +## 特殊装饰器 + +`nonebot_plugin_alconna` 提供 了一个 `funcommand` 装饰器, 其用于将一个接受任意参数, +返回 `str` 或 `Message` 或 `MessageSegment` 的函数转换为命令响应器。 + +```python +from nonebot_plugin_alconna import funcommand + +@funcommand() +async def echo(msg: str): + return msg +``` + +其等同于 + +```python +from arclet.alconna import Alconna, Args +from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match + +echo = on_alconna(Alconna("echo", Args["msg", str])) + +@echo.handle() +async def echo_exit(msg: Match[str] = AlconnaMatch("msg")): + await echo.finish(msg.result) +``` + +## 特殊构造器 + +`nonebot_plugin_alconna` 提供了一个 `Command` 构造器,其基于 `arclet.alconna.tools` 中的 `AlconnaString`, +以类似 `Koishi` 中注册命令的方式来构建一个 AlconnaMatcher: + +```python +from nonebot_plugin_alconna import Command, Arparma + +book = ( + Command("book", "测试") + .option("writer", "-w ") + .option("writer", "--anonymous", {"id": 0}) + .usage("book [-w | --anonymous]") + .shortcut("测试", {"args": ["--anonymous"]}) + .build() +) + +@book.handle() +async def _(arp: Arparma): + await book.send(str(arp.options)) +``` + +甚至,你可以设置 `action` 来设定响应行为: + +```python +book = ( + Command("book", "测试") + .option("writer", "-w ") + .option("writer", "--anonymous", {"id": 0}) + .usage("book [-w | --anonymous]") + .shortcut("测试", {"args": ["--anonymous"]}) + .action(lambda options: str(options)) # 会自动通过 bot.send 发送 + .build() +) +``` diff --git a/website/static/plugins.json b/website/static/plugins.json index 4ff5e3f8..343518a8 100644 --- a/website/static/plugins.json +++ b/website/static/plugins.json @@ -303,6 +303,31 @@ "type": null, "supported_adapters": null }, + { + "module_name": "nonebot_plugin_alconna", + "project_link": "nonebot-plugin-alconna", + "name": "Alconna 命令工具", + "desc": "提供一系列工具以在 nonebot 下使用 Alconna 拓展命令解析", + "author": "RF-Tar-Railt", + "homepage": "https://github.com/nonebot/plugin-alconna", + "tags": [ + { + "label": "matcher", + "color": "#5280ea" + }, + { + "label": "command", + "color": "#ea6f52" + }, + { + "label": "alconna", + "color": "#5452ea" + } + ], + "is_official": true, + "type": null, + "supported_adapters": null + }, { "module_name": "nonebot_plugin_mcstatus", "project_link": "nonebot-plugin-mcstatus", @@ -3990,31 +4015,6 @@ "type": null, "supported_adapters": null }, - { - "module_name": "nonebot_plugin_alconna", - "project_link": "nonebot-plugin-alconna", - "name": "Alconna 命令工具", - "desc": "提供一系列工具以在 nonebot 下使用 Alconna 拓展命令解析", - "author": "RF-Tar-Railt", - "homepage": "https://github.com/nonebot/plugin-alconna", - "tags": [ - { - "label": "matcher", - "color": "#5280ea" - }, - { - "label": "command", - "color": "#ea6f52" - }, - { - "label": "alconna", - "color": "#5452ea" - } - ], - "is_official": true, - "type": null, - "supported_adapters": null - }, { "module_name": "nonebot-plugin-mcport", "project_link": "nonebot-plugin-mcport",