mirror of
https://github.com/nonebot/nonebot2.git
synced 2026-09-17 06:43:48 +00:00
⬆️ auto update by pre-commit hooks (#4181)
Release Drafter / release (push) Skipped
Release Drafter / update-release-draft (push) Failing after 38s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.10) (push) Failing after 43s
Ruff Lint / Ruff Lint (push) Successful in 57s
Pyright Lint / Pyright Lint (pydantic-v1) (push) Failing after 1m1s
Pyright Lint / Pyright Lint (pydantic-v2) (push) Failing after 1m4s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.13) (push) Failing after 1m4s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.11) (push) Failing after 1m5s
Site Deploy / publish (push) Failing after 1m8s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.13) (push) Failing after 1m19s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.11) (push) Failing after 1m26s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.10) (push) Failing after 1m29s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.12) (push) Failing after 1m31s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.12) (push) Failing after 1m33s
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.10) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.11) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.12) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.13) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.10) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.11) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.12) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.13) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.10) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.11) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.12) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.13) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.10) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.11) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.12) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.13) (push) Canceled after 0s
Release Drafter / release (push) Skipped
Release Drafter / update-release-draft (push) Failing after 38s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.10) (push) Failing after 43s
Ruff Lint / Ruff Lint (push) Successful in 57s
Pyright Lint / Pyright Lint (pydantic-v1) (push) Failing after 1m1s
Pyright Lint / Pyright Lint (pydantic-v2) (push) Failing after 1m4s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.13) (push) Failing after 1m4s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.11) (push) Failing after 1m5s
Site Deploy / publish (push) Failing after 1m8s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.13) (push) Failing after 1m19s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.11) (push) Failing after 1m26s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.10) (push) Failing after 1m29s
Code Coverage / Test Coverage (pydantic-v2, ubuntu-latest, 3.12) (push) Failing after 1m31s
Code Coverage / Test Coverage (pydantic-v1, ubuntu-latest, 3.12) (push) Failing after 1m33s
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.10) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.11) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.12) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, macos-latest, 3.13) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.10) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.11) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.12) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v1, windows-latest, 3.13) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.10) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.11) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.12) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, macos-latest, 3.13) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.10) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.11) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.12) (push) Canceled after 0s
Code Coverage / Test Coverage (pydantic-v2, windows-latest, 3.13) (push) Canceled after 0s
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
parent
9b4772ff8f
commit
206d31a61d
@@ -20,7 +20,7 @@ alc = Alconna(
|
||||
Args["package", str],
|
||||
Option("-r|--requirement", Args["file", str]),
|
||||
Option("-i|--index-url", Args["url", str]),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
res = alc.parse("pip install nonebot2 -i URL")
|
||||
@@ -113,8 +113,8 @@ from arclet.alconna import Alconna, Args
|
||||
|
||||
|
||||
alc = Alconna("test", Args["foo", str])
|
||||
alc.parse("test --foo abc") # 错误
|
||||
alc.parse("test abc") # 正确
|
||||
alc.parse("test --foo abc") # 错误
|
||||
alc.parse("test abc") # 正确
|
||||
```
|
||||
|
||||
若需要 `test --foo abc`,你应该使用 `Option`:
|
||||
@@ -383,20 +383,33 @@ alc = Alconna(..., meta=CommandMeta("foo", example="bar"))
|
||||
from arclet.alconna import Alconna, namespace, Namespace, Subcommand, Args, config
|
||||
|
||||
|
||||
ns = Namespace("foo", prefixes=["/"]) # 创建 "foo"命名空间配置, 它要求创建的Alconna的主命令前缀必须是/
|
||||
ns = Namespace(
|
||||
"foo", prefixes=["/"]
|
||||
) # 创建 "foo"命名空间配置, 它要求创建的Alconna的主命令前缀必须是/
|
||||
|
||||
alc = Alconna("pip", Subcommand("install", Args["package", str]), namespace=ns) # 在创建Alconna时候传入命名空间以替换默认命名空间
|
||||
alc = Alconna(
|
||||
"pip", Subcommand("install", Args["package", str]), namespace=ns
|
||||
) # 在创建Alconna时候传入命名空间以替换默认命名空间
|
||||
|
||||
# 可以通过with方式创建命名空间
|
||||
with namespace("bar") as np1:
|
||||
np1.prefixes = ["!"] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
||||
np1.prefixes = [
|
||||
"!"
|
||||
] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
||||
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
|
||||
np1.builtin_option_name["help"] = {"帮助", "-h"} # 设置此命名空间下的命令的帮助选项名称
|
||||
np1.builtin_option_name["help"] = {
|
||||
"帮助",
|
||||
"-h",
|
||||
} # 设置此命名空间下的命令的帮助选项名称
|
||||
|
||||
# 你还可以使用config来管理所有命名空间并切换至任意命名空间
|
||||
config.namespaces["foo"] = ns # 将命名空间挂载到 config 上
|
||||
|
||||
alc = Alconna("pip", Subcommand("install", Args["package", str]), namespace=config.namespaces["foo"]) # 也是同样可以切换到"foo"命名空间
|
||||
alc = Alconna(
|
||||
"pip",
|
||||
Subcommand("install", Args["package", str]),
|
||||
namespace=config.namespaces["foo"],
|
||||
) # 也是同样可以切换到"foo"命名空间
|
||||
```
|
||||
|
||||
### 修改默认的命名空间
|
||||
@@ -466,13 +479,15 @@ alc = Alconna("eval", Args["content", str])
|
||||
alc.shortcut("echo", {"command": "eval print(\\'{*}\\')"})
|
||||
# 'Alconna::eval 的快捷指令: "echo" 添加成功'
|
||||
|
||||
alc.shortcut("echo", delete=True) # 删除快捷指令
|
||||
alc.shortcut("echo", delete=True) # 删除快捷指令
|
||||
# 'Alconna::eval 的快捷指令: "echo" 删除成功'
|
||||
|
||||
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
||||
|
||||
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
||||
def cb(content: str):
|
||||
eval(content, {}, {})
|
||||
|
||||
|
||||
alc.parse('eval print(\\"hello world\\")')
|
||||
# hello world
|
||||
|
||||
@@ -523,7 +538,12 @@ alc.parse("eval --shortcut list")
|
||||
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))
|
||||
alc = Alconna(
|
||||
"test",
|
||||
Args["foo", int],
|
||||
Option("BAR", Args["baz", str], compact=True),
|
||||
meta=CommandMeta(compact=True),
|
||||
)
|
||||
|
||||
assert alc.parse("test123 BARabc").matched
|
||||
```
|
||||
@@ -534,7 +554,9 @@ assert alc.parse("test123 BARabc").matched
|
||||
from arclet.alconna import Alconna, Option, Args, append
|
||||
|
||||
|
||||
alc = Alconna("gcc", Option("--flag|-F", Args["content", str], action=append, compact=True))
|
||||
alc = Alconna(
|
||||
"gcc", Option("--flag|-F", Args["content", str], action=append, compact=True)
|
||||
)
|
||||
print(alc.parse("gcc -Fabc -Fdef -Fxyz").query[list]("flag.content"))
|
||||
# ['abc', 'def', 'xyz']
|
||||
```
|
||||
@@ -577,7 +599,7 @@ from arclet.alconna import Alconna, Args, Option
|
||||
alc = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar")
|
||||
alc.parse("test --comp")
|
||||
|
||||
'''
|
||||
"""
|
||||
output
|
||||
|
||||
以下是建议的输入:
|
||||
@@ -588,7 +610,7 @@ output
|
||||
* --shortcut
|
||||
* foo
|
||||
* bar
|
||||
'''
|
||||
"""
|
||||
```
|
||||
|
||||
## Duplication
|
||||
@@ -600,7 +622,16 @@ output
|
||||
以pip为例,其对应的 Duplication 应如下构造:
|
||||
|
||||
```python
|
||||
from arclet.alconna import Alconna, Args, Option, OptionResult, Duplication, SubcommandStub, Subcommand, count
|
||||
from arclet.alconna import (
|
||||
Alconna,
|
||||
Args,
|
||||
Option,
|
||||
OptionResult,
|
||||
Duplication,
|
||||
SubcommandStub,
|
||||
Subcommand,
|
||||
count,
|
||||
)
|
||||
|
||||
|
||||
class MyDup(Duplication):
|
||||
@@ -620,7 +651,7 @@ alc = Alconna(
|
||||
Option("-v|--verbose", action=count),
|
||||
)
|
||||
|
||||
res = alc.parse("pip -v install ...") # 不使用duplication获得的提示较少
|
||||
res = alc.parse("pip -v install ...") # 不使用duplication获得的提示较少
|
||||
print(res.query("install"))
|
||||
# (value=Ellipsis args={'package': '...'} options={} subcommands={})
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@ 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)
|
||||
|
||||
```
|
||||
|
||||
相比于 `on_alconna`, `funcommand` 增加了三个参数 `name`, `prefixes` 和 `description`。
|
||||
@@ -54,6 +54,7 @@ book = (
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
@book.handle()
|
||||
async def _(arp: Arparma):
|
||||
await book.send(str(arp.options))
|
||||
|
||||
@@ -45,14 +45,11 @@ message = UniMessage(
|
||||
```python
|
||||
from nonebot_plugin_alconna import Button, UniMessage
|
||||
|
||||
message = (
|
||||
UniMessage.text("hello world")
|
||||
.keyboard(
|
||||
Button("link1", url="https://example.com/1"),
|
||||
Button("link2", url="https://example.com/2"),
|
||||
Button("link3", url="https://example.com/3"),
|
||||
row=3,
|
||||
)
|
||||
message = UniMessage.text("hello world").keyboard(
|
||||
Button("link1", url="https://example.com/1"),
|
||||
Button("link2", url="https://example.com/2"),
|
||||
Button("link3", url="https://example.com/3"),
|
||||
row=3,
|
||||
)
|
||||
```
|
||||
|
||||
@@ -94,6 +91,7 @@ async def _():
|
||||
```python
|
||||
from nonebot_plugin_alconna import message_recall, message_edit, message_reaction
|
||||
|
||||
|
||||
@matcher.handle()
|
||||
async def _():
|
||||
await message_edit(UniMessage.text("hello world"))
|
||||
@@ -120,9 +118,9 @@ async def _():
|
||||
```python
|
||||
from nonebot_plugin_alconna import UniMsg
|
||||
|
||||
|
||||
@matcher.handle()
|
||||
async def _(msg: UniMsg):
|
||||
...
|
||||
async def _(msg: UniMsg): ...
|
||||
```
|
||||
|
||||
然后你可以通过 `UniMessage` 的方法来处理消息.
|
||||
@@ -182,6 +180,7 @@ async def _(msg: UniMsg):
|
||||
```python
|
||||
from nonebot_plugin_alconna import UniMsg
|
||||
|
||||
|
||||
@matcher.handle()
|
||||
async def _(msg: UniMsg):
|
||||
data: list[dict] = msg.dump()
|
||||
@@ -193,6 +192,7 @@ async def _(msg: UniMsg):
|
||||
```python
|
||||
from nonebot_plugin_alconna import UniMessage
|
||||
|
||||
|
||||
@matcher.handle()
|
||||
async def _():
|
||||
data = [
|
||||
|
||||
@@ -12,9 +12,9 @@ from nonebot_plugin_alconna import Alconna, Args, Image, on_alconna
|
||||
|
||||
meme = on_alconna(Alconna("make_meme", Args["name", str]["img", Image]))
|
||||
|
||||
|
||||
@meme.handle()
|
||||
async def _(img: Image):
|
||||
...
|
||||
async def _(img: Image): ...
|
||||
```
|
||||
|
||||
## 模型定义
|
||||
@@ -24,6 +24,7 @@ async def _(img: Image):
|
||||
```python
|
||||
class Segment:
|
||||
"""基类标注"""
|
||||
|
||||
@property
|
||||
def type(self) -> str: ...
|
||||
@property
|
||||
@@ -31,29 +32,40 @@ class Segment:
|
||||
@property
|
||||
def children(self) -> list["Segment"]: ...
|
||||
|
||||
|
||||
class Text(Segment):
|
||||
"""Text对象, 表示一类文本元素"""
|
||||
|
||||
text: str
|
||||
styles: dict[tuple[int, int], list[str]]
|
||||
|
||||
def cover(self, text: str): ...
|
||||
def mark(self, start: Optional[int] = None, end: Optional[int] = None, *styles: str): ...
|
||||
def mark(
|
||||
self, start: Optional[int] = None, end: Optional[int] = None, *styles: str
|
||||
): ...
|
||||
|
||||
|
||||
class At(Segment):
|
||||
"""At对象, 表示一类提醒某用户的元素"""
|
||||
|
||||
flag: Literal["user", "role", "channel"]
|
||||
target: str
|
||||
display: Optional[str]
|
||||
|
||||
|
||||
class AtAll(Segment):
|
||||
"""AtAll对象, 表示一类提醒所有人的元素"""
|
||||
|
||||
here: bool
|
||||
|
||||
|
||||
class Emoji(Segment):
|
||||
"""Emoji对象, 表示一类表情元素"""
|
||||
|
||||
id: str
|
||||
name: Optional[str]
|
||||
|
||||
|
||||
class Media(Segment):
|
||||
id: Optional[str]
|
||||
url: Optional[str]
|
||||
@@ -64,53 +76,72 @@ class Media(Segment):
|
||||
|
||||
to_url: ClassVar[Optional[MediaToUrl]]
|
||||
|
||||
|
||||
class Image(Media):
|
||||
"""Image对象, 表示一类图片元素"""
|
||||
|
||||
width: Optional[int]
|
||||
height: Optional[int]
|
||||
|
||||
|
||||
class Audio(Media):
|
||||
"""Audio对象, 表示一类音频元素"""
|
||||
|
||||
duration: Optional[float]
|
||||
|
||||
|
||||
class Voice(Media):
|
||||
"""Voice对象, 表示一类语音元素"""
|
||||
|
||||
duration: Optional[float]
|
||||
|
||||
|
||||
class Video(Media):
|
||||
"""Video对象, 表示一类视频元素"""
|
||||
|
||||
thumbnail: Optional[Image]
|
||||
duration: Optional[float]
|
||||
|
||||
|
||||
class File(Media):
|
||||
"""File对象, 表示一类文件元素"""
|
||||
|
||||
|
||||
class Reply(Segment):
|
||||
"""Reply对象,表示一类回复消息"""
|
||||
|
||||
id: str
|
||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||
msg: Optional[Union[Message, str]]
|
||||
origin: Optional[Any]
|
||||
|
||||
|
||||
class Reference(Segment):
|
||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||
|
||||
id: Optional[str]
|
||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||
children: List[Union[RefNode, CustomNode]]
|
||||
|
||||
|
||||
class Hyper(Segment):
|
||||
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
||||
|
||||
format: Literal["xml", "json"]
|
||||
raw: Optional[str]
|
||||
content: Optional[Union[dict, list]]
|
||||
|
||||
|
||||
class Reference(Segment):
|
||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||
|
||||
id: Optional[str]
|
||||
nodes: Sequence[Union[RefNode, CustomNode]]
|
||||
|
||||
|
||||
class Button(Segment):
|
||||
"""Button对象,表示一类按钮消息"""
|
||||
|
||||
flag: Literal["action", "link", "input", "enter"]
|
||||
"""
|
||||
- 点击 action 类型的按钮时会触发一个关于 按钮回调 事件,该事件的 button 资源会包含上述 id
|
||||
@@ -138,20 +169,26 @@ class Button(Segment):
|
||||
- list[At]: 指定用户/身份组可操作
|
||||
"""
|
||||
|
||||
|
||||
class Keyboard(Segment):
|
||||
"""Keyboard对象,表示一行按钮元素"""
|
||||
|
||||
id: Optional[str]
|
||||
"""此处一般用来表示模板id,特殊情况下可能表示例如 bot_appid 等"""
|
||||
buttons: Optional[list[Button]]
|
||||
row: Optional[int]
|
||||
"""当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数"""
|
||||
|
||||
|
||||
class Other(Segment):
|
||||
"""其他 Segment"""
|
||||
|
||||
origin: MessageSegment
|
||||
|
||||
|
||||
class I18n(Segment):
|
||||
"""特殊的 Segment,用于 i18n 消息"""
|
||||
|
||||
item_or_scope: Union[LangItem, str]
|
||||
type_: Optional[str] = None
|
||||
|
||||
@@ -172,10 +209,14 @@ from nonebot_plugin_alconna import Args, Image, Alconna, select
|
||||
from nonebot_plugin_alconna.builtins.uniseg.market_face import MarketFace
|
||||
|
||||
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 Image 的元素选出来并将第一个作为结果
|
||||
alc1 = Alconna("make_meme", Args["name", str]["img", select(Image).first]) # 也可以使用 select(Image).nth(0)
|
||||
alc1 = Alconna(
|
||||
"make_meme", Args["name", str]["img", select(Image).first]
|
||||
) # 也可以使用 select(Image).nth(0)
|
||||
|
||||
# 表示这个指令需要的图片要么直接是 Image 要么是在 MarketFace 元素内的 Image
|
||||
alc2 = Alconna("make_meme", Args["name", str]["img", [Image, select(Image).from_(MarketFace)]])
|
||||
alc2 = Alconna(
|
||||
"make_meme", Args["name", str]["img", [Image, select(Image).from_(MarketFace)]]
|
||||
)
|
||||
```
|
||||
|
||||
也可以参考通用消息的 [`嵌套提取`](./message.mdx#嵌套提取)
|
||||
@@ -213,10 +254,13 @@ def mfbuild(builder: MessageBuilder, seg: BaseMessageSegment):
|
||||
|
||||
|
||||
@custom_handler(MarketFace)
|
||||
async def mfexport(exporter: MessageExporter, seg: MarketFace, bot: Bot, fallback: bool):
|
||||
async def mfexport(
|
||||
exporter: MessageExporter, seg: MarketFace, bot: Bot, fallback: bool
|
||||
):
|
||||
if exporter.get_message_type() is Message:
|
||||
return MessageSegment("chronocat:marketface", seg.data)(await exporter.export(seg.children, bot, fallback))
|
||||
|
||||
return MessageSegment("chronocat:marketface", seg.data)(
|
||||
await exporter.export(seg.children, bot, fallback)
|
||||
)
|
||||
```
|
||||
|
||||
具体而言,你可以使用 `custom_register` 来增加一个从 MessageSegment 到 Segment 的处理方法;使用 `custom_handler` 来增加一个从 Segment 到 MessageSegment 的处理方法。
|
||||
|
||||
@@ -155,7 +155,9 @@ op.create_table( # CREATE TABLE
|
||||
"weather_weather", # weather_weather
|
||||
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
||||
sa.Column("weather", sa.String(), nullable=False), # weather VARCHAR NOT NULL,
|
||||
sa.PrimaryKeyConstraint("location", name=op.f("pk_weather_weather")), # CONSTRAINT pk_weather_weather PRIMARY KEY (location)
|
||||
sa.PrimaryKeyConstraint(
|
||||
"location", name=op.f("pk_weather_weather")
|
||||
), # CONSTRAINT pk_weather_weather PRIMARY KEY (location)
|
||||
info={"bind_key": "weather"},
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
@@ -245,7 +247,9 @@ from nonebot.typing import T_State
|
||||
|
||||
|
||||
@weather.got("location", prompt="请输入地名")
|
||||
async def _(state: T_State, session: async_scoped_session, location: str = ArgPlainText()):
|
||||
async def _(
|
||||
state: T_State, session: async_scoped_session, location: str = ArgPlainText()
|
||||
):
|
||||
wea = await session.get(Weather, location)
|
||||
|
||||
if not wea:
|
||||
@@ -348,13 +352,16 @@ async def _(
|
||||
```python title=weather/__init__.py {5} showLineNumbers
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def _(
|
||||
weas: Sequence[Weather] = SQLDepends(
|
||||
select(Weather).where(Weather.weather == Depends(extract_arg_plain_text))
|
||||
),
|
||||
):
|
||||
await weather.send(f"今天的天气是{weas[0].weather}的城市有{','.join(wea.location for wea in weas)}")
|
||||
await weather.send(
|
||||
f"今天的天气是{weas[0].weather}的城市有{','.join(wea.location for wea in weas)}"
|
||||
)
|
||||
```
|
||||
|
||||
支持的类型标注请参见 [依赖注入](dependency)。
|
||||
@@ -364,6 +371,7 @@ async def _(
|
||||
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
||||
class Weather(Model):
|
||||
location: Mapped[str] = mapped_column(primary_key=True)
|
||||
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
||||
|
||||
@@ -78,8 +78,7 @@ async def html_to_pic(
|
||||
img_fetch_fn: ImgFetchFn = combined_img_fetcher,
|
||||
css_fetch_fn: CSSFetchFn = combined_css_fetcher,
|
||||
urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin,
|
||||
) -> bytes:
|
||||
...
|
||||
) -> bytes: ...
|
||||
```
|
||||
|
||||
最核心的渲染函数。
|
||||
@@ -107,8 +106,7 @@ async def text_to_pic(
|
||||
allow_refit: bool = True,
|
||||
image_format: Literal["png", "jpeg"] = "png",
|
||||
jpeg_quality: int = 100,
|
||||
) -> bytes:
|
||||
...
|
||||
) -> bytes: ...
|
||||
```
|
||||
|
||||
可用于渲染多行文本。
|
||||
@@ -128,8 +126,7 @@ async def md_to_pic(
|
||||
allow_refit: bool = True,
|
||||
image_format: Literal["png", "jpeg"] = "png",
|
||||
jpeg_quality: int = 100,
|
||||
) -> bytes:
|
||||
...
|
||||
) -> bytes: ...
|
||||
```
|
||||
|
||||
可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。
|
||||
@@ -153,8 +150,7 @@ async def template_to_pic(
|
||||
allow_refit: bool = True,
|
||||
image_format: Literal["png", "jpeg"] = "png",
|
||||
jpeg_quality: int = 100,
|
||||
) -> bytes:
|
||||
...
|
||||
) -> bytes: ...
|
||||
```
|
||||
|
||||
渲染 jinja2 模板。
|
||||
|
||||
@@ -44,15 +44,18 @@ require("nonebot_plugin_apscheduler")
|
||||
|
||||
from nonebot_plugin_apscheduler import scheduler
|
||||
|
||||
|
||||
# 基于装饰器的方式
|
||||
@scheduler.scheduled_job("cron", hour="*/2", id="job_0", args=[1], kwargs={arg2: 2})
|
||||
async def run_every_2_hour(arg1: int, arg2: int):
|
||||
pass
|
||||
|
||||
|
||||
# 基于 add_job 方法的方式
|
||||
def run_every_day(arg1: int, arg2: int):
|
||||
pass
|
||||
|
||||
|
||||
scheduler.add_job(
|
||||
run_every_day, "interval", days=1, id="job_1", args=[1], kwargs={arg2: 2}
|
||||
)
|
||||
|
||||
@@ -25,6 +25,7 @@ NoneBot 中的网络通信主要包括以下几种:
|
||||
```python {5,6} title=tests/test_http_server.py
|
||||
from nonebug import App
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_http_server(app: App):
|
||||
async with app.test_server() as ctx:
|
||||
@@ -45,6 +46,7 @@ import nonebot
|
||||
from nonebug import App
|
||||
from nonebot.adapters.fake import Adapter
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_http_server(app: App):
|
||||
adapter = nonebot.get_adapter(Adapter)
|
||||
@@ -72,6 +74,7 @@ import nonebot
|
||||
from nonebug import App
|
||||
from nonebot.adapters.fake import Adapter
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ws_server(app: App):
|
||||
adapter = nonebot.get_adapter(Adapter)
|
||||
|
||||
Reference in New Issue
Block a user