mirror of
				https://github.com/nonebot/nonebot2.git
				synced 2025-10-31 06:56:39 +00:00 
			
		
		
		
	📝 Docs: 更新最佳实践部分的 Alconna 章节 (#2237)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
		| @@ -333,4 +333,4 @@ matcher2 = group.on_message() | |||||||
| 基于 `Alconna` 的特性,该插件同时提供了一系列便捷的消息段标注。 | 基于 `Alconna` 的特性,该插件同时提供了一系列便捷的消息段标注。 | ||||||
| 标注可用于在 `Alconna` 中匹配消息中除 text 外的其他消息段,也可用于快速创建各适配器下的消息段。所有标注位于 `nonebot_plugin_alconna.adapters` 中。 | 标注可用于在 `Alconna` 中匹配消息中除 text 外的其他消息段,也可用于快速创建各适配器下的消息段。所有标注位于 `nonebot_plugin_alconna.adapters` 中。 | ||||||
|  |  | ||||||
| 详情请阅读最佳实践中的 [命令解析拓展](../best-practice/alconna.md) 章节。 | 详情请阅读最佳实践中的 [命令解析拓展](../best-practice/alconna/README.mdx) 章节。 | ||||||
|   | |||||||
| @@ -1,288 +0,0 @@ | |||||||
| --- |  | ||||||
| sidebar_position: 6 |  | ||||||
| description: Alconna 命令解析拓展 |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| # Alconna 命令解析 |  | ||||||
|  |  | ||||||
| [`nonebot-plugin-alconna`](https://github.com/nonebot/plugin-alconna) 是一类提供了拓展响应规则的插件。 |  | ||||||
| 该插件使用 [Alconna](https://github.com/ArcletProject/Alconna) 作为命令解析器, |  | ||||||
| 是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。 |  | ||||||
|  |  | ||||||
| 特点包括: |  | ||||||
|  |  | ||||||
| - 高效 |  | ||||||
| - 直观的命令组件创建方式 |  | ||||||
| - 强大的类型解析与类型转换功能 |  | ||||||
| - 自定义的帮助信息格式 |  | ||||||
| - 多语言支持 |  | ||||||
| - 易用的快捷命令创建与使用 |  | ||||||
| - 可创建命令补全会话, 以实现多轮连续的补全提示 |  | ||||||
| - 可嵌套的多级子命令 |  | ||||||
| - 正则匹配支持 |  | ||||||
|  |  | ||||||
| 该插件提供了一类新的事件响应器辅助函数 `on_alconna`,以及 `AlconnaResult` 等依赖注入函数。 |  | ||||||
|  |  | ||||||
| 同时,基于 [Annotated 支持](https://github.com/nonebot/nonebot2/pull/1832), 添加了两类注解 `AlcMatches` 与 `AlcResult` |  | ||||||
|  |  | ||||||
| 该插件还可以通过 `handle(parameterless)` 来控制一个具体的响应函数是否在不满足条件时跳过响应: |  | ||||||
|  |  | ||||||
| - `pip.handle([Check(assign("add.name", "nb"))])` 表示仅在命令为 `role-group add` 并且 name 为 `nb` 时响应 |  | ||||||
| - `pip.handle([Check(assign("list"))])` 表示仅在命令为 `role-group list` 时响应 |  | ||||||
| - `pip.handle([Check(assign("add"))])` 表示仅在命令为 `role-group add` 时响应 |  | ||||||
|  |  | ||||||
| 基于 `Alconna` 的特性,该插件同时提供了一系列便捷的消息段标注。 |  | ||||||
| 标注可用于在 `Alconna` 中匹配消息中除 text 外的其他消息段,也可用于快速创建各适配器下的消息段。所有标注位于 `nonebot_plugin_alconna.adapters` 中。 |  | ||||||
|  |  | ||||||
| ## 安装插件 |  | ||||||
|  |  | ||||||
| 在使用前请先安装 `nonebot-plugin-alconna` 插件至项目环境中,可参考[获取商店插件](../tutorial/store.mdx#安装插件)来了解并选择安装插件的方式。如: |  | ||||||
|  |  | ||||||
| 在**项目目录**下执行以下命令: |  | ||||||
|  |  | ||||||
| ```shell |  | ||||||
| nb plugin install nonebot-plugin-alconna |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 或 |  | ||||||
|  |  | ||||||
| ```shell |  | ||||||
| pip install nonebot-plugin-alconna |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## 使用插件 |  | ||||||
|  |  | ||||||
| 以下为一个简单的使用示例: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| from nonebot_plugin_alconna.adapters 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 |  | ||||||
| from nonebot.adapters.onebot.v12 import MessageSegment as Ob12MS |  | ||||||
| from arclet.alconna import Args, Option, Alconna, Arparma, MultiVar, Subcommand |  | ||||||
|  |  | ||||||
| alc = Alconna( |  | ||||||
|     ["/", "!"], |  | ||||||
|     "role-group", |  | ||||||
|     Subcommand( |  | ||||||
|         "add", |  | ||||||
|         Args["name", str], |  | ||||||
|         Option("member", Args["target", MultiVar(At)]), |  | ||||||
|     ), |  | ||||||
|     Option("list"), |  | ||||||
| ) |  | ||||||
| rg = on_alconna(alc, auto_send_output=True) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @rg.handle() |  | ||||||
| async def _(result: Arparma = AlconnaMatches()): |  | ||||||
|     if result.find("list"): |  | ||||||
|         img = await gen_role_group_list_image() |  | ||||||
|         await rg.finish(Message([Image(img)])) |  | ||||||
|     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) |  | ||||||
|         await rg.finish("添加成功") |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### 导入插件 |  | ||||||
|  |  | ||||||
| 由于 `nonebot-plugin-alconna` 作为插件,因此需要在使用前对其进行**加载**并**导入**其中的 `on_alconna` 来使用命令拓展。使用 `require` 方法可轻松完成这一过程,可参考 [跨插件访问](../advanced/requiring.md) 一节进行了解。 |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| from nonebot import require |  | ||||||
|  |  | ||||||
| require("nonebot_plugin_alconna") |  | ||||||
|  |  | ||||||
| from nonebot_plugin_alconna import on_alconna |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### 命令编写 |  | ||||||
|  |  | ||||||
| 我们可以看到主要的两大组件:`Option` 与 `Subcommand`。 |  | ||||||
|  |  | ||||||
| `Option` 可以传入一组别名,如 `Option("--foo|-F|--FOO|-f")` 或 `Option("--foo", alias=["-F"]` |  | ||||||
|  |  | ||||||
| `Subcommand` 则可以传入自己的 `Option` 与 `Subcommand`: |  | ||||||
|  |  | ||||||
| 他们拥有如下共同参数: |  | ||||||
|  |  | ||||||
| - `help_text`: 传入该组件的帮助信息 |  | ||||||
| - `dest`: 被指定为解析完成时标注匹配结果的标识符,不传入时默认为选项或子命令的名称 (name) |  | ||||||
| - `requires`: 一段指定顺序的字符串列表,作为唯一的前置序列与命令嵌套替换 |  | ||||||
| - `default`: 默认值,在该组件未被解析时使用使用该值替换。 |  | ||||||
|  |  | ||||||
| 然后是 `Args` 与 `MultiVar`,他们是用于解析参数的组件。 |  | ||||||
|  |  | ||||||
| `Args` 是参数解析的基础组件,构造方法形如 `Args["foo", str]["bar", int]["baz", bool, False]`, |  | ||||||
| 与函数签名类似,但是允许含有默认值的参数在前;同时支持 keyword-only 参数不依照构造顺序传入 (但是仍需要在非 keyword-only 参数之后)。 |  | ||||||
|  |  | ||||||
| `MultiVar` 则是一个特殊的标注,用于告知解析器该参数可以接受多个值,其构造方法形如 `MultiVar(str)`。 |  | ||||||
| 同样的还有 `KeyWordVar`,其构造方法形如 `KeyWordVar(str)`,用于告知解析器该参数为一个 keyword-only 参数。 |  | ||||||
|  |  | ||||||
| :::tip |  | ||||||
| `MultiVar` 与 `KeyWordVar` 组合时,代表该参数为一个可接受多个 key-value 的参数,其构造方法形如 `MultiVar(KeyWordVar(str))` |  | ||||||
|  |  | ||||||
| `MultiVar` 与 `KeyWordVar` 也可以传入 `default` 参数,用于指定默认值。 |  | ||||||
|  |  | ||||||
| `MultiVar` 不能在 `KeyWordVar` 之后传入。 |  | ||||||
| ::: |  | ||||||
|  |  | ||||||
| ### 参数标注 |  | ||||||
|  |  | ||||||
| `Args` 的参数类型表面上看需要传入一个 `type`,但实际上它需要的是一个 `nepattern.BasePattern` 的实例。 |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| from arclet.alconna import Args |  | ||||||
| from nepattern import BasePattern |  | ||||||
|  |  | ||||||
| # 表示 foo 参数需要匹配一个 @number 样式的字符串 |  | ||||||
| args = Args["foo", BasePattern("@\d+")] |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 示例中传入的 `str` 是因为 `str` 已经注册在了 `nepattern.global_patterns` 中,因此会替换为 `nepattern.global_patterns[str]`。 |  | ||||||
|  |  | ||||||
| 默认支持的类型有: |  | ||||||
|  |  | ||||||
| - `str`: 匹配任意字符串 |  | ||||||
| - `int`: 匹配整数 |  | ||||||
| - `float`: 匹配浮点数 |  | ||||||
| - `bool`: 匹配 `True` 与 `False` 以及他们小写形式 |  | ||||||
| - `hex`: 匹配 `0x` 开头的十六进制字符串 |  | ||||||
| - `url`: 匹配网址 |  | ||||||
| - `email`: 匹配 `xxxx@xxx` 的字符串 |  | ||||||
| - `ipv4`: 匹配 `xxx.xxx.xxx.xxx` 的字符串 |  | ||||||
| - `list`: 匹配类似 `["foo","bar","baz"]` 的字符串 |  | ||||||
| - `dict`: 匹配类似 `{"foo":"bar","baz":"qux"}` 的字符串 |  | ||||||
| - `datetime`: 传入一个 `datetime` 支持的格式字符串,或时间戳 |  | ||||||
| - `Any`: 匹配任意类型 |  | ||||||
| - `AnyString`: 匹配任意类型,转为 `str` |  | ||||||
| - `Number`: 匹配 `int` 与 `float`,转为 `int` |  | ||||||
|  |  | ||||||
| 同时可以使用 typing 中的类型: |  | ||||||
|  |  | ||||||
| - `Literal[X]`: 匹配其中的任意一个值 |  | ||||||
| - `Union[X, Y]`: 匹配其中的任意一个类型 |  | ||||||
| - `Optional[xxx]`: 会自动将默认值设为 `None`,并在解析失败时使用默认值 |  | ||||||
| - `List[X]`: 匹配一个列表,其中的元素为 `X` 类型 |  | ||||||
| - `Dict[X, Y]`: 匹配一个字典,其中的 key 为 `X` 类型,value 为 `Y` 类型 |  | ||||||
| - ... |  | ||||||
|  |  | ||||||
| :::tip |  | ||||||
| 几类特殊的传入标记: |  | ||||||
|  |  | ||||||
| - `"foo"`: 匹配字符串 "foo" (若没有某个 `BasePattern` 与之关联) |  | ||||||
| - `RawStr("foo")`: 匹配字符串 "foo" (不会被 `BasePattern` 替换) |  | ||||||
| - `"foo|bar|baz"`: 匹配 "foo" 或 "bar" 或 "baz" |  | ||||||
| - `[foo, bar, Baz, ...]`: 匹配其中的任意一个值或类型 |  | ||||||
| - `Callable[[X], Y]`: 匹配一个参数为 `X` 类型的值,并返回通过该函数调用得到的 `Y` 类型的值 |  | ||||||
| - `"re:xxx"`: 匹配一个正则表达式 `xxx`,会返回 Match[0] |  | ||||||
| - `"rep:xxx"`: 匹配一个正则表达式 `xxx`,会返回 `re.Match` 对象 |  | ||||||
| - `{foo: bar, baz: qux}`: 匹配字典中的任意一个键, 并返回对应的值 (特殊的键 ... 会匹配任意的值) |  | ||||||
| - ... |  | ||||||
|  |  | ||||||
| ::: |  | ||||||
|  |  | ||||||
| ### 消息段标注 |  | ||||||
|  |  | ||||||
| 示例中使用了消息段标注,其中 `At` 属于通用标注,而 `Image` 属于 `onebot12` 适配器下的标注。 |  | ||||||
|  |  | ||||||
| 消息段标注会匹配特定的 `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` 模型: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| class Segment: |  | ||||||
|     """基类标注""" |  | ||||||
|     origin: MessageSegment |  | ||||||
|  |  | ||||||
| class At(Segment): |  | ||||||
|     """At对象, 表示一类提醒某用户的元素""" |  | ||||||
|     target: str |  | ||||||
|  |  | ||||||
| class Emoji(Segment): |  | ||||||
|     """Emoji对象, 表示一类表情元素""" |  | ||||||
|     id: str |  | ||||||
|     name: Optional[str] |  | ||||||
|  |  | ||||||
| class Media(Segment): |  | ||||||
|     url: Optional[str] |  | ||||||
|     id: Optional[str] |  | ||||||
|  |  | ||||||
| class Image(Media): |  | ||||||
|     """Image对象, 表示一类图片元素""" |  | ||||||
|  |  | ||||||
| class Audio(Media): |  | ||||||
|     """Audio对象, 表示一类音频元素""" |  | ||||||
|  |  | ||||||
| class Voice(Media): |  | ||||||
|     """Voice对象, 表示一类语音元素""" |  | ||||||
|  |  | ||||||
| class Video(Media): |  | ||||||
|     """Video对象, 表示一类视频元素""" |  | ||||||
|  |  | ||||||
| class File(Segment): |  | ||||||
|     """File对象, 表示一类文件元素""" |  | ||||||
|     id: str |  | ||||||
|     name: Optional[str] = field(default=None) |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ::: |  | ||||||
|  |  | ||||||
| ### 响应器使用 |  | ||||||
|  |  | ||||||
| `on_alconna` 的所有参数如下: |  | ||||||
|  |  | ||||||
| - `command: Alconna | str`: Alconna 命令 |  | ||||||
| - `skip_for_unmatch: bool = True`: 是否在命令不匹配时跳过该响应 |  | ||||||
| - `auto_send_output: bool = False`: 是否自动发送输出信息并跳过响应 |  | ||||||
| - `output_converter: TConvert | None = None`: 输出信息字符串转换为消息序列方法 |  | ||||||
| - `aliases: set[str | tuple[str, ...]] | None = None`: 命令别名, 作用类似于 `on_command` 中的 aliases |  | ||||||
| - `comp_config: CompConfig | None = None`: 补全会话配置, 不传入则不启用补全会话 |  | ||||||
|  |  | ||||||
| `AlconnaMatches` 是一个依赖注入函数,可注入 `Alconna` 命令解析结果。 |  | ||||||
|  |  | ||||||
| ### 配置项 |  | ||||||
|  |  | ||||||
| #### alconna_auto_send_output |  | ||||||
|  |  | ||||||
| - **类型**: `bool` |  | ||||||
| - **默认值**: `False` |  | ||||||
|  |  | ||||||
| "是否全局启用输出信息自动发送,不启用则会在触特殊内置选项后仍然将解析结果传递至响应器。 |  | ||||||
|  |  | ||||||
| #### alconna_use_command_start |  | ||||||
|  |  | ||||||
| - **类型**: `bool` |  | ||||||
| - **默认值**: `False` |  | ||||||
|  |  | ||||||
| 是否读取 Nonebot 的配置项 `COMMAND_START` 来作为全局的 Alconna 命令前缀 |  | ||||||
|  |  | ||||||
| #### alconna_auto_completion |  | ||||||
|  |  | ||||||
| - **类型**: `bool` |  | ||||||
| - **默认值**: `False` |  | ||||||
|  |  | ||||||
| 是否全局启用命令自动补全,启用后会在参数缺失或触发 `--comp` 选项时自自动启用交互式补全。 |  | ||||||
|  |  | ||||||
| ## 文档参考 |  | ||||||
|  |  | ||||||
| 插件文档: [📦 这里](https://github.com/nonebot/plugin-alconna/blob/master/docs.md) |  | ||||||
|  |  | ||||||
| 官方文档: [👉 指路](https://arclet.top/) |  | ||||||
|  |  | ||||||
| QQ 交流群: [🔗 链接](https://jq.qq.com/?_wv=1027&k=PUPOnCSH) |  | ||||||
|  |  | ||||||
| 友链: [📚 文档](https://graiax.cn/guide/message_parser/alconna.html) |  | ||||||
							
								
								
									
										120
									
								
								website/docs/best-practice/alconna/README.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								website/docs/best-practice/alconna/README.mdx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | |||||||
|  | --- | ||||||
|  | sidebar_position: 1 | ||||||
|  | description: Alconna 命令解析拓展 | ||||||
|  |  | ||||||
|  | slug: /best-practice/alconna/ | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | # Alconna 插件 | ||||||
|  |  | ||||||
|  | [`nonebot-plugin-alconna`](https://github.com/nonebot/plugin-alconna) 是一类提供了拓展响应规则的插件。 | ||||||
|  | 该插件使用 [Alconna](https://github.com/ArcletProject/Alconna) 作为命令解析器, | ||||||
|  | 是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。 | ||||||
|  |  | ||||||
|  | 该插件提供了一类新的事件响应器辅助函数 `on_alconna`,以及 `AlconnaResult` 等依赖注入函数。 | ||||||
|  |  | ||||||
|  | 同时,基于 [Annotated 支持](https://github.com/nonebot/nonebot2/pull/1832), 添加了两类注解 `AlcMatches` 与 `AlcResult` | ||||||
|  |  | ||||||
|  | 该插件还可以通过 `handle(parameterless)` 来控制一个具体的响应函数是否在不满足条件时跳过响应: | ||||||
|  |  | ||||||
|  | - `pip.handle([Check(assign("add.name", "nb"))])` 表示仅在命令为 `role-group add` 并且 name 为 `nb` 时响应 | ||||||
|  | - `pip.handle([Check(assign("list"))])` 表示仅在命令为 `role-group list` 时响应 | ||||||
|  | - `pip.handle([Check(assign("add"))])` 表示仅在命令为 `role-group add` 时响应 | ||||||
|  |  | ||||||
|  | 基于 `Alconna` 的特性,该插件同时提供了一系列便捷的消息段标注。 | ||||||
|  | 标注可用于在 `Alconna` 中匹配消息中除 text 外的其他消息段,也可用于快速创建各适配器下的消息段。所有标注位于 `nonebot_plugin_alconna.adapters` 中。 | ||||||
|  |  | ||||||
|  | ## 安装插件 | ||||||
|  |  | ||||||
|  | 在使用前请先安装 `nonebot-plugin-alconna` 插件至项目环境中,可参考[获取商店插件](../../tutorial/store.mdx#安装插件)来了解并选择安装插件的方式。如: | ||||||
|  |  | ||||||
|  | 在**项目目录**下执行以下命令: | ||||||
|  |  | ||||||
|  | ```shell | ||||||
|  | nb plugin install nonebot-plugin-alconna | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | 或 | ||||||
|  |  | ||||||
|  | ```shell | ||||||
|  | pip install nonebot-plugin-alconna | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## 导入插件 | ||||||
|  |  | ||||||
|  | 由于 `nonebot-plugin-alconna` 作为插件,因此需要在使用前对其进行**加载**并**导入**其中的 `on_alconna` 来使用命令拓展。使用 `require` 方法可轻松完成这一过程,可参考 [跨插件访问](../../advanced/requiring.md) 一节进行了解。 | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from nonebot import require | ||||||
|  |  | ||||||
|  | require("nonebot_plugin_alconna") | ||||||
|  |  | ||||||
|  | from nonebot_plugin_alconna import on_alconna | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## 使用插件 | ||||||
|  |  | ||||||
|  | 在前面的[深入指南](../../appendices/session-control.mdx)中,我们已经得到了一个天气插件。 | ||||||
|  | 现在我们将使用 `Alconna` 来改写这个插件。 | ||||||
|  |  | ||||||
|  | <details> | ||||||
|  |   <summary>插件示例</summary> | ||||||
|  |  | ||||||
|  | ```python title=weather/__init__.py | ||||||
|  | from nonebot import on_command | ||||||
|  | from nonebot.rule import to_me | ||||||
|  | from nonebot.matcher import Matcher | ||||||
|  | from nonebot.adapters import Message | ||||||
|  | from nonebot.params import CommandArg, ArgPlainText | ||||||
|  |  | ||||||
|  | weather = on_command("天气", rule=to_me(), aliases={"weather", "天气预报"}) | ||||||
|  |  | ||||||
|  | @weather.handle() | ||||||
|  | async def handle_function(matcher: Matcher, args: Message = CommandArg()): | ||||||
|  |     if args.extract_plain_text(): | ||||||
|  |         matcher.set_arg("location", args) | ||||||
|  |  | ||||||
|  | @weather.got("location", prompt="请输入地名") | ||||||
|  | async def got_location(location: str = ArgPlainText()): | ||||||
|  |     if location not in ["北京", "上海", "广州", "深圳"]: | ||||||
|  |         await weather.reject(f"你想查询的城市 {location} 暂不支持,请重新输入!") | ||||||
|  |     await weather.finish(f"今天{location}的天气是...") | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | </details> | ||||||
|  |  | ||||||
|  | ```python {5-8,11-13,15-16} | ||||||
|  | from nonebot.rule import to_me | ||||||
|  | from arclet.alconna import Alconna, Args | ||||||
|  | from nonebot_plugin_alconna import Match, AlconnaMatch, on_alconna, AlconnaMatcher, AlconnaArg | ||||||
|  |  | ||||||
|  | weather = on_alconna( | ||||||
|  |     Alconna(["天气", "weather", "天气预报"], Args["location?", str]), | ||||||
|  |     rule=to_me(), | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | @weather.handle() | ||||||
|  | async def handle_function(matcher: AlconnaMatcher, location: Match[str] = AlconnaMatch("location")): | ||||||
|  |     if location.available: | ||||||
|  |         matcher.set_path_arg("location", location.value) | ||||||
|  |  | ||||||
|  | @weather.got_path("location", prompt="请输入地名") | ||||||
|  | async def got_location(location: str = AlconnaArg("location")): | ||||||
|  |     if location not in ["北京", "上海", "广州", "深圳"]: | ||||||
|  |         await weather.reject(f"你想查询的城市 {location} 暂不支持,请重新输入!") | ||||||
|  |     await weather.finish(f"今天{location}的天气是...") | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | 在上面的代码中,我们使用 `Alconna` 来解析命令,`on_alconna` 用来创建响应器,使用 `AlconnaMatch` 来获取解析结果。 | ||||||
|  |  | ||||||
|  | 关于更多 `Alconna` 的使用方法,可参考 [Alconna 文档](https://arclet.top/docs/tutorial/alconna), | ||||||
|  | 或阅读 [Alconna 基本介绍](./alconna.md) 一节。 | ||||||
|  |  | ||||||
|  | 关于更多 `on_alconna` 的使用方法,可参考 [插件文档](https://github.com/nonebot/plugin-alconna/blob/master/docs.md), | ||||||
|  | 或阅读 [响应规则的使用](./matcher.md) 一节。 | ||||||
|  |  | ||||||
|  | ## 交流与反馈 | ||||||
|  |  | ||||||
|  | QQ 交流群: [🔗 链接](https://jq.qq.com/?_wv=1027&k=PUPOnCSH) | ||||||
|  |  | ||||||
|  | 友链: [📚 文档](https://graiax.cn/guide/message_parser/alconna.html) | ||||||
							
								
								
									
										4
									
								
								website/docs/best-practice/alconna/_category_.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								website/docs/best-practice/alconna/_category_.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | { | ||||||
|  |   "label": "Alconna 命令解析拓展", | ||||||
|  |   "position": 6 | ||||||
|  | } | ||||||
							
								
								
									
										372
									
								
								website/docs/best-practice/alconna/alconna.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										372
									
								
								website/docs/best-practice/alconna/alconna.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,372 @@ | |||||||
|  | --- | ||||||
|  | sidebar_position: 2 | ||||||
|  | description: Alconna 基本介绍 | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | # Alconna 命令解析 | ||||||
|  |  | ||||||
|  | [Alconna](https://github.com/ArcletProject/Alconna) 作为命令解析器, | ||||||
|  | 是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。 | ||||||
|  |  | ||||||
|  | 特点包括: | ||||||
|  |  | ||||||
|  | - 高效 | ||||||
|  | - 直观的命令组件创建方式 | ||||||
|  | - 强大的类型解析与类型转换功能 | ||||||
|  | - 自定义的帮助信息格式 | ||||||
|  | - 多语言支持 | ||||||
|  | - 易用的快捷命令创建与使用 | ||||||
|  | - 可创建命令补全会话, 以实现多轮连续的补全提示 | ||||||
|  | - 可嵌套的多级子命令 | ||||||
|  | - 正则匹配支持 | ||||||
|  |  | ||||||
|  | ## 命令编写 | ||||||
|  |  | ||||||
|  | ### 命令头 | ||||||
|  |  | ||||||
|  | 命令头是指命令的前缀 (Prefix) 与命令名 (Command) 的组合,例如 `!help` 中的 `!` 与 `help`。 | ||||||
|  |  | ||||||
|  | 在 Alconna 中,你可以传入多种类型的命令头,例如: | ||||||
|  |  | ||||||
|  | |             前缀             |   命令名   |                          匹配内容                           |       说明       | | ||||||
|  | | :--------------------------: | :--------: | :---------------------------------------------------------: | :--------------: | | ||||||
|  | |              -               |   "foo"    |                           `"foo"`                           | 无前缀的纯文字头 | | ||||||
|  | |              -               |    123     |                            `123`                            |  无前缀的元素头  | | ||||||
|  | |              -               | "re:\d{2}" |                           `"32"`                            |  无前缀的正则头  | | ||||||
|  | |              -               |    int     |                      `123` 或 `"456"`                       |  无前缀的类型头  | | ||||||
|  | |         [int, bool]          |     -      |                       `True` 或 `123`                       |  无名的元素类头  | | ||||||
|  | |        ["foo", "bar"]        |     -      |                     `"foo"` 或 `"bar"`                      |  无名的纯文字头  | | ||||||
|  | |        ["foo", "bar"]        |   "baz"    |                  `"foobaz"` 或 `"barbaz"`                   |     纯文字头     | | ||||||
|  | |         [int, bool]          |   "foo"    |             `[123, "foo"]` 或 `[False, "foo"]`              |      类型头      | | ||||||
|  | |         [123, 4567]          |   "foo"    |              `[123, "foo"]` 或 `[4567, "foo"]`              |      元素头      | | ||||||
|  | |      [nepattern.NUMBER]      |   "bar"    |            `[123, "bar"]` 或 `[123.456, "bar"]`             |     表达式头     | | ||||||
|  | |         [123, "foo"]         |   "bar"    |      `[123, "bar"]` 或 `"foobar"` 或 `["foo", "bar"]`       |      混合头      | | ||||||
|  | | [(int, "foo"), (456, "bar")] |   "baz"    | `[123, "foobaz"]` 或 `[456, "foobaz"]` 或 `[456, "barbaz"]` |       对头       | | ||||||
|  |  | ||||||
|  | 其中 | ||||||
|  |  | ||||||
|  | - 元素头:只会匹配对应的值,例如 `[123, 456]` 只会匹配 `123` 或 `456`,不会匹配 `789`。 | ||||||
|  | - 纯文字头:只会匹配对应的字符串,例如 `["foo", "bar"]` 只会匹配 `"foo"` 或 `"bar"`,不会匹配 `"baz"`。 | ||||||
|  | - 正则头:`re:xxx` 会将 `xxx` 转为正则表达式,然后匹配对应的字符串,例如 `re:\d{2}` 只会匹配 `"12"` 或 `"34"`,不会匹配 `"foo"`。 | ||||||
|  |   **正则只在命令名上生效,命令前缀中的正则会被转义**。 | ||||||
|  | - 类型头:只会匹配对应的类型,例如 `[int, bool]` 只会匹配 `123` 或 `True`,不会匹配 `"foo"`。 | ||||||
|  |   - 无前缀的类型头:此时会将传入的值尝试转为 BasePattern,例如 `int` 会转为 `nepattern.INTEGER`。此时命令头会匹配对应的类型, | ||||||
|  |     例如 `int` 会匹配 `123` 或 `"456"`,但不会匹配 `"foo"`。同时,Alconna 会将命令头匹配到的值转为对应的类型,例如 `int` 会将 `"123"` 转为 `123`。 | ||||||
|  | - 表达式头:只会匹配对应的表达式,例如 `[nepattern.NUMBER]` 只会匹配 `123` 或 `123.456`,不会匹配 `"foo"`。 | ||||||
|  | - 混合头: | ||||||
|  |  | ||||||
|  | 除了通过传入 `re:xxx` 来使用正则表达式外,Alconna 还提供了一种更加简洁的方式来使用正则表达式,那就是 Bracket Header。 | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from alconna import Alconna | ||||||
|  |  | ||||||
|  | alc = Alconna(".rd{roll:int}") | ||||||
|  | assert alc.parse(".rd123").header["roll"] == 123 | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### 组件 | ||||||
|  |  | ||||||
|  | 我们可以看到主要的两大组件:`Option` 与 `Subcommand`。 | ||||||
|  |  | ||||||
|  | `Option` 可以传入一组 `alias`,如 `Option("--foo|-F|--FOO|-f")` 或 `Option("--foo", alias=["-F"])` | ||||||
|  |  | ||||||
|  | 传入别名后,Option 会选择其中长度最长的作为选项名称。若传入为 "--foo|-f",则命令名称为 "--foo"。 | ||||||
|  |  | ||||||
|  | `Subcommand` 则可以传入自己的 **Option** 与 **Subcommand**。 | ||||||
|  |  | ||||||
|  | 他们拥有如下共同参数: | ||||||
|  |  | ||||||
|  | - `help_text`: 传入该组件的帮助信息 | ||||||
|  | - `dest`: 被指定为解析完成时标注匹配结果的标识符,不传入时默认为选项或子命令的名称 (name) | ||||||
|  | - `requires`: 一段指定顺序的字符串列表,作为唯一的前置序列与命令嵌套替换 | ||||||
|  |   对于命令 `test foo bar baz qux <a:int>` 来讲,因为`foo bar baz` 仅需要判断是否相等, 所以可以这么编写: | ||||||
|  |  | ||||||
|  |   ```python | ||||||
|  |   Alconna("test", Option("qux", Args.a[int], requires=["foo", "bar", "baz"])) | ||||||
|  |   ``` | ||||||
|  |  | ||||||
|  | - `default`: 默认值,在该组件未被解析时使用使用该值替换。 | ||||||
|  |  | ||||||
|  |   特别的,使用 `OptionResult` 或 `SubcomanndResult` 可以设置包括参数字典在内的默认值: | ||||||
|  |  | ||||||
|  |   ```python | ||||||
|  |   from arclet.alconna import Option, OptionResult | ||||||
|  |  | ||||||
|  |   opt1 = Option("--foo", default=False) | ||||||
|  |   opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1})) | ||||||
|  |   ``` | ||||||
|  |  | ||||||
|  | ### 选项操作 | ||||||
|  |  | ||||||
|  | `Option` 可以特别设置传入一类 `Action`,作为解析操作 | ||||||
|  |  | ||||||
|  | `Action` 分为三类: | ||||||
|  |  | ||||||
|  | - `store`: 无 Args 时, 仅存储一个值, 默认为 Ellipsis; 有 Args 时, 后续的解析结果会覆盖之前的值 | ||||||
|  | - `append`: 无 Args 时, 将多个值存为列表, 默认为 Ellipsis; 有 Args 时, 每个解析结果会追加到列表中 | ||||||
|  |  | ||||||
|  |   当存在默认值并且不为列表时, 会自动将默认值变成列表, 以保证追加的正确性 | ||||||
|  |  | ||||||
|  | - `count`: 无 Args 时, 计数器加一; 有 Args 时, 表现与 STORE 相同 | ||||||
|  |  | ||||||
|  |   当存在默认值并且不为数字时, 会自动将默认值变成 1, 以保证计数器的正确性。 | ||||||
|  |  | ||||||
|  | `Alconna` 提供了预制的几类 `action`: | ||||||
|  |  | ||||||
|  | - `store`,`store_value`,`store_true`,`store_false` | ||||||
|  | - `append`,`append_value` | ||||||
|  | - `count` | ||||||
|  |  | ||||||
|  | ### 参数声明 | ||||||
|  |  | ||||||
|  | `Args` 是用于声明命令参数的组件。 | ||||||
|  |  | ||||||
|  | `Args` 是参数解析的基础组件,构造方法形如 `Args["foo", str]["bar", int]["baz", bool, False]`, | ||||||
|  | 与函数签名类似,但是允许含有默认值的参数在前;同时支持 keyword-only 参数不依照构造顺序传入 (但是仍需要在非 keyword-only 参数之后)。 | ||||||
|  |  | ||||||
|  | `Args` 中的 `name` 是用以标记解析出来的参数并存放于 **Arparma** 中,以方便用户调用。 | ||||||
|  |  | ||||||
|  | 其有三种为 Args 注解的标识符: `?`、`/` 与 `!`。标识符与 key 之间建议以 `;` 分隔: | ||||||
|  |  | ||||||
|  | - `!` 标识符表示该处传入的参数应不是规定的类型,或不在指定的值中。 | ||||||
|  | - `?` 标识符表示该参数为可选参数,会在无参数匹配时跳过。 | ||||||
|  | - `/` 标识符表示该参数的类型注解需要隐藏。 | ||||||
|  |  | ||||||
|  | 另外,对于参数的注释也可以标记在 `name` 中,其与 name 或者标识符 以 `#` 分割: | ||||||
|  |  | ||||||
|  | `foo#这是注释;?` 或 `foo?#这是注释` | ||||||
|  |  | ||||||
|  | :::tip | ||||||
|  | `Args` 中的 `name` 在实际命令中并不需要传入(keyword 参数除外): | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from arclet.alconna import Alconna, Args | ||||||
|  |  | ||||||
|  | alc = Alconna("test", Args["foo", str]) | ||||||
|  | alc.parse("test --foo abc")  # 错误 | ||||||
|  | alc.parse("test abc")  # 之前 | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | 若需要 `test --foo abc`,你应该使用 `Option`: | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from arclet.alconna import Alconna, Args, Option | ||||||
|  |  | ||||||
|  | alc = Alconna("test", Option("--foo", Args["foo", str])) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ::: | ||||||
|  |  | ||||||
|  | `Args` 的参数类型表面上看需要传入一个 `type`,但实际上它需要的是一个 `nepattern.BasePattern` 的实例。 | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from arclet.alconna import Args | ||||||
|  | from nepattern import BasePattern | ||||||
|  |  | ||||||
|  | # 表示 foo 参数需要匹配一个 @number 样式的字符串 | ||||||
|  | args = Args["foo", BasePattern("@\d+")] | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | 示例中传入的 `str` 是因为 `str` 已经注册在了 `nepattern.global_patterns` 中,因此会替换为 `nepattern.global_patterns[str]`。 | ||||||
|  |  | ||||||
|  | 默认支持的类型有: | ||||||
|  |  | ||||||
|  | - `str`: 匹配任意字符串 | ||||||
|  | - `int`: 匹配整数 | ||||||
|  | - `float`: 匹配浮点数 | ||||||
|  | - `bool`: 匹配 `True` 与 `False` 以及他们小写形式 | ||||||
|  | - `hex`: 匹配 `0x` 开头的十六进制字符串 | ||||||
|  | - `url`: 匹配网址 | ||||||
|  | - `email`: 匹配 `xxxx@xxx` 的字符串 | ||||||
|  | - `ipv4`: 匹配 `xxx.xxx.xxx.xxx` 的字符串 | ||||||
|  | - `list`: 匹配类似 `["foo","bar","baz"]` 的字符串 | ||||||
|  | - `dict`: 匹配类似 `{"foo":"bar","baz":"qux"}` 的字符串 | ||||||
|  | - `datetime`: 传入一个 `datetime` 支持的格式字符串,或时间戳 | ||||||
|  | - `Any`: 匹配任意类型 | ||||||
|  | - `AnyString`: 匹配任意类型,转为 `str` | ||||||
|  | - `Number`: 匹配 `int` 与 `float`,转为 `int` | ||||||
|  |  | ||||||
|  | 同时可以使用 typing 中的类型: | ||||||
|  |  | ||||||
|  | - `Literal[X]`: 匹配其中的任意一个值 | ||||||
|  | - `Union[X, Y]`: 匹配其中的任意一个类型 | ||||||
|  | - `Optional[xxx]`: 会自动将默认值设为 `None`,并在解析失败时使用默认值 | ||||||
|  | - `List[X]`: 匹配一个列表,其中的元素为 `X` 类型 | ||||||
|  | - `Dict[X, Y]`: 匹配一个字典,其中的 key 为 `X` 类型,value 为 `Y` 类型 | ||||||
|  | - ... | ||||||
|  |  | ||||||
|  | :::tip | ||||||
|  | 几类特殊的传入标记: | ||||||
|  |  | ||||||
|  | - `"foo"`: 匹配字符串 "foo" (若没有某个 `BasePattern` 与之关联) | ||||||
|  | - `RawStr("foo")`: 匹配字符串 "foo" (不会被 `BasePattern` 替换) | ||||||
|  | - `"foo|bar|baz"`: 匹配 "foo" 或 "bar" 或 "baz" | ||||||
|  | - `[foo, bar, Baz, ...]`: 匹配其中的任意一个值或类型 | ||||||
|  | - `Callable[[X], Y]`: 匹配一个参数为 `X` 类型的值,并返回通过该函数调用得到的 `Y` 类型的值 | ||||||
|  | - `"re:xxx"`: 匹配一个正则表达式 `xxx`,会返回 Match[0] | ||||||
|  | - `"rep:xxx"`: 匹配一个正则表达式 `xxx`,会返回 `re.Match` 对象 | ||||||
|  | - `{foo: bar, baz: qux}`: 匹配字典中的任意一个键, 并返回对应的值 (特殊的键 ... 会匹配任意的值) | ||||||
|  | - ... | ||||||
|  |  | ||||||
|  | ::: | ||||||
|  |  | ||||||
|  | `MultiVar` 则是一个特殊的标注,用于告知解析器该参数可以接受多个值,其构造方法形如 `MultiVar(str)`。 | ||||||
|  | 同样的还有 `KeyWordVar`,其构造方法形如 `KeyWordVar(str)`,用于告知解析器该参数为一个 keyword-only 参数。 | ||||||
|  |  | ||||||
|  | :::tip | ||||||
|  | `MultiVar` 与 `KeyWordVar` 组合时,代表该参数为一个可接受多个 key-value 的参数,其构造方法形如 `MultiVar(KeyWordVar(str))` | ||||||
|  |  | ||||||
|  | `MultiVar` 与 `KeyWordVar` 也可以传入 `default` 参数,用于指定默认值。 | ||||||
|  |  | ||||||
|  | `MultiVar` 不能在 `KeyWordVar` 之后传入。 | ||||||
|  | ::: | ||||||
|  |  | ||||||
|  | ### 紧凑命令 | ||||||
|  |  | ||||||
|  | `Alconna`, `Option` 与 `Subcommand` 可以设置 `compact=True` 使得解析命令时允许名称与后随参数之间没有分隔: | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from arclet.alconna import Alconna, Option, CommandMeta, Args | ||||||
|  |  | ||||||
|  | alc = Alconna("test", Args["foo", int], Option("BAR", Args["baz", str], compact=True), meta=CommandMeta(compact=True)) | ||||||
|  |  | ||||||
|  | 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") | ||||||
|  | ['abc', 'def', 'xyz'] | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | 当 `Option` 的 `action` 为 `count` 时,其自动支持 `compact` 特性: | ||||||
|  |  | ||||||
|  | ```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") | ||||||
|  | 3 | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## 命令特性 | ||||||
|  |  | ||||||
|  | ### 配置 | ||||||
|  |  | ||||||
|  | `arclet.alconna.Namespace` 表示某一命名空间下的默认配置: | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from arclet.alconna import config, namespace, Namespace | ||||||
|  | from arclet.alconna.tools import ShellTextFormatter | ||||||
|  |  | ||||||
|  |  | ||||||
|  | np = Namespace("foo", prefixes=["/"])  # 创建 Namespace 对象,并进行初始配置 | ||||||
|  |  | ||||||
|  | with namespace("bar") as np1: | ||||||
|  |     np1.prefixes = ["!"]    # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令 | ||||||
|  |     np1.formatter_type = ShellTextFormatter  # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter | ||||||
|  |     np1.builtin_option_name["help"] = {"帮助", "-h"}  # 设置此命名空间下的命令的帮助选项名称 | ||||||
|  |  | ||||||
|  | config.namespaces["foo"] = np  # 将命名空间挂载到 config 上 | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | 同时也提供了默认命名空间配置与修改方法: | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from arclet.alconna import config, namespace, Namespace | ||||||
|  |  | ||||||
|  |  | ||||||
|  | config.default_namespace.prefixes = [...]  # 直接修改默认配置 | ||||||
|  |  | ||||||
|  | np = Namespace("xxx", prefixes=[...]) | ||||||
|  | config.default_namespace = np  # 更换默认的命名空间 | ||||||
|  |  | ||||||
|  | with namespace(config.default_namespace.name) as np: | ||||||
|  |     np.prefixes = [...] | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### 半自动补全 | ||||||
|  |  | ||||||
|  | 半自动补全为用户提供了推荐后续输入的功能。 | ||||||
|  |  | ||||||
|  | 补全默认通过 `--comp` 或 `-cp` 触发:(命名空间配置可修改名称) | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from arclet.alconna import Alconna, Args, Option | ||||||
|  |  | ||||||
|  | alc = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar") | ||||||
|  | alc.parse("test --comp") | ||||||
|  |  | ||||||
|  | ''' | ||||||
|  | output | ||||||
|  |  | ||||||
|  | 以下是建议的输入: | ||||||
|  | * <abc: int> | ||||||
|  | * --help | ||||||
|  | * -h | ||||||
|  | * -sct | ||||||
|  | * --shortcut | ||||||
|  | * foo | ||||||
|  | * bar | ||||||
|  | ''' | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### 快捷指令 | ||||||
|  |  | ||||||
|  | 快捷指令顾名思义,可以为基础指令创建便捷的触发方式 | ||||||
|  |  | ||||||
|  | 一般情况下你可以通过 `Alconna.shortcut` 进行快捷指令操作 (创建,删除); | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | >>> from arclet.alconna import Alconna, Args | ||||||
|  | >>> alc = Alconna("setu", Args["count", int]) | ||||||
|  | >>> alc.shortcut("涩图(\d+)张", {"args": ["{0}"]}) | ||||||
|  | 'Alconna::setu 的快截指令: "涩图(\\d+)张" 添加成功' | ||||||
|  | >>> alc.parse("涩图3张").query("count") | ||||||
|  | 3 | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | `shortcut` 的第一个参数为快捷指令名称,第二个参数为 `ShortcutArgs`,作为快捷指令的配置 | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | class ShortcutArgs(TypedDict, Generic[TDC]): | ||||||
|  |     """快捷指令参数""" | ||||||
|  |  | ||||||
|  |     command: NotRequired[TDC] | ||||||
|  |     """快捷指令的命令""" | ||||||
|  |     args: NotRequired[list[Any]] | ||||||
|  |     """快捷指令的附带参数""" | ||||||
|  |     fuzzy: NotRequired[bool] | ||||||
|  |     """是否允许命令后随参数""" | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | 当 `fuzzy` 为 False 时,传入 `"涩图1张 abc"` 之类的快捷指令将视为解析失败 | ||||||
|  |  | ||||||
|  | 快捷指令允许三类特殊的 placeholder: | ||||||
|  |  | ||||||
|  | - `{%X}`: 只用于 `command`, 如 `setu {%0}`,表示此处填入快截指令后随的第 X 个参数。 | ||||||
|  |  | ||||||
|  |   例如,若快捷指令为 `涩图`, 配置为 `{"command": "setu {%0}"}`, 则指令 `涩图 1` 相当于 `setu 1` | ||||||
|  |  | ||||||
|  | - `{*}`: 只用于 `command`, 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。 | ||||||
|  | - `{X}`: 用于 `command` 与 `args`, 表示此处填入可能的正则匹配的组: | ||||||
|  |   - 若 `command` 中存在匹配组 `(xxx)`,则 `{X}` 表示第 X 个匹配组的内容 | ||||||
|  |   - 若 `command` 中存储匹配组 `(?P<xxx>...)`, 则 `{X}` 表示名字为 X 的匹配结果 | ||||||
|  |  | ||||||
|  | 除此之外, 通过内置选项 `--shortcut` 可以动态操作快捷指令。 | ||||||
|  |  | ||||||
|  | ### 使用模糊匹配 | ||||||
|  |  | ||||||
|  | 模糊匹配通过在 Alconna 中设置其 CommandMeta 开启。 | ||||||
|  |  | ||||||
|  | 模糊匹配会应用在任意需要进行名称判断的地方,如**命令名称**,**选项名称**和**参数名称**(如指定需要传入参数名称)。 | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from arclet.alconna import Alconna, CommandMeta | ||||||
|  |  | ||||||
|  | alc = Alconna("test_fuzzy", meta=CommandMeta(fuzzy_match=True)) | ||||||
|  | alc.parse("test_fuzy") | ||||||
|  | # output: test_fuzy is not matched. Do you mean "test_fuzzy"? | ||||||
|  | ``` | ||||||
							
								
								
									
										34
									
								
								website/docs/best-practice/alconna/config.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								website/docs/best-practice/alconna/config.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | --- | ||||||
|  | sidebar_position: 4 | ||||||
|  | description: 配置项 | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | # 配置项 | ||||||
|  |  | ||||||
|  | ## alconna_auto_send_output | ||||||
|  |  | ||||||
|  | - **类型**: `bool` | ||||||
|  | - **默认值**: `False` | ||||||
|  |  | ||||||
|  | "是否全局启用输出信息自动发送,不启用则会在触特殊内置选项后仍然将解析结果传递至响应器。 | ||||||
|  |  | ||||||
|  | ## alconna_use_command_start | ||||||
|  |  | ||||||
|  | - **类型**: `bool` | ||||||
|  | - **默认值**: `False` | ||||||
|  |  | ||||||
|  | 是否读取 Nonebot 的配置项 `COMMAND_START` 来作为全局的 Alconna 命令前缀 | ||||||
|  |  | ||||||
|  | ## alconna_auto_completion | ||||||
|  |  | ||||||
|  | - **类型**: `bool` | ||||||
|  | - **默认值**: `False` | ||||||
|  |  | ||||||
|  | 是否全局启用命令自动补全,启用后会在参数缺失或触发 `--comp` 选项时自自动启用交互式补全。 | ||||||
|  |  | ||||||
|  | ## alconna_use_origin | ||||||
|  |  | ||||||
|  | - **类型**: `bool` | ||||||
|  | - **默认值**: `False` | ||||||
|  |  | ||||||
|  | 是否全局使用原始消息 (即未经过 to_me 等处理的), 该选项会影响到 Alconna 的匹配行为。 | ||||||
							
								
								
									
										238
									
								
								website/docs/best-practice/alconna/matcher.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								website/docs/best-practice/alconna/matcher.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,238 @@ | |||||||
|  | --- | ||||||
|  | sidebar_position: 3 | ||||||
|  | description: 响应规则的使用 | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | # Alconna 响应规则 | ||||||
|  |  | ||||||
|  | 以下为一个简单的使用示例: | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from nonebot_plugin_alconna.adapters 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 | ||||||
|  | from nonebot.adapters.onebot.v12 import MessageSegment as Ob12MS | ||||||
|  | from arclet.alconna import Args, Option, Alconna, Arparma, MultiVar, Subcommand | ||||||
|  |  | ||||||
|  | alc = Alconna( | ||||||
|  |     ["/", "!"], | ||||||
|  |     "role-group", | ||||||
|  |     Subcommand( | ||||||
|  |         "add", | ||||||
|  |         Args["name", str], | ||||||
|  |         Option("member", Args["target", MultiVar(At)]), | ||||||
|  |     ), | ||||||
|  |     Option("list"), | ||||||
|  | ) | ||||||
|  | rg = on_alconna(alc, auto_send_output=True) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @rg.handle() | ||||||
|  | async def _(result: Arparma = AlconnaMatches()): | ||||||
|  |     if result.find("list"): | ||||||
|  |         img = await gen_role_group_list_image() | ||||||
|  |         await rg.finish(Message([Image(img)])) | ||||||
|  |     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) | ||||||
|  |         await rg.finish("添加成功") | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## 响应器使用 | ||||||
|  |  | ||||||
|  | `on_alconna` 的所有参数如下: | ||||||
|  |  | ||||||
|  | - `command: Alconna | str`: Alconna 命令 | ||||||
|  | - `skip_for_unmatch: bool = True`: 是否在命令不匹配时跳过该响应 | ||||||
|  | - `auto_send_output: bool = False`: 是否自动发送输出信息并跳过响应 | ||||||
|  | - `output_converter: TConvert | None = None`: 输出信息字符串转换为消息序列方法 | ||||||
|  | - `aliases: set[str | tuple[str, ...]] | None = None`: 命令别名, 作用类似于 `on_command` 中的 aliases | ||||||
|  | - `comp_config: CompConfig | None = None`: 补全会话配置, 不传入则不启用补全会话 | ||||||
|  | - `use_origin: bool = False`: 是否使用未经 to_me 等处理过的消息 | ||||||
|  |  | ||||||
|  | `on_alconna` 返回的是 `Matcher` 的子类 `AlconnaMatcher`,其拓展了四类方法: | ||||||
|  |  | ||||||
|  | - `.assign(path, value, or_not)`: 用于对包含多个选项/子命令的命令的分派处理 | ||||||
|  | - `.got_path(path, prompt)`: 在 `got` 方法的基础上,会以 path 对应的参数为准,读取传入 message 的最后一个消息段并验证转换 | ||||||
|  | - `.set_path_arg(key, value)`, `.get_path_arg(key)`: 类似 `set_arg` 和 `got_arg`,为 `got_path` 的特化版本 | ||||||
|  |  | ||||||
|  | 用例: | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from arclet.alconna import Alconna, Option, Args | ||||||
|  | from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match, AlconnaMatcher, AlconnaArg | ||||||
|  |  | ||||||
|  | login = on_alconna(Alconna(["/"], "login", Args["password?", str], Option("-r|--recall"))) | ||||||
|  |  | ||||||
|  | @login.assign("recall") | ||||||
|  | async def login_exit(): | ||||||
|  |     await login.finish("已退出") | ||||||
|  |  | ||||||
|  | @login.assign("password") | ||||||
|  | async def login_handle(matcher: AlconnaMatcher, pw: Match[str] = AlconnaMatch("password")): | ||||||
|  |     matcher.set_path_arg("password", pw.result) | ||||||
|  |  | ||||||
|  | @login.got_path("password", prompt="请输入密码") | ||||||
|  | async def login_got(password: str = AlconnaArg("password")): | ||||||
|  |     assert password | ||||||
|  |     await login.send("登录成功") | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## 依赖注入 | ||||||
|  |  | ||||||
|  | `Alconna` 的解析结果会放入 `Arparma` 类中,或用户指定的 `Duplication` 类。 | ||||||
|  |  | ||||||
|  | `nonebot_plugin_alconna` 提供了一系列的依赖注入函数,他们包括: | ||||||
|  |  | ||||||
|  | - `AlconnaResult`: `CommandResult` 类型的依赖注入函数 | ||||||
|  | - `AlconnaMatches`: `Arparma` 类型的依赖注入函数 | ||||||
|  | - `AlconnaDuplication`: `Duplication` 类型的依赖注入函数 | ||||||
|  | - `AlconnaMatch`: `Match` 类型的依赖注入函数 | ||||||
|  | - `AlconnaQuery`: `Query` 类型的依赖注入函数 | ||||||
|  | - `AlconnaExecResult`: 提供挂载在命令上的 callback 的返回结果 (`Dict[str, Any]`) 的依赖注入函数 | ||||||
|  |  | ||||||
|  | 可以看到,本插件提供了几类额外的模型: | ||||||
|  |  | ||||||
|  | - `CommandResult`: 解析结果,包括了源命令 `command: Alconna` ,解析结果 `result: Arparma`,以及可能的输出信息 `output: str | None` 字段 | ||||||
|  | - `Match`: 匹配项,表示参数是否存在于 `all_matched_args` 内,可用 `Match.available` 判断是否匹配,`Match.result` 获取匹配的值 | ||||||
|  | - `Query`: 查询项,表示参数是否可由 `Arparma.query` 查询并获得结果,可用 `Query.available` 判断是否查询成功,`Query.result` 获取查询结果 | ||||||
|  |  | ||||||
|  | 同时,基于 [`Annotated` 支持](https://github.com/nonebot/nonebot2/pull/1832), 添加了两类注解: | ||||||
|  |  | ||||||
|  | - `AlcMatches`:同 `AlconnaMatches` | ||||||
|  | - `AlcResult`:同 `AlconnaResult` | ||||||
|  | - `AlcExecResult`: 同 `AlconnaExecResult` | ||||||
|  |  | ||||||
|  | 实例: | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | ... | ||||||
|  | from nonebot import require | ||||||
|  | require("nonebot_plugin_alconna") | ||||||
|  | ... | ||||||
|  |  | ||||||
|  | from nonebot_plugin_alconna import ( | ||||||
|  |     on_alconna, | ||||||
|  |     Match, | ||||||
|  |     Query, | ||||||
|  |     AlconnaMatch, | ||||||
|  |     AlconnaQuery, | ||||||
|  |     AlconnaMatches, | ||||||
|  |     AlcResult | ||||||
|  | ) | ||||||
|  | from arclet.alconna import Alconna, Args, Option, Arparma | ||||||
|  |  | ||||||
|  | test = on_alconna( | ||||||
|  |     Alconna( | ||||||
|  |         "test", | ||||||
|  |         Option("foo", Args["bar", int]), | ||||||
|  |         Option("baz", Args["qux", bool, False]) | ||||||
|  |     ), | ||||||
|  |     auto_send_output=True | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @test.handle() | ||||||
|  | async def handle_test1(result: AlcResult): | ||||||
|  |     await test.send(f"matched: {result.matched}") | ||||||
|  |     await test.send(f"maybe output: {result.output}") | ||||||
|  |  | ||||||
|  | @test.handle() | ||||||
|  | async def handle_test2(result: Arparma = AlconnaMatches()): | ||||||
|  |     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")): | ||||||
|  |     if bar.available: | ||||||
|  |         await test.send(f"foo={bar.result}") | ||||||
|  |  | ||||||
|  | @test.handle() | ||||||
|  | async def handle_test4(qux: Query[bool] = AlconnaQuery("baz.qux", False)): | ||||||
|  |     if qux.available: | ||||||
|  |         await test.send(f"baz.qux={qux.result}") | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## 消息段标注 | ||||||
|  |  | ||||||
|  | 示例中使用了消息段标注,其中 `At` 属于通用标注,而 `Image` 属于 `onebot12` 适配器下的标注。 | ||||||
|  |  | ||||||
|  | 消息段标注会匹配特定的 `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` 模型: | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | class Segment: | ||||||
|  |     """基类标注""" | ||||||
|  |     origin: MessageSegment | ||||||
|  |  | ||||||
|  | class At(Segment): | ||||||
|  |     """At对象, 表示一类提醒某用户的元素""" | ||||||
|  |     target: str | ||||||
|  |  | ||||||
|  | class Emoji(Segment): | ||||||
|  |     """Emoji对象, 表示一类表情元素""" | ||||||
|  |     id: str | ||||||
|  |     name: Optional[str] | ||||||
|  |  | ||||||
|  | class Media(Segment): | ||||||
|  |     url: Optional[str] | ||||||
|  |     id: Optional[str] | ||||||
|  |  | ||||||
|  | class Image(Media): | ||||||
|  |     """Image对象, 表示一类图片元素""" | ||||||
|  |  | ||||||
|  | class Audio(Media): | ||||||
|  |     """Audio对象, 表示一类音频元素""" | ||||||
|  |  | ||||||
|  | class Voice(Media): | ||||||
|  |     """Voice对象, 表示一类语音元素""" | ||||||
|  |  | ||||||
|  | class Video(Media): | ||||||
|  |     """Video对象, 表示一类视频元素""" | ||||||
|  |  | ||||||
|  | class File(Segment): | ||||||
|  |     """File对象, 表示一类文件元素""" | ||||||
|  |     id: str | ||||||
|  |     name: Optional[str] = field(default=None) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ::: | ||||||
|  |  | ||||||
|  | ## 特殊装饰器 | ||||||
|  |  | ||||||
|  | `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) | ||||||
|  | ``` | ||||||
		Reference in New Issue
	
	Block a user