mirror of
				https://github.com/nonebot/nonebot2.git
				synced 2025-10-31 06:56:39 +00:00 
			
		
		
		
	📝 Docs: 更新最佳实践部分的 Alconna 章节 (#2303)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
		| @@ -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<xxx>...)`, 则 `{X}` 表示名字为 X 的匹配结果 | ||||
|  | ||||
| 除此之外, 通过内置选项 `--shortcut` 可以动态操作快捷指令。 | ||||
|  | ||||
| 例如: | ||||
|  | ||||
| - `cmd --shortcut <key> <cmd>` 来增加一个快捷指令 | ||||
| - `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) | ||||
| <class MyDup> | ||||
| ``` | ||||
|  | ||||
| **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 | ||||
| ``` | ||||
|   | ||||
| @@ -32,3 +32,10 @@ description: 配置项 | ||||
| - **默认值**: `False` | ||||
|  | ||||
| 是否全局使用原始消息 (即未经过 to_me 等处理的), 该选项会影响到 Alconna 的匹配行为。 | ||||
|  | ||||
| ## alconna_use_param | ||||
|  | ||||
| - **类型**: `bool` | ||||
| - **默认值**: `True` | ||||
|  | ||||
| 是否使用特制的 Param 提供更好的依赖注入,该选项不会对使用依赖注入函数形式造成影响 | ||||
|   | ||||
| @@ -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): | ||||
|     ... | ||||
| ``` | ||||
|   | ||||
							
								
								
									
										180
									
								
								website/docs/best-practice/alconna/utils.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								website/docs/best-practice/alconna/utils.md
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <id:int>") | ||||
|     .option("writer", "--anonymous", {"id": 0}) | ||||
|     .usage("book [-w <id:int> | --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 <id:int>") | ||||
|     .option("writer", "--anonymous", {"id": 0}) | ||||
|     .usage("book [-w <id:int> | --anonymous]") | ||||
|     .shortcut("测试", {"args": ["--anonymous"]}) | ||||
|     .action(lambda options: str(options))  # 会自动通过 bot.send 发送 | ||||
|     .build() | ||||
| ) | ||||
| ``` | ||||
| @@ -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", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user