mirror of
https://github.com/nonebot/nonebot2.git
synced 2026-09-16 22:33:49 +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
@@ -7,7 +7,7 @@ ci:
|
|||||||
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
|
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.16.1
|
rev: v0.16.6
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff-check
|
- id: ruff-check
|
||||||
args: [--fix]
|
args: [--fix]
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ NoneBot 默认使用 Python 的字典将事件响应器存储于内存中,但
|
|||||||
```python
|
```python
|
||||||
from nonebot.matcher import MatcherProvider
|
from nonebot.matcher import MatcherProvider
|
||||||
|
|
||||||
class CustomProvider(MatcherProvider):
|
|
||||||
...
|
class CustomProvider(MatcherProvider): ...
|
||||||
```
|
```
|
||||||
|
|
||||||
## 设置存储提供者
|
## 设置存储提供者
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ NoneBot 兼容层定义了两个数据类 `HTTPServerSetup` 和 `WebSocketServer
|
|||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.drivers import URL, Request, Response, ASGIMixin, HTTPServerSetup
|
from nonebot.drivers import URL, Request, Response, ASGIMixin, HTTPServerSetup
|
||||||
|
|
||||||
|
|
||||||
async def hello(request: Request) -> Response:
|
async def hello(request: Request) -> Response:
|
||||||
return Response(200, content="Hello, world!")
|
return Response(200, content="Hello, world!")
|
||||||
|
|
||||||
|
|
||||||
if isinstance((driver := get_driver()), ASGIMixin):
|
if isinstance((driver := get_driver()), ASGIMixin):
|
||||||
driver.setup_http_server(
|
driver.setup_http_server(
|
||||||
HTTPServerSetup(
|
HTTPServerSetup(
|
||||||
@@ -78,6 +80,7 @@ if isinstance((driver := get_driver()), ASGIMixin):
|
|||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.drivers import URL, ASGIMixin, WebSocket, WebSocketServerSetup
|
from nonebot.drivers import URL, ASGIMixin, WebSocket, WebSocketServerSetup
|
||||||
|
|
||||||
|
|
||||||
async def ws_handler(ws: WebSocket):
|
async def ws_handler(ws: WebSocket):
|
||||||
await ws.accept()
|
await ws.accept()
|
||||||
try:
|
try:
|
||||||
@@ -92,6 +95,7 @@ async def ws_handler(ws: WebSocket):
|
|||||||
await websocket.close()
|
await websocket.close()
|
||||||
# do some cleanup
|
# do some cleanup
|
||||||
|
|
||||||
|
|
||||||
if isinstance((driver := get_driver()), ASGIMixin):
|
if isinstance((driver := get_driver()), ASGIMixin):
|
||||||
driver.setup_websocket_server(
|
driver.setup_websocket_server(
|
||||||
WebSocketServerSetup(
|
WebSocketServerSetup(
|
||||||
@@ -129,6 +133,7 @@ from fastapi import FastAPI
|
|||||||
|
|
||||||
app: FastAPI = nonebot.get_app()
|
app: FastAPI = nonebot.get_app()
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api")
|
@app.get("/api")
|
||||||
async def custom_api():
|
async def custom_api():
|
||||||
return {"message": "Hello, world!"}
|
return {"message": "Hello, world!"}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
async def do_something():
|
async def do_something():
|
||||||
pass
|
pass
|
||||||
@@ -43,6 +44,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_shutdown
|
@driver.on_shutdown
|
||||||
async def do_something():
|
async def do_something():
|
||||||
pass
|
pass
|
||||||
@@ -57,6 +59,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_connect
|
@driver.on_bot_connect
|
||||||
async def do_something(bot: Bot):
|
async def do_something(bot: Bot):
|
||||||
pass
|
pass
|
||||||
@@ -71,6 +74,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_disconnect
|
@driver.on_bot_disconnect
|
||||||
async def do_something(bot: Bot):
|
async def do_something(bot: Bot):
|
||||||
pass
|
pass
|
||||||
@@ -88,6 +92,7 @@ async def do_something(bot: Bot):
|
|||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
from nonebot.message import event_preprocessor
|
from nonebot.message import event_preprocessor
|
||||||
|
|
||||||
|
|
||||||
@event_preprocessor
|
@event_preprocessor
|
||||||
async def do_something(event: Event):
|
async def do_something(event: Event):
|
||||||
if not event.is_tome():
|
if not event.is_tome():
|
||||||
@@ -101,6 +106,7 @@ async def do_something(event: Event):
|
|||||||
```python
|
```python
|
||||||
from nonebot.message import event_postprocessor
|
from nonebot.message import event_postprocessor
|
||||||
|
|
||||||
|
|
||||||
@event_postprocessor
|
@event_postprocessor
|
||||||
async def do_something(event: Event):
|
async def do_something(event: Event):
|
||||||
pass
|
pass
|
||||||
@@ -114,6 +120,7 @@ async def do_something(event: Event):
|
|||||||
from nonebot.message import run_preprocessor
|
from nonebot.message import run_preprocessor
|
||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
|
|
||||||
|
|
||||||
@run_preprocessor
|
@run_preprocessor
|
||||||
async def do_something(event: Event, matcher: Matcher):
|
async def do_something(event: Event, matcher: Matcher):
|
||||||
if not event.is_tome():
|
if not event.is_tome():
|
||||||
@@ -127,6 +134,7 @@ async def do_something(event: Event, matcher: Matcher):
|
|||||||
```python
|
```python
|
||||||
from nonebot.message import run_postprocessor
|
from nonebot.message import run_postprocessor
|
||||||
|
|
||||||
|
|
||||||
@run_postprocessor
|
@run_postprocessor
|
||||||
async def do_something(event: Event, matcher: Matcher, exception: Optional[Exception]):
|
async def do_something(event: Event, matcher: Matcher, exception: Optional[Exception]):
|
||||||
pass
|
pass
|
||||||
@@ -140,6 +148,7 @@ async def do_something(event: Event, matcher: Matcher, exception: Optional[Excep
|
|||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
from nonebot.exception import MockApiException
|
from nonebot.exception import MockApiException
|
||||||
|
|
||||||
|
|
||||||
@Bot.on_calling_api
|
@Bot.on_calling_api
|
||||||
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
||||||
if api == "send_msg":
|
if api == "send_msg":
|
||||||
@@ -154,9 +163,14 @@ async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
|||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
from nonebot.exception import MockApiException
|
from nonebot.exception import MockApiException
|
||||||
|
|
||||||
|
|
||||||
@Bot.on_called_api
|
@Bot.on_called_api
|
||||||
async def handle_api_result(
|
async def handle_api_result(
|
||||||
bot: Bot, exception: Optional[Exception], api: str, data: Dict[str, Any], result: Any
|
bot: Bot,
|
||||||
|
exception: Optional[Exception],
|
||||||
|
api: str,
|
||||||
|
data: Dict[str, Any],
|
||||||
|
result: Any,
|
||||||
):
|
):
|
||||||
if not exception and api == "send_msg":
|
if not exception and api == "send_msg":
|
||||||
raise MockApiException(result={**result, "message_id": 123})
|
raise MockApiException(result={**result, "message_id": 123})
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ options:
|
|||||||
```python {3-5}
|
```python {3-5}
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.type_updater
|
@foo.type_updater
|
||||||
async def _() -> str:
|
async def _() -> str:
|
||||||
return "notice"
|
return "notice"
|
||||||
@@ -37,6 +38,7 @@ from nonebot.permission import User
|
|||||||
|
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.permission_updater
|
@foo.permission_updater
|
||||||
async def _(event: Event, matcher: Matcher) -> Permission:
|
async def _(event: Event, matcher: Matcher) -> Permission:
|
||||||
return Permission(User.from_event(event, perm=matcher.permission))
|
return Permission(User.from_event(event, perm=matcher.permission))
|
||||||
@@ -49,6 +51,7 @@ from nonebot.permission import USER
|
|||||||
|
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.permission_updater
|
@foo.permission_updater
|
||||||
async def _(matcher: Matcher) -> Permission:
|
async def _(matcher: Matcher) -> Permission:
|
||||||
return USER("session1", "session2", perm=matcher.permission)
|
return USER("session1", "session2", perm=matcher.permission)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ logger.add(
|
|||||||
level=0,
|
level=0,
|
||||||
diagnose=True,
|
diagnose=True,
|
||||||
format="<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}",
|
format="<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}",
|
||||||
filter=default_filter
|
filter=default_filter,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ options:
|
|||||||
```python {4} title=weather/__init__.py
|
```python {4} title=weather/__init__.py
|
||||||
from nonebot.adapters.console import MessageEvent
|
from nonebot.adapters.console import MessageEvent
|
||||||
|
|
||||||
|
|
||||||
@weather.got("location", prompt="请输入地名")
|
@weather.got("location", prompt="请输入地名")
|
||||||
async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
||||||
await weather.finish(f"{event.time.strftime('%Y-%m-%d')} {location} 的天气是...")
|
await weather.finish(f"{event.time.strftime('%Y-%m-%d')} {location} 的天气是...")
|
||||||
@@ -39,10 +40,12 @@ async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
|||||||
```python {4,8}
|
```python {4,8}
|
||||||
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
|
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_private(event: PrivateMessageEvent):
|
async def handle_private(event: PrivateMessageEvent):
|
||||||
await matcher.finish("私聊消息")
|
await matcher.finish("私聊消息")
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_group(event: GroupMessageEvent):
|
async def handle_group(event: GroupMessageEvent):
|
||||||
await matcher.finish("群聊消息")
|
await matcher.finish("群聊消息")
|
||||||
@@ -54,10 +57,12 @@ async def handle_group(event: GroupMessageEvent):
|
|||||||
from nonebot.adapters.console import Bot as ConsoleBot
|
from nonebot.adapters.console import Bot as ConsoleBot
|
||||||
from nonebot.adapters.onebot.v11 import Bot as OneBot
|
from nonebot.adapters.onebot.v11 import Bot as OneBot
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_console(bot: ConsoleBot):
|
async def handle_console(bot: ConsoleBot):
|
||||||
await bot.bell()
|
await bot.bell()
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_onebot(bot: OneBot):
|
async def handle_onebot(bot: OneBot):
|
||||||
await bot.send_group_message(group_id=123123, message="OneBot")
|
await bot.send_group_message(group_id=123123, message="OneBot")
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ from .config import Config
|
|||||||
|
|
||||||
plugin_config = get_plugin_config(Config)
|
plugin_config = get_plugin_config(Config)
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
weather = on_command("天气", rule=is_enable)
|
weather = on_command("天气", rule=is_enable)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -43,12 +45,15 @@ weather = on_command("天气", rule=is_enable)
|
|||||||
from nonebot.rule import Rule
|
from nonebot.rule import Rule
|
||||||
from nonebot.adapters import Event
|
from nonebot.adapters import Event
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
async def is_blacklisted(event: Event) -> bool:
|
async def is_blacklisted(event: Event) -> bool:
|
||||||
return event.get_user_id() not in BLACKLIST
|
return event.get_user_id() not in BLACKLIST
|
||||||
|
|
||||||
|
|
||||||
rule = Rule(is_enable, is_blacklisted)
|
rule = Rule(is_enable, is_blacklisted)
|
||||||
|
|
||||||
weather = on_command("天气", rule=rule)
|
weather = on_command("天气", rule=rule)
|
||||||
@@ -66,9 +71,11 @@ from .config import Config
|
|||||||
|
|
||||||
plugin_config = get_plugin_config(Config)
|
plugin_config = get_plugin_config(Config)
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
weather = on_command(
|
weather = on_command(
|
||||||
"天气",
|
"天气",
|
||||||
rule=to_me() & is_enable,
|
rule=to_me() & is_enable,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ NoneBot 中的会话状态是一个字典,可以通过类型 `T_State` 来获
|
|||||||
```python
|
```python
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
|
|
||||||
|
|
||||||
@matcher.got("key", prompt="请输入密码")
|
@matcher.got("key", prompt="请输入密码")
|
||||||
async def _(state: T_State, key: str = ArgPlainText()):
|
async def _(state: T_State, key: str = ArgPlainText()):
|
||||||
if key != "some password":
|
if key != "some password":
|
||||||
@@ -34,10 +35,12 @@ async def _(state: T_State, key: str = ArgPlainText()):
|
|||||||
```python
|
```python
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
state["key"] = "value"
|
state["key"] = "value"
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
await matcher.finish(state["key"])
|
await matcher.finish(state["key"])
|
||||||
@@ -49,10 +52,12 @@ async def _(state: T_State):
|
|||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
from nonebot.adapters import MessageTemplate
|
from nonebot.adapters import MessageTemplate
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
state["username"] = "user"
|
state["username"] = "user"
|
||||||
|
|
||||||
|
|
||||||
@matcher.got("password", prompt=MessageTemplate("请输入 {username} 的密码"))
|
@matcher.got("password", prompt=MessageTemplate("请输入 {username} 的密码"))
|
||||||
async def _():
|
async def _():
|
||||||
await matcher.finish(MessageTemplate("密码为 {password}"))
|
await matcher.finish(MessageTemplate("密码为 {password}"))
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ alc = Alconna(
|
|||||||
Args["package", str],
|
Args["package", str],
|
||||||
Option("-r|--requirement", Args["file", str]),
|
Option("-r|--requirement", Args["file", str]),
|
||||||
Option("-i|--index-url", Args["url", str]),
|
Option("-i|--index-url", Args["url", str]),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
res = alc.parse("pip install nonebot2 -i URL")
|
res = alc.parse("pip install nonebot2 -i URL")
|
||||||
@@ -383,20 +383,33 @@ alc = Alconna(..., meta=CommandMeta("foo", example="bar"))
|
|||||||
from arclet.alconna import Alconna, namespace, Namespace, Subcommand, Args, config
|
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方式创建命名空间
|
||||||
with namespace("bar") as np1:
|
with namespace("bar") as np1:
|
||||||
np1.prefixes = ["!"] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
np1.prefixes = [
|
||||||
|
"!"
|
||||||
|
] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
||||||
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
|
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
|
||||||
np1.builtin_option_name["help"] = {"帮助", "-h"} # 设置此命名空间下的命令的帮助选项名称
|
np1.builtin_option_name["help"] = {
|
||||||
|
"帮助",
|
||||||
|
"-h",
|
||||||
|
} # 设置此命名空间下的命令的帮助选项名称
|
||||||
|
|
||||||
# 你还可以使用config来管理所有命名空间并切换至任意命名空间
|
# 你还可以使用config来管理所有命名空间并切换至任意命名空间
|
||||||
config.namespaces["foo"] = ns # 将命名空间挂载到 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"命名空间
|
||||||
```
|
```
|
||||||
|
|
||||||
### 修改默认的命名空间
|
### 修改默认的命名空间
|
||||||
@@ -469,10 +482,12 @@ alc.shortcut("echo", {"command": "eval print(\\'{*}\\')"})
|
|||||||
alc.shortcut("echo", delete=True) # 删除快捷指令
|
alc.shortcut("echo", delete=True) # 删除快捷指令
|
||||||
# 'Alconna::eval 的快捷指令: "echo" 删除成功'
|
# 'Alconna::eval 的快捷指令: "echo" 删除成功'
|
||||||
|
|
||||||
|
|
||||||
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
||||||
def cb(content: str):
|
def cb(content: str):
|
||||||
eval(content, {}, {})
|
eval(content, {}, {})
|
||||||
|
|
||||||
|
|
||||||
alc.parse('eval print(\\"hello world\\")')
|
alc.parse('eval print(\\"hello world\\")')
|
||||||
# hello world
|
# hello world
|
||||||
|
|
||||||
@@ -523,7 +538,12 @@ alc.parse("eval --shortcut list")
|
|||||||
from arclet.alconna import Alconna, Option, CommandMeta, Args
|
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
|
assert alc.parse("test123 BARabc").matched
|
||||||
```
|
```
|
||||||
@@ -534,7 +554,9 @@ assert alc.parse("test123 BARabc").matched
|
|||||||
from arclet.alconna import Alconna, Option, Args, append
|
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"))
|
print(alc.parse("gcc -Fabc -Fdef -Fxyz").query[list]("flag.content"))
|
||||||
# ['abc', 'def', 'xyz']
|
# ['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 = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar")
|
||||||
alc.parse("test --comp")
|
alc.parse("test --comp")
|
||||||
|
|
||||||
'''
|
"""
|
||||||
output
|
output
|
||||||
|
|
||||||
以下是建议的输入:
|
以下是建议的输入:
|
||||||
@@ -588,7 +610,7 @@ output
|
|||||||
* --shortcut
|
* --shortcut
|
||||||
* foo
|
* foo
|
||||||
* bar
|
* bar
|
||||||
'''
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
## Duplication
|
## Duplication
|
||||||
@@ -600,7 +622,16 @@ output
|
|||||||
以pip为例,其对应的 Duplication 应如下构造:
|
以pip为例,其对应的 Duplication 应如下构造:
|
||||||
|
|
||||||
```python
|
```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):
|
class MyDup(Duplication):
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match
|
|||||||
|
|
||||||
echo = on_alconna(Alconna("echo", Args["msg", str]))
|
echo = on_alconna(Alconna("echo", Args["msg", str]))
|
||||||
|
|
||||||
|
|
||||||
@echo.handle()
|
@echo.handle()
|
||||||
async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
|
async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
|
||||||
await echo.finish(msg.result)
|
await echo.finish(msg.result)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
相比于 `on_alconna`, `funcommand` 增加了三个参数 `name`, `prefixes` 和 `description`。
|
相比于 `on_alconna`, `funcommand` 增加了三个参数 `name`, `prefixes` 和 `description`。
|
||||||
@@ -54,6 +54,7 @@ book = (
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@book.handle()
|
@book.handle()
|
||||||
async def _(arp: Arparma):
|
async def _(arp: Arparma):
|
||||||
await book.send(str(arp.options))
|
await book.send(str(arp.options))
|
||||||
|
|||||||
@@ -45,15 +45,12 @@ message = UniMessage(
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import Button, UniMessage
|
from nonebot_plugin_alconna import Button, UniMessage
|
||||||
|
|
||||||
message = (
|
message = UniMessage.text("hello world").keyboard(
|
||||||
UniMessage.text("hello world")
|
|
||||||
.keyboard(
|
|
||||||
Button("link1", url="https://example.com/1"),
|
Button("link1", url="https://example.com/1"),
|
||||||
Button("link2", url="https://example.com/2"),
|
Button("link2", url="https://example.com/2"),
|
||||||
Button("link3", url="https://example.com/3"),
|
Button("link3", url="https://example.com/3"),
|
||||||
row=3,
|
row=3,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 发送
|
### 发送
|
||||||
@@ -94,6 +91,7 @@ async def _():
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import message_recall, message_edit, message_reaction
|
from nonebot_plugin_alconna import message_recall, message_edit, message_reaction
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _():
|
async def _():
|
||||||
await message_edit(UniMessage.text("hello world"))
|
await message_edit(UniMessage.text("hello world"))
|
||||||
@@ -120,9 +118,9 @@ async def _():
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(msg: UniMsg):
|
async def _(msg: UniMsg): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
然后你可以通过 `UniMessage` 的方法来处理消息.
|
然后你可以通过 `UniMessage` 的方法来处理消息.
|
||||||
@@ -182,6 +180,7 @@ async def _(msg: UniMsg):
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(msg: UniMsg):
|
async def _(msg: UniMsg):
|
||||||
data: list[dict] = msg.dump()
|
data: list[dict] = msg.dump()
|
||||||
@@ -193,6 +192,7 @@ async def _(msg: UniMsg):
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMessage
|
from nonebot_plugin_alconna import UniMessage
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _():
|
async def _():
|
||||||
data = [
|
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 = on_alconna(Alconna("make_meme", Args["name", str]["img", Image]))
|
||||||
|
|
||||||
|
|
||||||
@meme.handle()
|
@meme.handle()
|
||||||
async def _(img: Image):
|
async def _(img: Image): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 模型定义
|
## 模型定义
|
||||||
@@ -24,6 +24,7 @@ async def _(img: Image):
|
|||||||
```python
|
```python
|
||||||
class Segment:
|
class Segment:
|
||||||
"""基类标注"""
|
"""基类标注"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> str: ...
|
def type(self) -> str: ...
|
||||||
@property
|
@property
|
||||||
@@ -31,29 +32,40 @@ class Segment:
|
|||||||
@property
|
@property
|
||||||
def children(self) -> list["Segment"]: ...
|
def children(self) -> list["Segment"]: ...
|
||||||
|
|
||||||
|
|
||||||
class Text(Segment):
|
class Text(Segment):
|
||||||
"""Text对象, 表示一类文本元素"""
|
"""Text对象, 表示一类文本元素"""
|
||||||
|
|
||||||
text: str
|
text: str
|
||||||
styles: dict[tuple[int, int], list[str]]
|
styles: dict[tuple[int, int], list[str]]
|
||||||
|
|
||||||
def cover(self, text: 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):
|
class At(Segment):
|
||||||
"""At对象, 表示一类提醒某用户的元素"""
|
"""At对象, 表示一类提醒某用户的元素"""
|
||||||
|
|
||||||
flag: Literal["user", "role", "channel"]
|
flag: Literal["user", "role", "channel"]
|
||||||
target: str
|
target: str
|
||||||
display: Optional[str]
|
display: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class AtAll(Segment):
|
class AtAll(Segment):
|
||||||
"""AtAll对象, 表示一类提醒所有人的元素"""
|
"""AtAll对象, 表示一类提醒所有人的元素"""
|
||||||
|
|
||||||
here: bool
|
here: bool
|
||||||
|
|
||||||
|
|
||||||
class Emoji(Segment):
|
class Emoji(Segment):
|
||||||
"""Emoji对象, 表示一类表情元素"""
|
"""Emoji对象, 表示一类表情元素"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
name: Optional[str]
|
name: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class Media(Segment):
|
class Media(Segment):
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
url: Optional[str]
|
url: Optional[str]
|
||||||
@@ -64,53 +76,72 @@ class Media(Segment):
|
|||||||
|
|
||||||
to_url: ClassVar[Optional[MediaToUrl]]
|
to_url: ClassVar[Optional[MediaToUrl]]
|
||||||
|
|
||||||
|
|
||||||
class Image(Media):
|
class Image(Media):
|
||||||
"""Image对象, 表示一类图片元素"""
|
"""Image对象, 表示一类图片元素"""
|
||||||
|
|
||||||
width: Optional[int]
|
width: Optional[int]
|
||||||
height: Optional[int]
|
height: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
class Audio(Media):
|
class Audio(Media):
|
||||||
"""Audio对象, 表示一类音频元素"""
|
"""Audio对象, 表示一类音频元素"""
|
||||||
|
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class Voice(Media):
|
class Voice(Media):
|
||||||
"""Voice对象, 表示一类语音元素"""
|
"""Voice对象, 表示一类语音元素"""
|
||||||
|
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class Video(Media):
|
class Video(Media):
|
||||||
"""Video对象, 表示一类视频元素"""
|
"""Video对象, 表示一类视频元素"""
|
||||||
|
|
||||||
thumbnail: Optional[Image]
|
thumbnail: Optional[Image]
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class File(Media):
|
class File(Media):
|
||||||
"""File对象, 表示一类文件元素"""
|
"""File对象, 表示一类文件元素"""
|
||||||
|
|
||||||
|
|
||||||
class Reply(Segment):
|
class Reply(Segment):
|
||||||
"""Reply对象,表示一类回复消息"""
|
"""Reply对象,表示一类回复消息"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||||
msg: Optional[Union[Message, str]]
|
msg: Optional[Union[Message, str]]
|
||||||
origin: Optional[Any]
|
origin: Optional[Any]
|
||||||
|
|
||||||
|
|
||||||
class Reference(Segment):
|
class Reference(Segment):
|
||||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||||
children: List[Union[RefNode, CustomNode]]
|
children: List[Union[RefNode, CustomNode]]
|
||||||
|
|
||||||
|
|
||||||
class Hyper(Segment):
|
class Hyper(Segment):
|
||||||
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
||||||
|
|
||||||
format: Literal["xml", "json"]
|
format: Literal["xml", "json"]
|
||||||
raw: Optional[str]
|
raw: Optional[str]
|
||||||
content: Optional[Union[dict, list]]
|
content: Optional[Union[dict, list]]
|
||||||
|
|
||||||
|
|
||||||
class Reference(Segment):
|
class Reference(Segment):
|
||||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
nodes: Sequence[Union[RefNode, CustomNode]]
|
nodes: Sequence[Union[RefNode, CustomNode]]
|
||||||
|
|
||||||
|
|
||||||
class Button(Segment):
|
class Button(Segment):
|
||||||
"""Button对象,表示一类按钮消息"""
|
"""Button对象,表示一类按钮消息"""
|
||||||
|
|
||||||
flag: Literal["action", "link", "input", "enter"]
|
flag: Literal["action", "link", "input", "enter"]
|
||||||
"""
|
"""
|
||||||
- 点击 action 类型的按钮时会触发一个关于 按钮回调 事件,该事件的 button 资源会包含上述 id
|
- 点击 action 类型的按钮时会触发一个关于 按钮回调 事件,该事件的 button 资源会包含上述 id
|
||||||
@@ -138,20 +169,26 @@ class Button(Segment):
|
|||||||
- list[At]: 指定用户/身份组可操作
|
- list[At]: 指定用户/身份组可操作
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Keyboard(Segment):
|
class Keyboard(Segment):
|
||||||
"""Keyboard对象,表示一行按钮元素"""
|
"""Keyboard对象,表示一行按钮元素"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
"""此处一般用来表示模板id,特殊情况下可能表示例如 bot_appid 等"""
|
"""此处一般用来表示模板id,特殊情况下可能表示例如 bot_appid 等"""
|
||||||
buttons: Optional[list[Button]]
|
buttons: Optional[list[Button]]
|
||||||
row: Optional[int]
|
row: Optional[int]
|
||||||
"""当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数"""
|
"""当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数"""
|
||||||
|
|
||||||
|
|
||||||
class Other(Segment):
|
class Other(Segment):
|
||||||
"""其他 Segment"""
|
"""其他 Segment"""
|
||||||
|
|
||||||
origin: MessageSegment
|
origin: MessageSegment
|
||||||
|
|
||||||
|
|
||||||
class I18n(Segment):
|
class I18n(Segment):
|
||||||
"""特殊的 Segment,用于 i18n 消息"""
|
"""特殊的 Segment,用于 i18n 消息"""
|
||||||
|
|
||||||
item_or_scope: Union[LangItem, str]
|
item_or_scope: Union[LangItem, str]
|
||||||
type_: Optional[str] = None
|
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
|
from nonebot_plugin_alconna.builtins.uniseg.market_face import MarketFace
|
||||||
|
|
||||||
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 Image 的元素选出来并将第一个作为结果
|
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 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
|
# 表示这个指令需要的图片要么直接是 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#嵌套提取)
|
也可以参考通用消息的 [`嵌套提取`](./message.mdx#嵌套提取)
|
||||||
@@ -213,10 +254,13 @@ def mfbuild(builder: MessageBuilder, seg: BaseMessageSegment):
|
|||||||
|
|
||||||
|
|
||||||
@custom_handler(MarketFace)
|
@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:
|
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 的处理方法。
|
具体而言,你可以使用 `custom_register` 来增加一个从 MessageSegment 到 Segment 的处理方法;使用 `custom_handler` 来增加一个从 Segment 到 MessageSegment 的处理方法。
|
||||||
|
|||||||
@@ -155,7 +155,9 @@ op.create_table( # CREATE TABLE
|
|||||||
"weather_weather", # weather_weather
|
"weather_weather", # weather_weather
|
||||||
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
||||||
sa.Column("weather", sa.String(), nullable=False), # weather 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"},
|
info={"bind_key": "weather"},
|
||||||
)
|
)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
@@ -245,7 +247,9 @@ from nonebot.typing import T_State
|
|||||||
|
|
||||||
|
|
||||||
@weather.got("location", prompt="请输入地名")
|
@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)
|
wea = await session.get(Weather, location)
|
||||||
|
|
||||||
if not wea:
|
if not wea:
|
||||||
@@ -348,13 +352,16 @@ async def _(
|
|||||||
```python title=weather/__init__.py {5} showLineNumbers
|
```python title=weather/__init__.py {5} showLineNumbers
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
@weather.handle()
|
@weather.handle()
|
||||||
async def _(
|
async def _(
|
||||||
weas: Sequence[Weather] = SQLDepends(
|
weas: Sequence[Weather] = SQLDepends(
|
||||||
select(Weather).where(Weather.weather == Depends(extract_arg_plain_text))
|
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)。
|
支持的类型标注请参见 [依赖注入](dependency)。
|
||||||
@@ -364,6 +371,7 @@ async def _(
|
|||||||
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
class Weather(Model):
|
class Weather(Model):
|
||||||
location: Mapped[str] = mapped_column(primary_key=True)
|
location: Mapped[str] = mapped_column(primary_key=True)
|
||||||
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
||||||
|
|||||||
@@ -78,8 +78,7 @@ async def html_to_pic(
|
|||||||
img_fetch_fn: ImgFetchFn = combined_img_fetcher,
|
img_fetch_fn: ImgFetchFn = combined_img_fetcher,
|
||||||
css_fetch_fn: CSSFetchFn = combined_css_fetcher,
|
css_fetch_fn: CSSFetchFn = combined_css_fetcher,
|
||||||
urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin,
|
urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
最核心的渲染函数。
|
最核心的渲染函数。
|
||||||
@@ -107,8 +106,7 @@ async def text_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可用于渲染多行文本。
|
可用于渲染多行文本。
|
||||||
@@ -128,8 +126,7 @@ async def md_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。
|
可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。
|
||||||
@@ -153,8 +150,7 @@ async def template_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
渲染 jinja2 模板。
|
渲染 jinja2 模板。
|
||||||
|
|||||||
@@ -44,15 +44,18 @@ require("nonebot_plugin_apscheduler")
|
|||||||
|
|
||||||
from nonebot_plugin_apscheduler import scheduler
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
|
||||||
|
|
||||||
# 基于装饰器的方式
|
# 基于装饰器的方式
|
||||||
@scheduler.scheduled_job("cron", hour="*/2", id="job_0", args=[1], kwargs={arg2: 2})
|
@scheduler.scheduled_job("cron", hour="*/2", id="job_0", args=[1], kwargs={arg2: 2})
|
||||||
async def run_every_2_hour(arg1: int, arg2: int):
|
async def run_every_2_hour(arg1: int, arg2: int):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# 基于 add_job 方法的方式
|
# 基于 add_job 方法的方式
|
||||||
def run_every_day(arg1: int, arg2: int):
|
def run_every_day(arg1: int, arg2: int):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
run_every_day, "interval", days=1, id="job_1", args=[1], kwargs={arg2: 2}
|
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
|
```python {5,6} title=tests/test_http_server.py
|
||||||
from nonebug import App
|
from nonebug import App
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_http_server(app: App):
|
async def test_http_server(app: App):
|
||||||
async with app.test_server() as ctx:
|
async with app.test_server() as ctx:
|
||||||
@@ -45,6 +46,7 @@ import nonebot
|
|||||||
from nonebug import App
|
from nonebug import App
|
||||||
from nonebot.adapters.fake import Adapter
|
from nonebot.adapters.fake import Adapter
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_http_server(app: App):
|
async def test_http_server(app: App):
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
@@ -72,6 +74,7 @@ import nonebot
|
|||||||
from nonebug import App
|
from nonebug import App
|
||||||
from nonebot.adapters.fake import Adapter
|
from nonebot.adapters.fake import Adapter
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_ws_server(app: App):
|
async def test_ws_server(app: App):
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ except Exception as e:
|
|||||||
```python title=config.py
|
```python title=config.py
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseModel):
|
class Config(BaseModel):
|
||||||
xxx_id: str
|
xxx_id: str
|
||||||
xxx_token: str
|
xxx_token: str
|
||||||
@@ -102,6 +103,7 @@ from nonebot.adapters import Adapter as BaseAdapter
|
|||||||
|
|
||||||
from .config import Config
|
from .config import Config
|
||||||
|
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
def __init__(self, driver: Driver, **kwargs: Any):
|
def __init__(self, driver: Driver, **kwargs: Any):
|
||||||
@@ -208,9 +210,10 @@ from nonebot.drivers import (
|
|||||||
ASGIMixin,
|
ASGIMixin,
|
||||||
WebSocket,
|
WebSocket,
|
||||||
HTTPServerSetup,
|
HTTPServerSetup,
|
||||||
WebSocketServerSetup
|
WebSocketServerSetup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
def __init__(self, driver: Driver, **kwargs: Any):
|
def __init__(self, driver: Driver, **kwargs: Any):
|
||||||
@@ -242,7 +245,6 @@ class Adapter(BaseAdapter):
|
|||||||
)
|
)
|
||||||
self.setup_websocket_server(ws_setup)
|
self.setup_websocket_server(ws_setup)
|
||||||
|
|
||||||
|
|
||||||
async def _handle_http(self, request: Request) -> Response:
|
async def _handle_http(self, request: Request) -> Response:
|
||||||
"""HTTP 路由处理函数,只有一个类型为 Request 的参数,且返回值类型为 Response"""
|
"""HTTP 路由处理函数,只有一个类型为 Request 的参数,且返回值类型为 Response"""
|
||||||
...
|
...
|
||||||
@@ -270,8 +272,8 @@ class Adapter(BaseAdapter):
|
|||||||
```python {7,8,11} title=adapter.py
|
```python {7,8,11} title=adapter.py
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
def _handle_connect(self):
|
def _handle_connect(self):
|
||||||
bot_id = ... # 通过配置或者平台 API 等方式,获取到 Bot 的 ID
|
bot_id = ... # 通过配置或者平台 API 等方式,获取到 Bot 的 ID
|
||||||
bot = Bot(self, self_id=bot_id) # 实例化 Bot
|
bot = Bot(self, self_id=bot_id) # 实例化 Bot
|
||||||
@@ -295,8 +297,8 @@ from .bot import Bot
|
|||||||
from .event import Event
|
from .event import Event
|
||||||
from .log import log
|
from .log import log
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
@classmethod
|
@classmethod
|
||||||
def payload_to_event(cls, payload: Dict[str, Any]) -> Event:
|
def payload_to_event(cls, payload: Dict[str, Any]) -> Event:
|
||||||
"""根据平台事件的特性,转换平台 payload 为具体 Event
|
"""根据平台事件的特性,转换平台 payload 为具体 Event
|
||||||
@@ -316,7 +318,6 @@ class Adapter(BaseAdapter):
|
|||||||
# 也可以尝试转为基础 Event 进行处理
|
# 也可以尝试转为基础 Event 进行处理
|
||||||
return type_validate_python(Event, payload)
|
return type_validate_python(Event, payload)
|
||||||
|
|
||||||
|
|
||||||
async def _forward(self, bot: Bot):
|
async def _forward(self, bot: Bot):
|
||||||
|
|
||||||
payload: Dict[str, Any] # 接收到的事件数据
|
payload: Dict[str, Any] # 接收到的事件数据
|
||||||
@@ -337,8 +338,8 @@ from nonebot.drivers import Request, WebSocket
|
|||||||
|
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
|
async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
|
||||||
log("DEBUG", f"Calling API <y>{api}</y>") # 给予日志提示
|
log("DEBUG", f"Calling API <y>{api}</y>") # 给予日志提示
|
||||||
@@ -356,7 +357,6 @@ class Adapter(BaseAdapter):
|
|||||||
# 发送请求,返回结果
|
# 发送请求,返回结果
|
||||||
return await self.driver.request(request)
|
return await self.driver.request(request)
|
||||||
|
|
||||||
|
|
||||||
# 采用 WebSocket 通信的方式,可以直接调用 send 方法发送数据
|
# 采用 WebSocket 通信的方式,可以直接调用 send 方法发送数据
|
||||||
# 通过某种方式获取到 bot 对应的 websocket 对象
|
# 通过某种方式获取到 bot 对应的 websocket 对象
|
||||||
ws: WebSocket = your_get_websocket_method(bot.self_id)
|
ws: WebSocket = your_get_websocket_method(bot.self_id)
|
||||||
@@ -450,8 +450,8 @@ from typing_extensions import override
|
|||||||
from nonebot.compat import model_dump
|
from nonebot.compat import model_dump
|
||||||
from nonebot.adapters import Event as BaseEvent
|
from nonebot.adapters import Event as BaseEvent
|
||||||
|
|
||||||
class Event(BaseEvent):
|
|
||||||
|
|
||||||
|
class Event(BaseEvent):
|
||||||
@override
|
@override
|
||||||
def get_event_name(self) -> str:
|
def get_event_name(self) -> str:
|
||||||
# 返回事件的名称,用于日志打印
|
# 返回事件的名称,用于日志打印
|
||||||
@@ -488,6 +488,7 @@ class Event(BaseEvent):
|
|||||||
```python {7,16,20,25,34,42} title=event.py
|
```python {7,16,20,25,34,42} title=event.py
|
||||||
from .message import Message
|
from .message import Message
|
||||||
|
|
||||||
|
|
||||||
class HeartbeatEvent(Event):
|
class HeartbeatEvent(Event):
|
||||||
"""心跳时间,通常为元事件"""
|
"""心跳时间,通常为元事件"""
|
||||||
|
|
||||||
@@ -495,8 +496,10 @@ class HeartbeatEvent(Event):
|
|||||||
def get_type(self) -> str:
|
def get_type(self) -> str:
|
||||||
return "meta_event"
|
return "meta_event"
|
||||||
|
|
||||||
|
|
||||||
class MessageEvent(Event):
|
class MessageEvent(Event):
|
||||||
"""消息事件"""
|
"""消息事件"""
|
||||||
|
|
||||||
message_id: str
|
message_id: str
|
||||||
user_id: str
|
user_id: str
|
||||||
|
|
||||||
@@ -513,8 +516,10 @@ class MessageEvent(Event):
|
|||||||
def get_user_id(self) -> str:
|
def get_user_id(self) -> str:
|
||||||
return self.user_id
|
return self.user_id
|
||||||
|
|
||||||
|
|
||||||
class JoinRoomEvent(Event):
|
class JoinRoomEvent(Event):
|
||||||
"""加入房间事件,通常为通知事件"""
|
"""加入房间事件,通常为通知事件"""
|
||||||
|
|
||||||
user_id: str
|
user_id: str
|
||||||
room_id: str
|
room_id: str
|
||||||
|
|
||||||
@@ -522,8 +527,10 @@ class JoinRoomEvent(Event):
|
|||||||
def get_type(self) -> str:
|
def get_type(self) -> str:
|
||||||
return "notice"
|
return "notice"
|
||||||
|
|
||||||
|
|
||||||
class ApplyAddFriendEvent(Event):
|
class ApplyAddFriendEvent(Event):
|
||||||
"""申请添加好友事件,通常为请求事件"""
|
"""申请添加好友事件,通常为请求事件"""
|
||||||
|
|
||||||
user_id: str
|
user_id: str
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -544,6 +551,7 @@ from nonebot.utils import escape_tag
|
|||||||
from nonebot.adapters import Message as BaseMessage
|
from nonebot.adapters import Message as BaseMessage
|
||||||
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
||||||
|
|
||||||
|
|
||||||
class MessageSegment(BaseMessageSegment["Message"]):
|
class MessageSegment(BaseMessageSegment["Message"]):
|
||||||
@classmethod
|
@classmethod
|
||||||
@override
|
@override
|
||||||
@@ -591,6 +599,7 @@ class Message(BaseMessage[MessageSegment]):
|
|||||||
```python title=tests/conftest.py
|
```python title=tests/conftest.py
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import nonebot.adapters
|
import nonebot.adapters
|
||||||
|
|
||||||
nonebot.adapters.__path__.append( # type: ignore
|
nonebot.adapters.__path__.append( # type: ignore
|
||||||
str((Path(__file__).parent.parent / "nonebot" / "adapters").resolve())
|
str((Path(__file__).parent.parent / "nonebot" / "adapters").resolve())
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ weather = on_command("天气")
|
|||||||
from nonebot import on_command
|
from nonebot import on_command
|
||||||
from nonebot.rule import to_me
|
from nonebot.rule import to_me
|
||||||
|
|
||||||
weather = on_command("天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True)
|
weather = on_command(
|
||||||
|
"天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
|
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
|
||||||
|
|||||||
@@ -174,9 +174,7 @@ message = Message(
|
|||||||
# 索引
|
# 索引
|
||||||
message[0] == MessageSegment.text("test")
|
message[0] == MessageSegment.text("test")
|
||||||
# 切片
|
# 切片
|
||||||
message[0:2] == Message(
|
message[0:2] == Message([MessageSegment.text("test"), MessageSegment.markdown("test2")])
|
||||||
[MessageSegment.text("test"), MessageSegment.markdown("test2")]
|
|
||||||
)
|
|
||||||
# 类型过滤
|
# 类型过滤
|
||||||
message["markdown"] == Message(
|
message["markdown"] == Message(
|
||||||
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
||||||
@@ -262,7 +260,7 @@ msg = seg.join(
|
|||||||
MessageSegment.text("second"),
|
MessageSegment.text("second"),
|
||||||
MessageSegment.text("third"),
|
MessageSegment.text("third"),
|
||||||
]
|
]
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
msg == Message(
|
msg == Message(
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ NoneBot 默认使用 Python 的字典将事件响应器存储于内存中,但
|
|||||||
```python
|
```python
|
||||||
from nonebot.matcher import MatcherProvider
|
from nonebot.matcher import MatcherProvider
|
||||||
|
|
||||||
class CustomProvider(MatcherProvider):
|
|
||||||
...
|
class CustomProvider(MatcherProvider): ...
|
||||||
```
|
```
|
||||||
|
|
||||||
## 设置存储提供者
|
## 设置存储提供者
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ NoneBot 兼容层定义了两个数据类 `HTTPServerSetup` 和 `WebSocketServer
|
|||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.drivers import URL, Request, Response, ASGIMixin, HTTPServerSetup
|
from nonebot.drivers import URL, Request, Response, ASGIMixin, HTTPServerSetup
|
||||||
|
|
||||||
|
|
||||||
async def hello(request: Request) -> Response:
|
async def hello(request: Request) -> Response:
|
||||||
return Response(200, content="Hello, world!")
|
return Response(200, content="Hello, world!")
|
||||||
|
|
||||||
|
|
||||||
if isinstance((driver := get_driver()), ASGIMixin):
|
if isinstance((driver := get_driver()), ASGIMixin):
|
||||||
driver.setup_http_server(
|
driver.setup_http_server(
|
||||||
HTTPServerSetup(
|
HTTPServerSetup(
|
||||||
@@ -78,6 +80,7 @@ if isinstance((driver := get_driver()), ASGIMixin):
|
|||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.drivers import URL, ASGIMixin, WebSocket, WebSocketServerSetup
|
from nonebot.drivers import URL, ASGIMixin, WebSocket, WebSocketServerSetup
|
||||||
|
|
||||||
|
|
||||||
async def ws_handler(ws: WebSocket):
|
async def ws_handler(ws: WebSocket):
|
||||||
await ws.accept()
|
await ws.accept()
|
||||||
try:
|
try:
|
||||||
@@ -92,6 +95,7 @@ async def ws_handler(ws: WebSocket):
|
|||||||
await websocket.close()
|
await websocket.close()
|
||||||
# do some cleanup
|
# do some cleanup
|
||||||
|
|
||||||
|
|
||||||
if isinstance((driver := get_driver()), ASGIMixin):
|
if isinstance((driver := get_driver()), ASGIMixin):
|
||||||
driver.setup_websocket_server(
|
driver.setup_websocket_server(
|
||||||
WebSocketServerSetup(
|
WebSocketServerSetup(
|
||||||
@@ -129,6 +133,7 @@ from fastapi import FastAPI
|
|||||||
|
|
||||||
app: FastAPI = nonebot.get_app()
|
app: FastAPI = nonebot.get_app()
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api")
|
@app.get("/api")
|
||||||
async def custom_api():
|
async def custom_api():
|
||||||
return {"message": "Hello, world!"}
|
return {"message": "Hello, world!"}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
async def do_something():
|
async def do_something():
|
||||||
pass
|
pass
|
||||||
@@ -43,6 +44,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_shutdown
|
@driver.on_shutdown
|
||||||
async def do_something():
|
async def do_something():
|
||||||
pass
|
pass
|
||||||
@@ -57,6 +59,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_connect
|
@driver.on_bot_connect
|
||||||
async def do_something(bot: Bot):
|
async def do_something(bot: Bot):
|
||||||
pass
|
pass
|
||||||
@@ -71,6 +74,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_disconnect
|
@driver.on_bot_disconnect
|
||||||
async def do_something(bot: Bot):
|
async def do_something(bot: Bot):
|
||||||
pass
|
pass
|
||||||
@@ -88,6 +92,7 @@ async def do_something(bot: Bot):
|
|||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
from nonebot.message import event_preprocessor
|
from nonebot.message import event_preprocessor
|
||||||
|
|
||||||
|
|
||||||
@event_preprocessor
|
@event_preprocessor
|
||||||
async def do_something(event: Event):
|
async def do_something(event: Event):
|
||||||
if not event.is_tome():
|
if not event.is_tome():
|
||||||
@@ -101,6 +106,7 @@ async def do_something(event: Event):
|
|||||||
```python
|
```python
|
||||||
from nonebot.message import event_postprocessor
|
from nonebot.message import event_postprocessor
|
||||||
|
|
||||||
|
|
||||||
@event_postprocessor
|
@event_postprocessor
|
||||||
async def do_something(event: Event):
|
async def do_something(event: Event):
|
||||||
pass
|
pass
|
||||||
@@ -114,6 +120,7 @@ async def do_something(event: Event):
|
|||||||
from nonebot.message import run_preprocessor
|
from nonebot.message import run_preprocessor
|
||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
|
|
||||||
|
|
||||||
@run_preprocessor
|
@run_preprocessor
|
||||||
async def do_something(event: Event, matcher: Matcher):
|
async def do_something(event: Event, matcher: Matcher):
|
||||||
if not event.is_tome():
|
if not event.is_tome():
|
||||||
@@ -127,6 +134,7 @@ async def do_something(event: Event, matcher: Matcher):
|
|||||||
```python
|
```python
|
||||||
from nonebot.message import run_postprocessor
|
from nonebot.message import run_postprocessor
|
||||||
|
|
||||||
|
|
||||||
@run_postprocessor
|
@run_postprocessor
|
||||||
async def do_something(event: Event, matcher: Matcher, exception: Optional[Exception]):
|
async def do_something(event: Event, matcher: Matcher, exception: Optional[Exception]):
|
||||||
pass
|
pass
|
||||||
@@ -140,6 +148,7 @@ async def do_something(event: Event, matcher: Matcher, exception: Optional[Excep
|
|||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
from nonebot.exception import MockApiException
|
from nonebot.exception import MockApiException
|
||||||
|
|
||||||
|
|
||||||
@Bot.on_calling_api
|
@Bot.on_calling_api
|
||||||
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
||||||
if api == "send_msg":
|
if api == "send_msg":
|
||||||
@@ -154,9 +163,14 @@ async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
|||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
from nonebot.exception import MockApiException
|
from nonebot.exception import MockApiException
|
||||||
|
|
||||||
|
|
||||||
@Bot.on_called_api
|
@Bot.on_called_api
|
||||||
async def handle_api_result(
|
async def handle_api_result(
|
||||||
bot: Bot, exception: Optional[Exception], api: str, data: Dict[str, Any], result: Any
|
bot: Bot,
|
||||||
|
exception: Optional[Exception],
|
||||||
|
api: str,
|
||||||
|
data: Dict[str, Any],
|
||||||
|
result: Any,
|
||||||
):
|
):
|
||||||
if not exception and api == "send_msg":
|
if not exception and api == "send_msg":
|
||||||
raise MockApiException(result={**result, "message_id": 123})
|
raise MockApiException(result={**result, "message_id": 123})
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ options:
|
|||||||
```python {3-5}
|
```python {3-5}
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.type_updater
|
@foo.type_updater
|
||||||
async def _() -> str:
|
async def _() -> str:
|
||||||
return "notice"
|
return "notice"
|
||||||
@@ -37,6 +38,7 @@ from nonebot.permission import User
|
|||||||
|
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.permission_updater
|
@foo.permission_updater
|
||||||
async def _(event: Event, matcher: Matcher) -> Permission:
|
async def _(event: Event, matcher: Matcher) -> Permission:
|
||||||
return Permission(User.from_event(event, perm=matcher.permission))
|
return Permission(User.from_event(event, perm=matcher.permission))
|
||||||
@@ -49,6 +51,7 @@ from nonebot.permission import USER
|
|||||||
|
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.permission_updater
|
@foo.permission_updater
|
||||||
async def _(matcher: Matcher) -> Permission:
|
async def _(matcher: Matcher) -> Permission:
|
||||||
return USER("session1", "session2", perm=matcher.permission)
|
return USER("session1", "session2", perm=matcher.permission)
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ NoneBotException
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
matcher = on_notice(block=True)
|
matcher = on_notice(block=True)
|
||||||
|
|
||||||
|
|
||||||
# 或者
|
# 或者
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handler(matcher: Matcher):
|
async def handler(matcher: Matcher):
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ description: nonebot 模块
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from nonebot.adapters.console import Adapter
|
from nonebot.adapters.console import Adapter
|
||||||
|
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -142,17 +142,18 @@ description: nonebot.params 模块
|
|||||||
def depend_func() -> Any:
|
def depend_func() -> Any:
|
||||||
return ...
|
return ...
|
||||||
|
|
||||||
|
|
||||||
def depend_gen_func():
|
def depend_gen_func():
|
||||||
try:
|
try:
|
||||||
yield ...
|
yield ...
|
||||||
finally:
|
finally:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
async def handler(
|
async def handler(
|
||||||
param_name: Any = Depends(depend_func),
|
param_name: Any = Depends(depend_func),
|
||||||
gen: Any = Depends(depend_gen_func),
|
gen: Any = Depends(depend_gen_func),
|
||||||
):
|
): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## _class_ `EventParam(*args, checker=None, **kwargs)` {#EventParam}
|
## _class_ `EventParam(*args, checker=None, **kwargs)` {#EventParam}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ logger.add(
|
|||||||
level=0,
|
level=0,
|
||||||
diagnose=True,
|
diagnose=True,
|
||||||
format="<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}",
|
format="<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}",
|
||||||
filter=default_filter
|
filter=default_filter,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ options:
|
|||||||
```python {4} title=weather/__init__.py
|
```python {4} title=weather/__init__.py
|
||||||
from nonebot.adapters.console import MessageEvent
|
from nonebot.adapters.console import MessageEvent
|
||||||
|
|
||||||
|
|
||||||
@weather.got("location", prompt="请输入地名")
|
@weather.got("location", prompt="请输入地名")
|
||||||
async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
||||||
await weather.finish(f"{event.time.strftime('%Y-%m-%d')} {location} 的天气是...")
|
await weather.finish(f"{event.time.strftime('%Y-%m-%d')} {location} 的天气是...")
|
||||||
@@ -39,10 +40,12 @@ async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
|||||||
```python {4,8}
|
```python {4,8}
|
||||||
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
|
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_private(event: PrivateMessageEvent):
|
async def handle_private(event: PrivateMessageEvent):
|
||||||
await matcher.finish("私聊消息")
|
await matcher.finish("私聊消息")
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_group(event: GroupMessageEvent):
|
async def handle_group(event: GroupMessageEvent):
|
||||||
await matcher.finish("群聊消息")
|
await matcher.finish("群聊消息")
|
||||||
@@ -54,10 +57,12 @@ async def handle_group(event: GroupMessageEvent):
|
|||||||
from nonebot.adapters.console import Bot as ConsoleBot
|
from nonebot.adapters.console import Bot as ConsoleBot
|
||||||
from nonebot.adapters.onebot.v11 import Bot as OneBot
|
from nonebot.adapters.onebot.v11 import Bot as OneBot
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_console(bot: ConsoleBot):
|
async def handle_console(bot: ConsoleBot):
|
||||||
await bot.bell()
|
await bot.bell()
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_onebot(bot: OneBot):
|
async def handle_onebot(bot: OneBot):
|
||||||
await bot.send_group_message(group_id=123123, message="OneBot")
|
await bot.send_group_message(group_id=123123, message="OneBot")
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ from .config import Config
|
|||||||
|
|
||||||
plugin_config = get_plugin_config(Config)
|
plugin_config = get_plugin_config(Config)
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
weather = on_command("天气", rule=is_enable)
|
weather = on_command("天气", rule=is_enable)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -43,12 +45,15 @@ weather = on_command("天气", rule=is_enable)
|
|||||||
from nonebot.rule import Rule
|
from nonebot.rule import Rule
|
||||||
from nonebot.adapters import Event
|
from nonebot.adapters import Event
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
async def is_blacklisted(event: Event) -> bool:
|
async def is_blacklisted(event: Event) -> bool:
|
||||||
return event.get_user_id() not in BLACKLIST
|
return event.get_user_id() not in BLACKLIST
|
||||||
|
|
||||||
|
|
||||||
rule = Rule(is_enable, is_blacklisted)
|
rule = Rule(is_enable, is_blacklisted)
|
||||||
|
|
||||||
weather = on_command("天气", rule=rule)
|
weather = on_command("天气", rule=rule)
|
||||||
@@ -66,9 +71,11 @@ from .config import Config
|
|||||||
|
|
||||||
plugin_config = get_plugin_config(Config)
|
plugin_config = get_plugin_config(Config)
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
weather = on_command(
|
weather = on_command(
|
||||||
"天气",
|
"天气",
|
||||||
rule=to_me() & is_enable,
|
rule=to_me() & is_enable,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ NoneBot 中的会话状态是一个字典,可以通过类型 `T_State` 来获
|
|||||||
```python
|
```python
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
|
|
||||||
|
|
||||||
@matcher.got("key", prompt="请输入密码")
|
@matcher.got("key", prompt="请输入密码")
|
||||||
async def _(state: T_State, key: str = ArgPlainText()):
|
async def _(state: T_State, key: str = ArgPlainText()):
|
||||||
if key != "some password":
|
if key != "some password":
|
||||||
@@ -34,10 +35,12 @@ async def _(state: T_State, key: str = ArgPlainText()):
|
|||||||
```python
|
```python
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
state["key"] = "value"
|
state["key"] = "value"
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
await matcher.finish(state["key"])
|
await matcher.finish(state["key"])
|
||||||
@@ -49,10 +52,12 @@ async def _(state: T_State):
|
|||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
from nonebot.adapters import MessageTemplate
|
from nonebot.adapters import MessageTemplate
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
state["username"] = "user"
|
state["username"] = "user"
|
||||||
|
|
||||||
|
|
||||||
@matcher.got("password", prompt=MessageTemplate("请输入 {username} 的密码"))
|
@matcher.got("password", prompt=MessageTemplate("请输入 {username} 的密码"))
|
||||||
async def _():
|
async def _():
|
||||||
await matcher.finish(MessageTemplate("密码为 {password}"))
|
await matcher.finish(MessageTemplate("密码为 {password}"))
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ alc = Alconna(
|
|||||||
Args["package", str],
|
Args["package", str],
|
||||||
Option("-r|--requirement", Args["file", str]),
|
Option("-r|--requirement", Args["file", str]),
|
||||||
Option("-i|--index-url", Args["url", str]),
|
Option("-i|--index-url", Args["url", str]),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
res = alc.parse("pip install nonebot2 -i URL")
|
res = alc.parse("pip install nonebot2 -i URL")
|
||||||
@@ -383,20 +383,33 @@ alc = Alconna(..., meta=CommandMeta("foo", example="bar"))
|
|||||||
from arclet.alconna import Alconna, namespace, Namespace, Subcommand, Args, config
|
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方式创建命名空间
|
||||||
with namespace("bar") as np1:
|
with namespace("bar") as np1:
|
||||||
np1.prefixes = ["!"] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
np1.prefixes = [
|
||||||
|
"!"
|
||||||
|
] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
||||||
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
|
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
|
||||||
np1.builtin_option_name["help"] = {"帮助", "-h"} # 设置此命名空间下的命令的帮助选项名称
|
np1.builtin_option_name["help"] = {
|
||||||
|
"帮助",
|
||||||
|
"-h",
|
||||||
|
} # 设置此命名空间下的命令的帮助选项名称
|
||||||
|
|
||||||
# 你还可以使用config来管理所有命名空间并切换至任意命名空间
|
# 你还可以使用config来管理所有命名空间并切换至任意命名空间
|
||||||
config.namespaces["foo"] = ns # 将命名空间挂载到 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"命名空间
|
||||||
```
|
```
|
||||||
|
|
||||||
### 修改默认的命名空间
|
### 修改默认的命名空间
|
||||||
@@ -469,10 +482,12 @@ alc.shortcut("echo", {"command": "eval print(\\'{*}\\')"})
|
|||||||
alc.shortcut("echo", delete=True) # 删除快捷指令
|
alc.shortcut("echo", delete=True) # 删除快捷指令
|
||||||
# 'Alconna::eval 的快捷指令: "echo" 删除成功'
|
# 'Alconna::eval 的快捷指令: "echo" 删除成功'
|
||||||
|
|
||||||
|
|
||||||
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
||||||
def cb(content: str):
|
def cb(content: str):
|
||||||
eval(content, {}, {})
|
eval(content, {}, {})
|
||||||
|
|
||||||
|
|
||||||
alc.parse('eval print(\\"hello world\\")')
|
alc.parse('eval print(\\"hello world\\")')
|
||||||
# hello world
|
# hello world
|
||||||
|
|
||||||
@@ -523,7 +538,12 @@ alc.parse("eval --shortcut list")
|
|||||||
from arclet.alconna import Alconna, Option, CommandMeta, Args
|
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
|
assert alc.parse("test123 BARabc").matched
|
||||||
```
|
```
|
||||||
@@ -534,7 +554,9 @@ assert alc.parse("test123 BARabc").matched
|
|||||||
from arclet.alconna import Alconna, Option, Args, append
|
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"))
|
print(alc.parse("gcc -Fabc -Fdef -Fxyz").query[list]("flag.content"))
|
||||||
# ['abc', 'def', 'xyz']
|
# ['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 = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar")
|
||||||
alc.parse("test --comp")
|
alc.parse("test --comp")
|
||||||
|
|
||||||
'''
|
"""
|
||||||
output
|
output
|
||||||
|
|
||||||
以下是建议的输入:
|
以下是建议的输入:
|
||||||
@@ -588,7 +610,7 @@ output
|
|||||||
* --shortcut
|
* --shortcut
|
||||||
* foo
|
* foo
|
||||||
* bar
|
* bar
|
||||||
'''
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
## Duplication
|
## Duplication
|
||||||
@@ -600,7 +622,16 @@ output
|
|||||||
以pip为例,其对应的 Duplication 应如下构造:
|
以pip为例,其对应的 Duplication 应如下构造:
|
||||||
|
|
||||||
```python
|
```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):
|
class MyDup(Duplication):
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match
|
|||||||
|
|
||||||
echo = on_alconna(Alconna("echo", Args["msg", str]))
|
echo = on_alconna(Alconna("echo", Args["msg", str]))
|
||||||
|
|
||||||
|
|
||||||
@echo.handle()
|
@echo.handle()
|
||||||
async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
|
async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
|
||||||
await echo.finish(msg.result)
|
await echo.finish(msg.result)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
相比于 `on_alconna`, `funcommand` 增加了三个参数 `name`, `prefixes` 和 `description`。
|
相比于 `on_alconna`, `funcommand` 增加了三个参数 `name`, `prefixes` 和 `description`。
|
||||||
@@ -54,6 +54,7 @@ book = (
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@book.handle()
|
@book.handle()
|
||||||
async def _(arp: Arparma):
|
async def _(arp: Arparma):
|
||||||
await book.send(str(arp.options))
|
await book.send(str(arp.options))
|
||||||
|
|||||||
@@ -45,15 +45,12 @@ message = UniMessage(
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import Button, UniMessage
|
from nonebot_plugin_alconna import Button, UniMessage
|
||||||
|
|
||||||
message = (
|
message = UniMessage.text("hello world").keyboard(
|
||||||
UniMessage.text("hello world")
|
|
||||||
.keyboard(
|
|
||||||
Button("link1", url="https://example.com/1"),
|
Button("link1", url="https://example.com/1"),
|
||||||
Button("link2", url="https://example.com/2"),
|
Button("link2", url="https://example.com/2"),
|
||||||
Button("link3", url="https://example.com/3"),
|
Button("link3", url="https://example.com/3"),
|
||||||
row=3,
|
row=3,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 发送
|
### 发送
|
||||||
@@ -94,6 +91,7 @@ async def _():
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import message_recall, message_edit, message_reaction
|
from nonebot_plugin_alconna import message_recall, message_edit, message_reaction
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _():
|
async def _():
|
||||||
await message_edit(UniMessage.text("hello world"))
|
await message_edit(UniMessage.text("hello world"))
|
||||||
@@ -120,9 +118,9 @@ async def _():
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(msg: UniMsg):
|
async def _(msg: UniMsg): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
然后你可以通过 `UniMessage` 的方法来处理消息.
|
然后你可以通过 `UniMessage` 的方法来处理消息.
|
||||||
@@ -182,6 +180,7 @@ async def _(msg: UniMsg):
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(msg: UniMsg):
|
async def _(msg: UniMsg):
|
||||||
data: list[dict] = msg.dump()
|
data: list[dict] = msg.dump()
|
||||||
@@ -193,6 +192,7 @@ async def _(msg: UniMsg):
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMessage
|
from nonebot_plugin_alconna import UniMessage
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _():
|
async def _():
|
||||||
data = [
|
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 = on_alconna(Alconna("make_meme", Args["name", str]["img", Image]))
|
||||||
|
|
||||||
|
|
||||||
@meme.handle()
|
@meme.handle()
|
||||||
async def _(img: Image):
|
async def _(img: Image): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 模型定义
|
## 模型定义
|
||||||
@@ -24,6 +24,7 @@ async def _(img: Image):
|
|||||||
```python
|
```python
|
||||||
class Segment:
|
class Segment:
|
||||||
"""基类标注"""
|
"""基类标注"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> str: ...
|
def type(self) -> str: ...
|
||||||
@property
|
@property
|
||||||
@@ -31,29 +32,40 @@ class Segment:
|
|||||||
@property
|
@property
|
||||||
def children(self) -> list["Segment"]: ...
|
def children(self) -> list["Segment"]: ...
|
||||||
|
|
||||||
|
|
||||||
class Text(Segment):
|
class Text(Segment):
|
||||||
"""Text对象, 表示一类文本元素"""
|
"""Text对象, 表示一类文本元素"""
|
||||||
|
|
||||||
text: str
|
text: str
|
||||||
styles: dict[tuple[int, int], list[str]]
|
styles: dict[tuple[int, int], list[str]]
|
||||||
|
|
||||||
def cover(self, text: 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):
|
class At(Segment):
|
||||||
"""At对象, 表示一类提醒某用户的元素"""
|
"""At对象, 表示一类提醒某用户的元素"""
|
||||||
|
|
||||||
flag: Literal["user", "role", "channel"]
|
flag: Literal["user", "role", "channel"]
|
||||||
target: str
|
target: str
|
||||||
display: Optional[str]
|
display: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class AtAll(Segment):
|
class AtAll(Segment):
|
||||||
"""AtAll对象, 表示一类提醒所有人的元素"""
|
"""AtAll对象, 表示一类提醒所有人的元素"""
|
||||||
|
|
||||||
here: bool
|
here: bool
|
||||||
|
|
||||||
|
|
||||||
class Emoji(Segment):
|
class Emoji(Segment):
|
||||||
"""Emoji对象, 表示一类表情元素"""
|
"""Emoji对象, 表示一类表情元素"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
name: Optional[str]
|
name: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class Media(Segment):
|
class Media(Segment):
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
url: Optional[str]
|
url: Optional[str]
|
||||||
@@ -64,53 +76,72 @@ class Media(Segment):
|
|||||||
|
|
||||||
to_url: ClassVar[Optional[MediaToUrl]]
|
to_url: ClassVar[Optional[MediaToUrl]]
|
||||||
|
|
||||||
|
|
||||||
class Image(Media):
|
class Image(Media):
|
||||||
"""Image对象, 表示一类图片元素"""
|
"""Image对象, 表示一类图片元素"""
|
||||||
|
|
||||||
width: Optional[int]
|
width: Optional[int]
|
||||||
height: Optional[int]
|
height: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
class Audio(Media):
|
class Audio(Media):
|
||||||
"""Audio对象, 表示一类音频元素"""
|
"""Audio对象, 表示一类音频元素"""
|
||||||
|
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class Voice(Media):
|
class Voice(Media):
|
||||||
"""Voice对象, 表示一类语音元素"""
|
"""Voice对象, 表示一类语音元素"""
|
||||||
|
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class Video(Media):
|
class Video(Media):
|
||||||
"""Video对象, 表示一类视频元素"""
|
"""Video对象, 表示一类视频元素"""
|
||||||
|
|
||||||
thumbnail: Optional[Image]
|
thumbnail: Optional[Image]
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class File(Media):
|
class File(Media):
|
||||||
"""File对象, 表示一类文件元素"""
|
"""File对象, 表示一类文件元素"""
|
||||||
|
|
||||||
|
|
||||||
class Reply(Segment):
|
class Reply(Segment):
|
||||||
"""Reply对象,表示一类回复消息"""
|
"""Reply对象,表示一类回复消息"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||||
msg: Optional[Union[Message, str]]
|
msg: Optional[Union[Message, str]]
|
||||||
origin: Optional[Any]
|
origin: Optional[Any]
|
||||||
|
|
||||||
|
|
||||||
class Reference(Segment):
|
class Reference(Segment):
|
||||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||||
children: List[Union[RefNode, CustomNode]]
|
children: List[Union[RefNode, CustomNode]]
|
||||||
|
|
||||||
|
|
||||||
class Hyper(Segment):
|
class Hyper(Segment):
|
||||||
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
||||||
|
|
||||||
format: Literal["xml", "json"]
|
format: Literal["xml", "json"]
|
||||||
raw: Optional[str]
|
raw: Optional[str]
|
||||||
content: Optional[Union[dict, list]]
|
content: Optional[Union[dict, list]]
|
||||||
|
|
||||||
|
|
||||||
class Reference(Segment):
|
class Reference(Segment):
|
||||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
nodes: Sequence[Union[RefNode, CustomNode]]
|
nodes: Sequence[Union[RefNode, CustomNode]]
|
||||||
|
|
||||||
|
|
||||||
class Button(Segment):
|
class Button(Segment):
|
||||||
"""Button对象,表示一类按钮消息"""
|
"""Button对象,表示一类按钮消息"""
|
||||||
|
|
||||||
flag: Literal["action", "link", "input", "enter"]
|
flag: Literal["action", "link", "input", "enter"]
|
||||||
"""
|
"""
|
||||||
- 点击 action 类型的按钮时会触发一个关于 按钮回调 事件,该事件的 button 资源会包含上述 id
|
- 点击 action 类型的按钮时会触发一个关于 按钮回调 事件,该事件的 button 资源会包含上述 id
|
||||||
@@ -138,20 +169,26 @@ class Button(Segment):
|
|||||||
- list[At]: 指定用户/身份组可操作
|
- list[At]: 指定用户/身份组可操作
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Keyboard(Segment):
|
class Keyboard(Segment):
|
||||||
"""Keyboard对象,表示一行按钮元素"""
|
"""Keyboard对象,表示一行按钮元素"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
"""此处一般用来表示模板id,特殊情况下可能表示例如 bot_appid 等"""
|
"""此处一般用来表示模板id,特殊情况下可能表示例如 bot_appid 等"""
|
||||||
buttons: Optional[list[Button]]
|
buttons: Optional[list[Button]]
|
||||||
row: Optional[int]
|
row: Optional[int]
|
||||||
"""当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数"""
|
"""当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数"""
|
||||||
|
|
||||||
|
|
||||||
class Other(Segment):
|
class Other(Segment):
|
||||||
"""其他 Segment"""
|
"""其他 Segment"""
|
||||||
|
|
||||||
origin: MessageSegment
|
origin: MessageSegment
|
||||||
|
|
||||||
|
|
||||||
class I18n(Segment):
|
class I18n(Segment):
|
||||||
"""特殊的 Segment,用于 i18n 消息"""
|
"""特殊的 Segment,用于 i18n 消息"""
|
||||||
|
|
||||||
item_or_scope: Union[LangItem, str]
|
item_or_scope: Union[LangItem, str]
|
||||||
type_: Optional[str] = None
|
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
|
from nonebot_plugin_alconna.builtins.uniseg.market_face import MarketFace
|
||||||
|
|
||||||
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 Image 的元素选出来并将第一个作为结果
|
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 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
|
# 表示这个指令需要的图片要么直接是 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#嵌套提取)
|
也可以参考通用消息的 [`嵌套提取`](./message.mdx#嵌套提取)
|
||||||
@@ -213,10 +254,13 @@ def mfbuild(builder: MessageBuilder, seg: BaseMessageSegment):
|
|||||||
|
|
||||||
|
|
||||||
@custom_handler(MarketFace)
|
@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:
|
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 的处理方法。
|
具体而言,你可以使用 `custom_register` 来增加一个从 MessageSegment 到 Segment 的处理方法;使用 `custom_handler` 来增加一个从 Segment 到 MessageSegment 的处理方法。
|
||||||
|
|||||||
@@ -155,7 +155,9 @@ op.create_table( # CREATE TABLE
|
|||||||
"weather_weather", # weather_weather
|
"weather_weather", # weather_weather
|
||||||
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
||||||
sa.Column("weather", sa.String(), nullable=False), # weather 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"},
|
info={"bind_key": "weather"},
|
||||||
)
|
)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
@@ -245,7 +247,9 @@ from nonebot.typing import T_State
|
|||||||
|
|
||||||
|
|
||||||
@weather.got("location", prompt="请输入地名")
|
@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)
|
wea = await session.get(Weather, location)
|
||||||
|
|
||||||
if not wea:
|
if not wea:
|
||||||
@@ -348,13 +352,16 @@ async def _(
|
|||||||
```python title=weather/__init__.py {5} showLineNumbers
|
```python title=weather/__init__.py {5} showLineNumbers
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
@weather.handle()
|
@weather.handle()
|
||||||
async def _(
|
async def _(
|
||||||
weas: Sequence[Weather] = SQLDepends(
|
weas: Sequence[Weather] = SQLDepends(
|
||||||
select(Weather).where(Weather.weather == Depends(extract_arg_plain_text))
|
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)。
|
支持的类型标注请参见 [依赖注入](dependency)。
|
||||||
@@ -364,6 +371,7 @@ async def _(
|
|||||||
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
class Weather(Model):
|
class Weather(Model):
|
||||||
location: Mapped[str] = mapped_column(primary_key=True)
|
location: Mapped[str] = mapped_column(primary_key=True)
|
||||||
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
||||||
|
|||||||
@@ -78,8 +78,7 @@ async def html_to_pic(
|
|||||||
img_fetch_fn: ImgFetchFn = combined_img_fetcher,
|
img_fetch_fn: ImgFetchFn = combined_img_fetcher,
|
||||||
css_fetch_fn: CSSFetchFn = combined_css_fetcher,
|
css_fetch_fn: CSSFetchFn = combined_css_fetcher,
|
||||||
urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin,
|
urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
最核心的渲染函数。
|
最核心的渲染函数。
|
||||||
@@ -107,8 +106,7 @@ async def text_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可用于渲染多行文本。
|
可用于渲染多行文本。
|
||||||
@@ -128,8 +126,7 @@ async def md_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。
|
可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。
|
||||||
@@ -153,8 +150,7 @@ async def template_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
渲染 jinja2 模板。
|
渲染 jinja2 模板。
|
||||||
|
|||||||
@@ -44,15 +44,18 @@ require("nonebot_plugin_apscheduler")
|
|||||||
|
|
||||||
from nonebot_plugin_apscheduler import scheduler
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
|
||||||
|
|
||||||
# 基于装饰器的方式
|
# 基于装饰器的方式
|
||||||
@scheduler.scheduled_job("cron", hour="*/2", id="job_0", args=[1], kwargs={arg2: 2})
|
@scheduler.scheduled_job("cron", hour="*/2", id="job_0", args=[1], kwargs={arg2: 2})
|
||||||
async def run_every_2_hour(arg1: int, arg2: int):
|
async def run_every_2_hour(arg1: int, arg2: int):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# 基于 add_job 方法的方式
|
# 基于 add_job 方法的方式
|
||||||
def run_every_day(arg1: int, arg2: int):
|
def run_every_day(arg1: int, arg2: int):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
run_every_day, "interval", days=1, id="job_1", args=[1], kwargs={arg2: 2}
|
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
|
```python {5,6} title=tests/test_http_server.py
|
||||||
from nonebug import App
|
from nonebug import App
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_http_server(app: App):
|
async def test_http_server(app: App):
|
||||||
async with app.test_server() as ctx:
|
async with app.test_server() as ctx:
|
||||||
@@ -45,6 +46,7 @@ import nonebot
|
|||||||
from nonebug import App
|
from nonebug import App
|
||||||
from nonebot.adapters.fake import Adapter
|
from nonebot.adapters.fake import Adapter
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_http_server(app: App):
|
async def test_http_server(app: App):
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
@@ -72,6 +74,7 @@ import nonebot
|
|||||||
from nonebug import App
|
from nonebug import App
|
||||||
from nonebot.adapters.fake import Adapter
|
from nonebot.adapters.fake import Adapter
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_ws_server(app: App):
|
async def test_ws_server(app: App):
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ except Exception as e:
|
|||||||
```python title=config.py
|
```python title=config.py
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseModel):
|
class Config(BaseModel):
|
||||||
xxx_id: str
|
xxx_id: str
|
||||||
xxx_token: str
|
xxx_token: str
|
||||||
@@ -102,6 +103,7 @@ from nonebot.adapters import Adapter as BaseAdapter
|
|||||||
|
|
||||||
from .config import Config
|
from .config import Config
|
||||||
|
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
def __init__(self, driver: Driver, **kwargs: Any):
|
def __init__(self, driver: Driver, **kwargs: Any):
|
||||||
@@ -208,9 +210,10 @@ from nonebot.drivers import (
|
|||||||
ASGIMixin,
|
ASGIMixin,
|
||||||
WebSocket,
|
WebSocket,
|
||||||
HTTPServerSetup,
|
HTTPServerSetup,
|
||||||
WebSocketServerSetup
|
WebSocketServerSetup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
def __init__(self, driver: Driver, **kwargs: Any):
|
def __init__(self, driver: Driver, **kwargs: Any):
|
||||||
@@ -242,7 +245,6 @@ class Adapter(BaseAdapter):
|
|||||||
)
|
)
|
||||||
self.setup_websocket_server(ws_setup)
|
self.setup_websocket_server(ws_setup)
|
||||||
|
|
||||||
|
|
||||||
async def _handle_http(self, request: Request) -> Response:
|
async def _handle_http(self, request: Request) -> Response:
|
||||||
"""HTTP 路由处理函数,只有一个类型为 Request 的参数,且返回值类型为 Response"""
|
"""HTTP 路由处理函数,只有一个类型为 Request 的参数,且返回值类型为 Response"""
|
||||||
...
|
...
|
||||||
@@ -270,8 +272,8 @@ class Adapter(BaseAdapter):
|
|||||||
```python {7,8,11} title=adapter.py
|
```python {7,8,11} title=adapter.py
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
def _handle_connect(self):
|
def _handle_connect(self):
|
||||||
bot_id = ... # 通过配置或者平台 API 等方式,获取到 Bot 的 ID
|
bot_id = ... # 通过配置或者平台 API 等方式,获取到 Bot 的 ID
|
||||||
bot = Bot(self, self_id=bot_id) # 实例化 Bot
|
bot = Bot(self, self_id=bot_id) # 实例化 Bot
|
||||||
@@ -295,8 +297,8 @@ from .bot import Bot
|
|||||||
from .event import Event
|
from .event import Event
|
||||||
from .log import log
|
from .log import log
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
@classmethod
|
@classmethod
|
||||||
def payload_to_event(cls, payload: Dict[str, Any]) -> Event:
|
def payload_to_event(cls, payload: Dict[str, Any]) -> Event:
|
||||||
"""根据平台事件的特性,转换平台 payload 为具体 Event
|
"""根据平台事件的特性,转换平台 payload 为具体 Event
|
||||||
@@ -316,7 +318,6 @@ class Adapter(BaseAdapter):
|
|||||||
# 也可以尝试转为基础 Event 进行处理
|
# 也可以尝试转为基础 Event 进行处理
|
||||||
return type_validate_python(Event, payload)
|
return type_validate_python(Event, payload)
|
||||||
|
|
||||||
|
|
||||||
async def _forward(self, bot: Bot):
|
async def _forward(self, bot: Bot):
|
||||||
|
|
||||||
payload: Dict[str, Any] # 接收到的事件数据
|
payload: Dict[str, Any] # 接收到的事件数据
|
||||||
@@ -337,8 +338,8 @@ from nonebot.drivers import Request, WebSocket
|
|||||||
|
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
|
async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
|
||||||
log("DEBUG", f"Calling API <y>{api}</y>") # 给予日志提示
|
log("DEBUG", f"Calling API <y>{api}</y>") # 给予日志提示
|
||||||
@@ -356,7 +357,6 @@ class Adapter(BaseAdapter):
|
|||||||
# 发送请求,返回结果
|
# 发送请求,返回结果
|
||||||
return await self.driver.request(request)
|
return await self.driver.request(request)
|
||||||
|
|
||||||
|
|
||||||
# 采用 WebSocket 通信的方式,可以直接调用 send 方法发送数据
|
# 采用 WebSocket 通信的方式,可以直接调用 send 方法发送数据
|
||||||
# 通过某种方式获取到 bot 对应的 websocket 对象
|
# 通过某种方式获取到 bot 对应的 websocket 对象
|
||||||
ws: WebSocket = your_get_websocket_method(bot.self_id)
|
ws: WebSocket = your_get_websocket_method(bot.self_id)
|
||||||
@@ -450,8 +450,8 @@ from typing_extensions import override
|
|||||||
from nonebot.compat import model_dump
|
from nonebot.compat import model_dump
|
||||||
from nonebot.adapters import Event as BaseEvent
|
from nonebot.adapters import Event as BaseEvent
|
||||||
|
|
||||||
class Event(BaseEvent):
|
|
||||||
|
|
||||||
|
class Event(BaseEvent):
|
||||||
@override
|
@override
|
||||||
def get_event_name(self) -> str:
|
def get_event_name(self) -> str:
|
||||||
# 返回事件的名称,用于日志打印
|
# 返回事件的名称,用于日志打印
|
||||||
@@ -488,6 +488,7 @@ class Event(BaseEvent):
|
|||||||
```python {7,16,20,25,34,42} title=event.py
|
```python {7,16,20,25,34,42} title=event.py
|
||||||
from .message import Message
|
from .message import Message
|
||||||
|
|
||||||
|
|
||||||
class HeartbeatEvent(Event):
|
class HeartbeatEvent(Event):
|
||||||
"""心跳时间,通常为元事件"""
|
"""心跳时间,通常为元事件"""
|
||||||
|
|
||||||
@@ -495,8 +496,10 @@ class HeartbeatEvent(Event):
|
|||||||
def get_type(self) -> str:
|
def get_type(self) -> str:
|
||||||
return "meta_event"
|
return "meta_event"
|
||||||
|
|
||||||
|
|
||||||
class MessageEvent(Event):
|
class MessageEvent(Event):
|
||||||
"""消息事件"""
|
"""消息事件"""
|
||||||
|
|
||||||
message_id: str
|
message_id: str
|
||||||
user_id: str
|
user_id: str
|
||||||
|
|
||||||
@@ -513,8 +516,10 @@ class MessageEvent(Event):
|
|||||||
def get_user_id(self) -> str:
|
def get_user_id(self) -> str:
|
||||||
return self.user_id
|
return self.user_id
|
||||||
|
|
||||||
|
|
||||||
class JoinRoomEvent(Event):
|
class JoinRoomEvent(Event):
|
||||||
"""加入房间事件,通常为通知事件"""
|
"""加入房间事件,通常为通知事件"""
|
||||||
|
|
||||||
user_id: str
|
user_id: str
|
||||||
room_id: str
|
room_id: str
|
||||||
|
|
||||||
@@ -522,8 +527,10 @@ class JoinRoomEvent(Event):
|
|||||||
def get_type(self) -> str:
|
def get_type(self) -> str:
|
||||||
return "notice"
|
return "notice"
|
||||||
|
|
||||||
|
|
||||||
class ApplyAddFriendEvent(Event):
|
class ApplyAddFriendEvent(Event):
|
||||||
"""申请添加好友事件,通常为请求事件"""
|
"""申请添加好友事件,通常为请求事件"""
|
||||||
|
|
||||||
user_id: str
|
user_id: str
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -544,6 +551,7 @@ from nonebot.utils import escape_tag
|
|||||||
from nonebot.adapters import Message as BaseMessage
|
from nonebot.adapters import Message as BaseMessage
|
||||||
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
||||||
|
|
||||||
|
|
||||||
class MessageSegment(BaseMessageSegment["Message"]):
|
class MessageSegment(BaseMessageSegment["Message"]):
|
||||||
@classmethod
|
@classmethod
|
||||||
@override
|
@override
|
||||||
@@ -591,6 +599,7 @@ class Message(BaseMessage[MessageSegment]):
|
|||||||
```python title=tests/conftest.py
|
```python title=tests/conftest.py
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import nonebot.adapters
|
import nonebot.adapters
|
||||||
|
|
||||||
nonebot.adapters.__path__.append( # type: ignore
|
nonebot.adapters.__path__.append( # type: ignore
|
||||||
str((Path(__file__).parent.parent / "nonebot" / "adapters").resolve())
|
str((Path(__file__).parent.parent / "nonebot" / "adapters").resolve())
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ weather = on_command("天气")
|
|||||||
from nonebot import on_command
|
from nonebot import on_command
|
||||||
from nonebot.rule import to_me
|
from nonebot.rule import to_me
|
||||||
|
|
||||||
weather = on_command("天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True)
|
weather = on_command(
|
||||||
|
"天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
|
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
|
||||||
|
|||||||
@@ -174,9 +174,7 @@ message = Message(
|
|||||||
# 索引
|
# 索引
|
||||||
message[0] == MessageSegment.text("test")
|
message[0] == MessageSegment.text("test")
|
||||||
# 切片
|
# 切片
|
||||||
message[0:2] == Message(
|
message[0:2] == Message([MessageSegment.text("test"), MessageSegment.markdown("test2")])
|
||||||
[MessageSegment.text("test"), MessageSegment.markdown("test2")]
|
|
||||||
)
|
|
||||||
# 类型过滤
|
# 类型过滤
|
||||||
message["markdown"] == Message(
|
message["markdown"] == Message(
|
||||||
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
||||||
@@ -262,7 +260,7 @@ msg = seg.join(
|
|||||||
MessageSegment.text("second"),
|
MessageSegment.text("second"),
|
||||||
MessageSegment.text("third"),
|
MessageSegment.text("third"),
|
||||||
]
|
]
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
msg == Message(
|
msg == Message(
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ NoneBot 默认使用 Python 的字典将事件响应器存储于内存中,但
|
|||||||
```python
|
```python
|
||||||
from nonebot.matcher import MatcherProvider
|
from nonebot.matcher import MatcherProvider
|
||||||
|
|
||||||
class CustomProvider(MatcherProvider):
|
|
||||||
...
|
class CustomProvider(MatcherProvider): ...
|
||||||
```
|
```
|
||||||
|
|
||||||
## 设置存储提供者
|
## 设置存储提供者
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ NoneBot 兼容层定义了两个数据类 `HTTPServerSetup` 和 `WebSocketServer
|
|||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.drivers import URL, Request, Response, ASGIMixin, HTTPServerSetup
|
from nonebot.drivers import URL, Request, Response, ASGIMixin, HTTPServerSetup
|
||||||
|
|
||||||
|
|
||||||
async def hello(request: Request) -> Response:
|
async def hello(request: Request) -> Response:
|
||||||
return Response(200, content="Hello, world!")
|
return Response(200, content="Hello, world!")
|
||||||
|
|
||||||
|
|
||||||
if isinstance((driver := get_driver()), ASGIMixin):
|
if isinstance((driver := get_driver()), ASGIMixin):
|
||||||
driver.setup_http_server(
|
driver.setup_http_server(
|
||||||
HTTPServerSetup(
|
HTTPServerSetup(
|
||||||
@@ -78,6 +80,7 @@ if isinstance((driver := get_driver()), ASGIMixin):
|
|||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.drivers import URL, ASGIMixin, WebSocket, WebSocketServerSetup
|
from nonebot.drivers import URL, ASGIMixin, WebSocket, WebSocketServerSetup
|
||||||
|
|
||||||
|
|
||||||
async def ws_handler(ws: WebSocket):
|
async def ws_handler(ws: WebSocket):
|
||||||
await ws.accept()
|
await ws.accept()
|
||||||
try:
|
try:
|
||||||
@@ -92,6 +95,7 @@ async def ws_handler(ws: WebSocket):
|
|||||||
await websocket.close()
|
await websocket.close()
|
||||||
# do some cleanup
|
# do some cleanup
|
||||||
|
|
||||||
|
|
||||||
if isinstance((driver := get_driver()), ASGIMixin):
|
if isinstance((driver := get_driver()), ASGIMixin):
|
||||||
driver.setup_websocket_server(
|
driver.setup_websocket_server(
|
||||||
WebSocketServerSetup(
|
WebSocketServerSetup(
|
||||||
@@ -129,6 +133,7 @@ from fastapi import FastAPI
|
|||||||
|
|
||||||
app: FastAPI = nonebot.get_app()
|
app: FastAPI = nonebot.get_app()
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api")
|
@app.get("/api")
|
||||||
async def custom_api():
|
async def custom_api():
|
||||||
return {"message": "Hello, world!"}
|
return {"message": "Hello, world!"}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
async def do_something():
|
async def do_something():
|
||||||
pass
|
pass
|
||||||
@@ -43,6 +44,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_shutdown
|
@driver.on_shutdown
|
||||||
async def do_something():
|
async def do_something():
|
||||||
pass
|
pass
|
||||||
@@ -57,6 +59,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_connect
|
@driver.on_bot_connect
|
||||||
async def do_something(bot: Bot):
|
async def do_something(bot: Bot):
|
||||||
pass
|
pass
|
||||||
@@ -71,6 +74,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_disconnect
|
@driver.on_bot_disconnect
|
||||||
async def do_something(bot: Bot):
|
async def do_something(bot: Bot):
|
||||||
pass
|
pass
|
||||||
@@ -88,6 +92,7 @@ async def do_something(bot: Bot):
|
|||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
from nonebot.message import event_preprocessor
|
from nonebot.message import event_preprocessor
|
||||||
|
|
||||||
|
|
||||||
@event_preprocessor
|
@event_preprocessor
|
||||||
async def do_something(event: Event):
|
async def do_something(event: Event):
|
||||||
if not event.is_tome():
|
if not event.is_tome():
|
||||||
@@ -101,6 +106,7 @@ async def do_something(event: Event):
|
|||||||
```python
|
```python
|
||||||
from nonebot.message import event_postprocessor
|
from nonebot.message import event_postprocessor
|
||||||
|
|
||||||
|
|
||||||
@event_postprocessor
|
@event_postprocessor
|
||||||
async def do_something(event: Event):
|
async def do_something(event: Event):
|
||||||
pass
|
pass
|
||||||
@@ -114,6 +120,7 @@ async def do_something(event: Event):
|
|||||||
from nonebot.message import run_preprocessor
|
from nonebot.message import run_preprocessor
|
||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
|
|
||||||
|
|
||||||
@run_preprocessor
|
@run_preprocessor
|
||||||
async def do_something(event: Event, matcher: Matcher):
|
async def do_something(event: Event, matcher: Matcher):
|
||||||
if not event.is_tome():
|
if not event.is_tome():
|
||||||
@@ -127,6 +134,7 @@ async def do_something(event: Event, matcher: Matcher):
|
|||||||
```python
|
```python
|
||||||
from nonebot.message import run_postprocessor
|
from nonebot.message import run_postprocessor
|
||||||
|
|
||||||
|
|
||||||
@run_postprocessor
|
@run_postprocessor
|
||||||
async def do_something(event: Event, matcher: Matcher, exception: Optional[Exception]):
|
async def do_something(event: Event, matcher: Matcher, exception: Optional[Exception]):
|
||||||
pass
|
pass
|
||||||
@@ -140,6 +148,7 @@ async def do_something(event: Event, matcher: Matcher, exception: Optional[Excep
|
|||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
from nonebot.exception import MockApiException
|
from nonebot.exception import MockApiException
|
||||||
|
|
||||||
|
|
||||||
@Bot.on_calling_api
|
@Bot.on_calling_api
|
||||||
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
||||||
if api == "send_msg":
|
if api == "send_msg":
|
||||||
@@ -154,9 +163,14 @@ async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
|||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
from nonebot.exception import MockApiException
|
from nonebot.exception import MockApiException
|
||||||
|
|
||||||
|
|
||||||
@Bot.on_called_api
|
@Bot.on_called_api
|
||||||
async def handle_api_result(
|
async def handle_api_result(
|
||||||
bot: Bot, exception: Optional[Exception], api: str, data: Dict[str, Any], result: Any
|
bot: Bot,
|
||||||
|
exception: Optional[Exception],
|
||||||
|
api: str,
|
||||||
|
data: Dict[str, Any],
|
||||||
|
result: Any,
|
||||||
):
|
):
|
||||||
if not exception and api == "send_msg":
|
if not exception and api == "send_msg":
|
||||||
raise MockApiException(result={**result, "message_id": 123})
|
raise MockApiException(result={**result, "message_id": 123})
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ options:
|
|||||||
```python {3-5}
|
```python {3-5}
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.type_updater
|
@foo.type_updater
|
||||||
async def _() -> str:
|
async def _() -> str:
|
||||||
return "notice"
|
return "notice"
|
||||||
@@ -37,6 +38,7 @@ from nonebot.permission import User
|
|||||||
|
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.permission_updater
|
@foo.permission_updater
|
||||||
async def _(event: Event, matcher: Matcher) -> Permission:
|
async def _(event: Event, matcher: Matcher) -> Permission:
|
||||||
return Permission(User.from_event(event, perm=matcher.permission))
|
return Permission(User.from_event(event, perm=matcher.permission))
|
||||||
@@ -49,6 +51,7 @@ from nonebot.permission import USER
|
|||||||
|
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.permission_updater
|
@foo.permission_updater
|
||||||
async def _(matcher: Matcher) -> Permission:
|
async def _(matcher: Matcher) -> Permission:
|
||||||
return USER("session1", "session2", perm=matcher.permission)
|
return USER("session1", "session2", perm=matcher.permission)
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ NoneBotException
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
matcher = on_notice(block=True)
|
matcher = on_notice(block=True)
|
||||||
|
|
||||||
|
|
||||||
# 或者
|
# 或者
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handler(matcher: Matcher):
|
async def handler(matcher: Matcher):
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ description: nonebot 模块
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from nonebot.adapters.console import Adapter
|
from nonebot.adapters.console import Adapter
|
||||||
|
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -142,17 +142,18 @@ description: nonebot.params 模块
|
|||||||
def depend_func() -> Any:
|
def depend_func() -> Any:
|
||||||
return ...
|
return ...
|
||||||
|
|
||||||
|
|
||||||
def depend_gen_func():
|
def depend_gen_func():
|
||||||
try:
|
try:
|
||||||
yield ...
|
yield ...
|
||||||
finally:
|
finally:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
async def handler(
|
async def handler(
|
||||||
param_name: Any = Depends(depend_func),
|
param_name: Any = Depends(depend_func),
|
||||||
gen: Any = Depends(depend_gen_func),
|
gen: Any = Depends(depend_gen_func),
|
||||||
):
|
): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## _class_ `EventParam(*args, checker=None, **kwargs)` {#EventParam}
|
## _class_ `EventParam(*args, checker=None, **kwargs)` {#EventParam}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ logger.add(
|
|||||||
level=0,
|
level=0,
|
||||||
diagnose=True,
|
diagnose=True,
|
||||||
format="<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}",
|
format="<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}",
|
||||||
filter=default_filter
|
filter=default_filter,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ options:
|
|||||||
```python {4} title=weather/__init__.py
|
```python {4} title=weather/__init__.py
|
||||||
from nonebot.adapters.console import MessageEvent
|
from nonebot.adapters.console import MessageEvent
|
||||||
|
|
||||||
|
|
||||||
@weather.got("location", prompt="请输入地名")
|
@weather.got("location", prompt="请输入地名")
|
||||||
async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
||||||
await weather.finish(f"{event.time.strftime('%Y-%m-%d')} {location} 的天气是...")
|
await weather.finish(f"{event.time.strftime('%Y-%m-%d')} {location} 的天气是...")
|
||||||
@@ -39,10 +40,12 @@ async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
|||||||
```python {4,8}
|
```python {4,8}
|
||||||
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
|
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_private(event: PrivateMessageEvent):
|
async def handle_private(event: PrivateMessageEvent):
|
||||||
await matcher.finish("私聊消息")
|
await matcher.finish("私聊消息")
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_group(event: GroupMessageEvent):
|
async def handle_group(event: GroupMessageEvent):
|
||||||
await matcher.finish("群聊消息")
|
await matcher.finish("群聊消息")
|
||||||
@@ -54,10 +57,12 @@ async def handle_group(event: GroupMessageEvent):
|
|||||||
from nonebot.adapters.console import Bot as ConsoleBot
|
from nonebot.adapters.console import Bot as ConsoleBot
|
||||||
from nonebot.adapters.onebot.v11 import Bot as OneBot
|
from nonebot.adapters.onebot.v11 import Bot as OneBot
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_console(bot: ConsoleBot):
|
async def handle_console(bot: ConsoleBot):
|
||||||
await bot.bell()
|
await bot.bell()
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_onebot(bot: OneBot):
|
async def handle_onebot(bot: OneBot):
|
||||||
await bot.send_group_message(group_id=123123, message="OneBot")
|
await bot.send_group_message(group_id=123123, message="OneBot")
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ from .config import Config
|
|||||||
|
|
||||||
plugin_config = get_plugin_config(Config)
|
plugin_config = get_plugin_config(Config)
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
weather = on_command("天气", rule=is_enable)
|
weather = on_command("天气", rule=is_enable)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -43,12 +45,15 @@ weather = on_command("天气", rule=is_enable)
|
|||||||
from nonebot.rule import Rule
|
from nonebot.rule import Rule
|
||||||
from nonebot.adapters import Event
|
from nonebot.adapters import Event
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
async def is_blacklisted(event: Event) -> bool:
|
async def is_blacklisted(event: Event) -> bool:
|
||||||
return event.get_user_id() not in BLACKLIST
|
return event.get_user_id() not in BLACKLIST
|
||||||
|
|
||||||
|
|
||||||
rule = Rule(is_enable, is_blacklisted)
|
rule = Rule(is_enable, is_blacklisted)
|
||||||
|
|
||||||
weather = on_command("天气", rule=rule)
|
weather = on_command("天气", rule=rule)
|
||||||
@@ -66,9 +71,11 @@ from .config import Config
|
|||||||
|
|
||||||
plugin_config = get_plugin_config(Config)
|
plugin_config = get_plugin_config(Config)
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
weather = on_command(
|
weather = on_command(
|
||||||
"天气",
|
"天气",
|
||||||
rule=to_me() & is_enable,
|
rule=to_me() & is_enable,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ NoneBot 中的会话状态是一个字典,可以通过类型 `T_State` 来获
|
|||||||
```python
|
```python
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
|
|
||||||
|
|
||||||
@matcher.got("key", prompt="请输入密码")
|
@matcher.got("key", prompt="请输入密码")
|
||||||
async def _(state: T_State, key: str = ArgPlainText()):
|
async def _(state: T_State, key: str = ArgPlainText()):
|
||||||
if key != "some password":
|
if key != "some password":
|
||||||
@@ -34,10 +35,12 @@ async def _(state: T_State, key: str = ArgPlainText()):
|
|||||||
```python
|
```python
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
state["key"] = "value"
|
state["key"] = "value"
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
await matcher.finish(state["key"])
|
await matcher.finish(state["key"])
|
||||||
@@ -49,10 +52,12 @@ async def _(state: T_State):
|
|||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
from nonebot.adapters import MessageTemplate
|
from nonebot.adapters import MessageTemplate
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
state["username"] = "user"
|
state["username"] = "user"
|
||||||
|
|
||||||
|
|
||||||
@matcher.got("password", prompt=MessageTemplate("请输入 {username} 的密码"))
|
@matcher.got("password", prompt=MessageTemplate("请输入 {username} 的密码"))
|
||||||
async def _():
|
async def _():
|
||||||
await matcher.finish(MessageTemplate("密码为 {password}"))
|
await matcher.finish(MessageTemplate("密码为 {password}"))
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ alc = Alconna(
|
|||||||
Args["package", str],
|
Args["package", str],
|
||||||
Option("-r|--requirement", Args["file", str]),
|
Option("-r|--requirement", Args["file", str]),
|
||||||
Option("-i|--index-url", Args["url", str]),
|
Option("-i|--index-url", Args["url", str]),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
res = alc.parse("pip install nonebot2 -i URL")
|
res = alc.parse("pip install nonebot2 -i URL")
|
||||||
@@ -383,20 +383,33 @@ alc = Alconna(..., meta=CommandMeta("foo", example="bar"))
|
|||||||
from arclet.alconna import Alconna, namespace, Namespace, Subcommand, Args, config
|
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方式创建命名空间
|
||||||
with namespace("bar") as np1:
|
with namespace("bar") as np1:
|
||||||
np1.prefixes = ["!"] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
np1.prefixes = [
|
||||||
|
"!"
|
||||||
|
] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
||||||
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
|
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
|
||||||
np1.builtin_option_name["help"] = {"帮助", "-h"} # 设置此命名空间下的命令的帮助选项名称
|
np1.builtin_option_name["help"] = {
|
||||||
|
"帮助",
|
||||||
|
"-h",
|
||||||
|
} # 设置此命名空间下的命令的帮助选项名称
|
||||||
|
|
||||||
# 你还可以使用config来管理所有命名空间并切换至任意命名空间
|
# 你还可以使用config来管理所有命名空间并切换至任意命名空间
|
||||||
config.namespaces["foo"] = ns # 将命名空间挂载到 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"命名空间
|
||||||
```
|
```
|
||||||
|
|
||||||
### 修改默认的命名空间
|
### 修改默认的命名空间
|
||||||
@@ -469,10 +482,12 @@ alc.shortcut("echo", {"command": "eval print(\\'{*}\\')"})
|
|||||||
alc.shortcut("echo", delete=True) # 删除快捷指令
|
alc.shortcut("echo", delete=True) # 删除快捷指令
|
||||||
# 'Alconna::eval 的快捷指令: "echo" 删除成功'
|
# 'Alconna::eval 的快捷指令: "echo" 删除成功'
|
||||||
|
|
||||||
|
|
||||||
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
||||||
def cb(content: str):
|
def cb(content: str):
|
||||||
eval(content, {}, {})
|
eval(content, {}, {})
|
||||||
|
|
||||||
|
|
||||||
alc.parse('eval print(\\"hello world\\")')
|
alc.parse('eval print(\\"hello world\\")')
|
||||||
# hello world
|
# hello world
|
||||||
|
|
||||||
@@ -523,7 +538,12 @@ alc.parse("eval --shortcut list")
|
|||||||
from arclet.alconna import Alconna, Option, CommandMeta, Args
|
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
|
assert alc.parse("test123 BARabc").matched
|
||||||
```
|
```
|
||||||
@@ -534,7 +554,9 @@ assert alc.parse("test123 BARabc").matched
|
|||||||
from arclet.alconna import Alconna, Option, Args, append
|
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"))
|
print(alc.parse("gcc -Fabc -Fdef -Fxyz").query[list]("flag.content"))
|
||||||
# ['abc', 'def', 'xyz']
|
# ['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 = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar")
|
||||||
alc.parse("test --comp")
|
alc.parse("test --comp")
|
||||||
|
|
||||||
'''
|
"""
|
||||||
output
|
output
|
||||||
|
|
||||||
以下是建议的输入:
|
以下是建议的输入:
|
||||||
@@ -588,7 +610,7 @@ output
|
|||||||
* --shortcut
|
* --shortcut
|
||||||
* foo
|
* foo
|
||||||
* bar
|
* bar
|
||||||
'''
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
## Duplication
|
## Duplication
|
||||||
@@ -600,7 +622,16 @@ output
|
|||||||
以pip为例,其对应的 Duplication 应如下构造:
|
以pip为例,其对应的 Duplication 应如下构造:
|
||||||
|
|
||||||
```python
|
```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):
|
class MyDup(Duplication):
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match
|
|||||||
|
|
||||||
echo = on_alconna(Alconna("echo", Args["msg", str]))
|
echo = on_alconna(Alconna("echo", Args["msg", str]))
|
||||||
|
|
||||||
|
|
||||||
@echo.handle()
|
@echo.handle()
|
||||||
async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
|
async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
|
||||||
await echo.finish(msg.result)
|
await echo.finish(msg.result)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
相比于 `on_alconna`, `funcommand` 增加了三个参数 `name`, `prefixes` 和 `description`。
|
相比于 `on_alconna`, `funcommand` 增加了三个参数 `name`, `prefixes` 和 `description`。
|
||||||
@@ -54,6 +54,7 @@ book = (
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@book.handle()
|
@book.handle()
|
||||||
async def _(arp: Arparma):
|
async def _(arp: Arparma):
|
||||||
await book.send(str(arp.options))
|
await book.send(str(arp.options))
|
||||||
|
|||||||
@@ -45,15 +45,12 @@ message = UniMessage(
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import Button, UniMessage
|
from nonebot_plugin_alconna import Button, UniMessage
|
||||||
|
|
||||||
message = (
|
message = UniMessage.text("hello world").keyboard(
|
||||||
UniMessage.text("hello world")
|
|
||||||
.keyboard(
|
|
||||||
Button("link1", url="https://example.com/1"),
|
Button("link1", url="https://example.com/1"),
|
||||||
Button("link2", url="https://example.com/2"),
|
Button("link2", url="https://example.com/2"),
|
||||||
Button("link3", url="https://example.com/3"),
|
Button("link3", url="https://example.com/3"),
|
||||||
row=3,
|
row=3,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 发送
|
### 发送
|
||||||
@@ -94,6 +91,7 @@ async def _():
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import message_recall, message_edit, message_reaction
|
from nonebot_plugin_alconna import message_recall, message_edit, message_reaction
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _():
|
async def _():
|
||||||
await message_edit(UniMessage.text("hello world"))
|
await message_edit(UniMessage.text("hello world"))
|
||||||
@@ -120,9 +118,9 @@ async def _():
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(msg: UniMsg):
|
async def _(msg: UniMsg): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
然后你可以通过 `UniMessage` 的方法来处理消息.
|
然后你可以通过 `UniMessage` 的方法来处理消息.
|
||||||
@@ -182,6 +180,7 @@ async def _(msg: UniMsg):
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(msg: UniMsg):
|
async def _(msg: UniMsg):
|
||||||
data: list[dict] = msg.dump()
|
data: list[dict] = msg.dump()
|
||||||
@@ -193,6 +192,7 @@ async def _(msg: UniMsg):
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMessage
|
from nonebot_plugin_alconna import UniMessage
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _():
|
async def _():
|
||||||
data = [
|
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 = on_alconna(Alconna("make_meme", Args["name", str]["img", Image]))
|
||||||
|
|
||||||
|
|
||||||
@meme.handle()
|
@meme.handle()
|
||||||
async def _(img: Image):
|
async def _(img: Image): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 模型定义
|
## 模型定义
|
||||||
@@ -24,6 +24,7 @@ async def _(img: Image):
|
|||||||
```python
|
```python
|
||||||
class Segment:
|
class Segment:
|
||||||
"""基类标注"""
|
"""基类标注"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> str: ...
|
def type(self) -> str: ...
|
||||||
@property
|
@property
|
||||||
@@ -31,29 +32,40 @@ class Segment:
|
|||||||
@property
|
@property
|
||||||
def children(self) -> list["Segment"]: ...
|
def children(self) -> list["Segment"]: ...
|
||||||
|
|
||||||
|
|
||||||
class Text(Segment):
|
class Text(Segment):
|
||||||
"""Text对象, 表示一类文本元素"""
|
"""Text对象, 表示一类文本元素"""
|
||||||
|
|
||||||
text: str
|
text: str
|
||||||
styles: dict[tuple[int, int], list[str]]
|
styles: dict[tuple[int, int], list[str]]
|
||||||
|
|
||||||
def cover(self, text: 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):
|
class At(Segment):
|
||||||
"""At对象, 表示一类提醒某用户的元素"""
|
"""At对象, 表示一类提醒某用户的元素"""
|
||||||
|
|
||||||
flag: Literal["user", "role", "channel"]
|
flag: Literal["user", "role", "channel"]
|
||||||
target: str
|
target: str
|
||||||
display: Optional[str]
|
display: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class AtAll(Segment):
|
class AtAll(Segment):
|
||||||
"""AtAll对象, 表示一类提醒所有人的元素"""
|
"""AtAll对象, 表示一类提醒所有人的元素"""
|
||||||
|
|
||||||
here: bool
|
here: bool
|
||||||
|
|
||||||
|
|
||||||
class Emoji(Segment):
|
class Emoji(Segment):
|
||||||
"""Emoji对象, 表示一类表情元素"""
|
"""Emoji对象, 表示一类表情元素"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
name: Optional[str]
|
name: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class Media(Segment):
|
class Media(Segment):
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
url: Optional[str]
|
url: Optional[str]
|
||||||
@@ -64,53 +76,72 @@ class Media(Segment):
|
|||||||
|
|
||||||
to_url: ClassVar[Optional[MediaToUrl]]
|
to_url: ClassVar[Optional[MediaToUrl]]
|
||||||
|
|
||||||
|
|
||||||
class Image(Media):
|
class Image(Media):
|
||||||
"""Image对象, 表示一类图片元素"""
|
"""Image对象, 表示一类图片元素"""
|
||||||
|
|
||||||
width: Optional[int]
|
width: Optional[int]
|
||||||
height: Optional[int]
|
height: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
class Audio(Media):
|
class Audio(Media):
|
||||||
"""Audio对象, 表示一类音频元素"""
|
"""Audio对象, 表示一类音频元素"""
|
||||||
|
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class Voice(Media):
|
class Voice(Media):
|
||||||
"""Voice对象, 表示一类语音元素"""
|
"""Voice对象, 表示一类语音元素"""
|
||||||
|
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class Video(Media):
|
class Video(Media):
|
||||||
"""Video对象, 表示一类视频元素"""
|
"""Video对象, 表示一类视频元素"""
|
||||||
|
|
||||||
thumbnail: Optional[Image]
|
thumbnail: Optional[Image]
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class File(Media):
|
class File(Media):
|
||||||
"""File对象, 表示一类文件元素"""
|
"""File对象, 表示一类文件元素"""
|
||||||
|
|
||||||
|
|
||||||
class Reply(Segment):
|
class Reply(Segment):
|
||||||
"""Reply对象,表示一类回复消息"""
|
"""Reply对象,表示一类回复消息"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||||
msg: Optional[Union[Message, str]]
|
msg: Optional[Union[Message, str]]
|
||||||
origin: Optional[Any]
|
origin: Optional[Any]
|
||||||
|
|
||||||
|
|
||||||
class Reference(Segment):
|
class Reference(Segment):
|
||||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||||
children: List[Union[RefNode, CustomNode]]
|
children: List[Union[RefNode, CustomNode]]
|
||||||
|
|
||||||
|
|
||||||
class Hyper(Segment):
|
class Hyper(Segment):
|
||||||
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
||||||
|
|
||||||
format: Literal["xml", "json"]
|
format: Literal["xml", "json"]
|
||||||
raw: Optional[str]
|
raw: Optional[str]
|
||||||
content: Optional[Union[dict, list]]
|
content: Optional[Union[dict, list]]
|
||||||
|
|
||||||
|
|
||||||
class Reference(Segment):
|
class Reference(Segment):
|
||||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
nodes: Sequence[Union[RefNode, CustomNode]]
|
nodes: Sequence[Union[RefNode, CustomNode]]
|
||||||
|
|
||||||
|
|
||||||
class Button(Segment):
|
class Button(Segment):
|
||||||
"""Button对象,表示一类按钮消息"""
|
"""Button对象,表示一类按钮消息"""
|
||||||
|
|
||||||
flag: Literal["action", "link", "input", "enter"]
|
flag: Literal["action", "link", "input", "enter"]
|
||||||
"""
|
"""
|
||||||
- 点击 action 类型的按钮时会触发一个关于 按钮回调 事件,该事件的 button 资源会包含上述 id
|
- 点击 action 类型的按钮时会触发一个关于 按钮回调 事件,该事件的 button 资源会包含上述 id
|
||||||
@@ -138,20 +169,26 @@ class Button(Segment):
|
|||||||
- list[At]: 指定用户/身份组可操作
|
- list[At]: 指定用户/身份组可操作
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Keyboard(Segment):
|
class Keyboard(Segment):
|
||||||
"""Keyboard对象,表示一行按钮元素"""
|
"""Keyboard对象,表示一行按钮元素"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
"""此处一般用来表示模板id,特殊情况下可能表示例如 bot_appid 等"""
|
"""此处一般用来表示模板id,特殊情况下可能表示例如 bot_appid 等"""
|
||||||
buttons: Optional[list[Button]]
|
buttons: Optional[list[Button]]
|
||||||
row: Optional[int]
|
row: Optional[int]
|
||||||
"""当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数"""
|
"""当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数"""
|
||||||
|
|
||||||
|
|
||||||
class Other(Segment):
|
class Other(Segment):
|
||||||
"""其他 Segment"""
|
"""其他 Segment"""
|
||||||
|
|
||||||
origin: MessageSegment
|
origin: MessageSegment
|
||||||
|
|
||||||
|
|
||||||
class I18n(Segment):
|
class I18n(Segment):
|
||||||
"""特殊的 Segment,用于 i18n 消息"""
|
"""特殊的 Segment,用于 i18n 消息"""
|
||||||
|
|
||||||
item_or_scope: Union[LangItem, str]
|
item_or_scope: Union[LangItem, str]
|
||||||
type_: Optional[str] = None
|
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
|
from nonebot_plugin_alconna.builtins.uniseg.market_face import MarketFace
|
||||||
|
|
||||||
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 Image 的元素选出来并将第一个作为结果
|
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 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
|
# 表示这个指令需要的图片要么直接是 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#嵌套提取)
|
也可以参考通用消息的 [`嵌套提取`](./message.mdx#嵌套提取)
|
||||||
@@ -213,10 +254,13 @@ def mfbuild(builder: MessageBuilder, seg: BaseMessageSegment):
|
|||||||
|
|
||||||
|
|
||||||
@custom_handler(MarketFace)
|
@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:
|
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 的处理方法。
|
具体而言,你可以使用 `custom_register` 来增加一个从 MessageSegment 到 Segment 的处理方法;使用 `custom_handler` 来增加一个从 Segment 到 MessageSegment 的处理方法。
|
||||||
|
|||||||
@@ -155,7 +155,9 @@ op.create_table( # CREATE TABLE
|
|||||||
"weather_weather", # weather_weather
|
"weather_weather", # weather_weather
|
||||||
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
||||||
sa.Column("weather", sa.String(), nullable=False), # weather 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"},
|
info={"bind_key": "weather"},
|
||||||
)
|
)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
@@ -245,7 +247,9 @@ from nonebot.typing import T_State
|
|||||||
|
|
||||||
|
|
||||||
@weather.got("location", prompt="请输入地名")
|
@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)
|
wea = await session.get(Weather, location)
|
||||||
|
|
||||||
if not wea:
|
if not wea:
|
||||||
@@ -348,13 +352,16 @@ async def _(
|
|||||||
```python title=weather/__init__.py {5} showLineNumbers
|
```python title=weather/__init__.py {5} showLineNumbers
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
@weather.handle()
|
@weather.handle()
|
||||||
async def _(
|
async def _(
|
||||||
weas: Sequence[Weather] = SQLDepends(
|
weas: Sequence[Weather] = SQLDepends(
|
||||||
select(Weather).where(Weather.weather == Depends(extract_arg_plain_text))
|
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)。
|
支持的类型标注请参见 [依赖注入](dependency)。
|
||||||
@@ -364,6 +371,7 @@ async def _(
|
|||||||
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
class Weather(Model):
|
class Weather(Model):
|
||||||
location: Mapped[str] = mapped_column(primary_key=True)
|
location: Mapped[str] = mapped_column(primary_key=True)
|
||||||
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
||||||
|
|||||||
@@ -78,8 +78,7 @@ async def html_to_pic(
|
|||||||
img_fetch_fn: ImgFetchFn = combined_img_fetcher,
|
img_fetch_fn: ImgFetchFn = combined_img_fetcher,
|
||||||
css_fetch_fn: CSSFetchFn = combined_css_fetcher,
|
css_fetch_fn: CSSFetchFn = combined_css_fetcher,
|
||||||
urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin,
|
urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
最核心的渲染函数。
|
最核心的渲染函数。
|
||||||
@@ -107,8 +106,7 @@ async def text_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可用于渲染多行文本。
|
可用于渲染多行文本。
|
||||||
@@ -128,8 +126,7 @@ async def md_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。
|
可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。
|
||||||
@@ -153,8 +150,7 @@ async def template_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
渲染 jinja2 模板。
|
渲染 jinja2 模板。
|
||||||
|
|||||||
@@ -44,15 +44,18 @@ require("nonebot_plugin_apscheduler")
|
|||||||
|
|
||||||
from nonebot_plugin_apscheduler import scheduler
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
|
||||||
|
|
||||||
# 基于装饰器的方式
|
# 基于装饰器的方式
|
||||||
@scheduler.scheduled_job("cron", hour="*/2", id="job_0", args=[1], kwargs={arg2: 2})
|
@scheduler.scheduled_job("cron", hour="*/2", id="job_0", args=[1], kwargs={arg2: 2})
|
||||||
async def run_every_2_hour(arg1: int, arg2: int):
|
async def run_every_2_hour(arg1: int, arg2: int):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# 基于 add_job 方法的方式
|
# 基于 add_job 方法的方式
|
||||||
def run_every_day(arg1: int, arg2: int):
|
def run_every_day(arg1: int, arg2: int):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
run_every_day, "interval", days=1, id="job_1", args=[1], kwargs={arg2: 2}
|
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
|
```python {5,6} title=tests/test_http_server.py
|
||||||
from nonebug import App
|
from nonebug import App
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_http_server(app: App):
|
async def test_http_server(app: App):
|
||||||
async with app.test_server() as ctx:
|
async with app.test_server() as ctx:
|
||||||
@@ -45,6 +46,7 @@ import nonebot
|
|||||||
from nonebug import App
|
from nonebug import App
|
||||||
from nonebot.adapters.fake import Adapter
|
from nonebot.adapters.fake import Adapter
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_http_server(app: App):
|
async def test_http_server(app: App):
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
@@ -72,6 +74,7 @@ import nonebot
|
|||||||
from nonebug import App
|
from nonebug import App
|
||||||
from nonebot.adapters.fake import Adapter
|
from nonebot.adapters.fake import Adapter
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_ws_server(app: App):
|
async def test_ws_server(app: App):
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ except Exception as e:
|
|||||||
```python title=config.py
|
```python title=config.py
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseModel):
|
class Config(BaseModel):
|
||||||
xxx_id: str
|
xxx_id: str
|
||||||
xxx_token: str
|
xxx_token: str
|
||||||
@@ -102,6 +103,7 @@ from nonebot.adapters import Adapter as BaseAdapter
|
|||||||
|
|
||||||
from .config import Config
|
from .config import Config
|
||||||
|
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
def __init__(self, driver: Driver, **kwargs: Any):
|
def __init__(self, driver: Driver, **kwargs: Any):
|
||||||
@@ -208,9 +210,10 @@ from nonebot.drivers import (
|
|||||||
ASGIMixin,
|
ASGIMixin,
|
||||||
WebSocket,
|
WebSocket,
|
||||||
HTTPServerSetup,
|
HTTPServerSetup,
|
||||||
WebSocketServerSetup
|
WebSocketServerSetup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
def __init__(self, driver: Driver, **kwargs: Any):
|
def __init__(self, driver: Driver, **kwargs: Any):
|
||||||
@@ -242,7 +245,6 @@ class Adapter(BaseAdapter):
|
|||||||
)
|
)
|
||||||
self.setup_websocket_server(ws_setup)
|
self.setup_websocket_server(ws_setup)
|
||||||
|
|
||||||
|
|
||||||
async def _handle_http(self, request: Request) -> Response:
|
async def _handle_http(self, request: Request) -> Response:
|
||||||
"""HTTP 路由处理函数,只有一个类型为 Request 的参数,且返回值类型为 Response"""
|
"""HTTP 路由处理函数,只有一个类型为 Request 的参数,且返回值类型为 Response"""
|
||||||
...
|
...
|
||||||
@@ -270,8 +272,8 @@ class Adapter(BaseAdapter):
|
|||||||
```python {7,8,11} title=adapter.py
|
```python {7,8,11} title=adapter.py
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
def _handle_connect(self):
|
def _handle_connect(self):
|
||||||
bot_id = ... # 通过配置或者平台 API 等方式,获取到 Bot 的 ID
|
bot_id = ... # 通过配置或者平台 API 等方式,获取到 Bot 的 ID
|
||||||
bot = Bot(self, self_id=bot_id) # 实例化 Bot
|
bot = Bot(self, self_id=bot_id) # 实例化 Bot
|
||||||
@@ -295,8 +297,8 @@ from .bot import Bot
|
|||||||
from .event import Event
|
from .event import Event
|
||||||
from .log import log
|
from .log import log
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
@classmethod
|
@classmethod
|
||||||
def payload_to_event(cls, payload: Dict[str, Any]) -> Event:
|
def payload_to_event(cls, payload: Dict[str, Any]) -> Event:
|
||||||
"""根据平台事件的特性,转换平台 payload 为具体 Event
|
"""根据平台事件的特性,转换平台 payload 为具体 Event
|
||||||
@@ -316,7 +318,6 @@ class Adapter(BaseAdapter):
|
|||||||
# 也可以尝试转为基础 Event 进行处理
|
# 也可以尝试转为基础 Event 进行处理
|
||||||
return type_validate_python(Event, payload)
|
return type_validate_python(Event, payload)
|
||||||
|
|
||||||
|
|
||||||
async def _forward(self, bot: Bot):
|
async def _forward(self, bot: Bot):
|
||||||
|
|
||||||
payload: Dict[str, Any] # 接收到的事件数据
|
payload: Dict[str, Any] # 接收到的事件数据
|
||||||
@@ -337,8 +338,8 @@ from nonebot.drivers import Request, WebSocket
|
|||||||
|
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
|
async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
|
||||||
log("DEBUG", f"Calling API <y>{api}</y>") # 给予日志提示
|
log("DEBUG", f"Calling API <y>{api}</y>") # 给予日志提示
|
||||||
@@ -356,7 +357,6 @@ class Adapter(BaseAdapter):
|
|||||||
# 发送请求,返回结果
|
# 发送请求,返回结果
|
||||||
return await self.driver.request(request)
|
return await self.driver.request(request)
|
||||||
|
|
||||||
|
|
||||||
# 采用 WebSocket 通信的方式,可以直接调用 send 方法发送数据
|
# 采用 WebSocket 通信的方式,可以直接调用 send 方法发送数据
|
||||||
# 通过某种方式获取到 bot 对应的 websocket 对象
|
# 通过某种方式获取到 bot 对应的 websocket 对象
|
||||||
ws: WebSocket = your_get_websocket_method(bot.self_id)
|
ws: WebSocket = your_get_websocket_method(bot.self_id)
|
||||||
@@ -450,8 +450,8 @@ from typing_extensions import override
|
|||||||
from nonebot.compat import model_dump
|
from nonebot.compat import model_dump
|
||||||
from nonebot.adapters import Event as BaseEvent
|
from nonebot.adapters import Event as BaseEvent
|
||||||
|
|
||||||
class Event(BaseEvent):
|
|
||||||
|
|
||||||
|
class Event(BaseEvent):
|
||||||
@override
|
@override
|
||||||
def get_event_name(self) -> str:
|
def get_event_name(self) -> str:
|
||||||
# 返回事件的名称,用于日志打印
|
# 返回事件的名称,用于日志打印
|
||||||
@@ -488,6 +488,7 @@ class Event(BaseEvent):
|
|||||||
```python {7,16,20,25,34,42} title=event.py
|
```python {7,16,20,25,34,42} title=event.py
|
||||||
from .message import Message
|
from .message import Message
|
||||||
|
|
||||||
|
|
||||||
class HeartbeatEvent(Event):
|
class HeartbeatEvent(Event):
|
||||||
"""心跳时间,通常为元事件"""
|
"""心跳时间,通常为元事件"""
|
||||||
|
|
||||||
@@ -495,8 +496,10 @@ class HeartbeatEvent(Event):
|
|||||||
def get_type(self) -> str:
|
def get_type(self) -> str:
|
||||||
return "meta_event"
|
return "meta_event"
|
||||||
|
|
||||||
|
|
||||||
class MessageEvent(Event):
|
class MessageEvent(Event):
|
||||||
"""消息事件"""
|
"""消息事件"""
|
||||||
|
|
||||||
message_id: str
|
message_id: str
|
||||||
user_id: str
|
user_id: str
|
||||||
|
|
||||||
@@ -513,8 +516,10 @@ class MessageEvent(Event):
|
|||||||
def get_user_id(self) -> str:
|
def get_user_id(self) -> str:
|
||||||
return self.user_id
|
return self.user_id
|
||||||
|
|
||||||
|
|
||||||
class JoinRoomEvent(Event):
|
class JoinRoomEvent(Event):
|
||||||
"""加入房间事件,通常为通知事件"""
|
"""加入房间事件,通常为通知事件"""
|
||||||
|
|
||||||
user_id: str
|
user_id: str
|
||||||
room_id: str
|
room_id: str
|
||||||
|
|
||||||
@@ -522,8 +527,10 @@ class JoinRoomEvent(Event):
|
|||||||
def get_type(self) -> str:
|
def get_type(self) -> str:
|
||||||
return "notice"
|
return "notice"
|
||||||
|
|
||||||
|
|
||||||
class ApplyAddFriendEvent(Event):
|
class ApplyAddFriendEvent(Event):
|
||||||
"""申请添加好友事件,通常为请求事件"""
|
"""申请添加好友事件,通常为请求事件"""
|
||||||
|
|
||||||
user_id: str
|
user_id: str
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -544,6 +551,7 @@ from nonebot.utils import escape_tag
|
|||||||
from nonebot.adapters import Message as BaseMessage
|
from nonebot.adapters import Message as BaseMessage
|
||||||
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
||||||
|
|
||||||
|
|
||||||
class MessageSegment(BaseMessageSegment["Message"]):
|
class MessageSegment(BaseMessageSegment["Message"]):
|
||||||
@classmethod
|
@classmethod
|
||||||
@override
|
@override
|
||||||
@@ -591,6 +599,7 @@ class Message(BaseMessage[MessageSegment]):
|
|||||||
```python title=tests/conftest.py
|
```python title=tests/conftest.py
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import nonebot.adapters
|
import nonebot.adapters
|
||||||
|
|
||||||
nonebot.adapters.__path__.append( # type: ignore
|
nonebot.adapters.__path__.append( # type: ignore
|
||||||
str((Path(__file__).parent.parent / "nonebot" / "adapters").resolve())
|
str((Path(__file__).parent.parent / "nonebot" / "adapters").resolve())
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ weather = on_command("天气")
|
|||||||
from nonebot import on_command
|
from nonebot import on_command
|
||||||
from nonebot.rule import to_me
|
from nonebot.rule import to_me
|
||||||
|
|
||||||
weather = on_command("天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True)
|
weather = on_command(
|
||||||
|
"天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
|
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
|
||||||
|
|||||||
@@ -174,9 +174,7 @@ message = Message(
|
|||||||
# 索引
|
# 索引
|
||||||
message[0] == MessageSegment.text("test")
|
message[0] == MessageSegment.text("test")
|
||||||
# 切片
|
# 切片
|
||||||
message[0:2] == Message(
|
message[0:2] == Message([MessageSegment.text("test"), MessageSegment.markdown("test2")])
|
||||||
[MessageSegment.text("test"), MessageSegment.markdown("test2")]
|
|
||||||
)
|
|
||||||
# 类型过滤
|
# 类型过滤
|
||||||
message["markdown"] == Message(
|
message["markdown"] == Message(
|
||||||
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
||||||
@@ -262,7 +260,7 @@ msg = seg.join(
|
|||||||
MessageSegment.text("second"),
|
MessageSegment.text("second"),
|
||||||
MessageSegment.text("third"),
|
MessageSegment.text("third"),
|
||||||
]
|
]
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
msg == Message(
|
msg == Message(
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ NoneBot 默认使用 Python 的字典将事件响应器存储于内存中,但
|
|||||||
```python
|
```python
|
||||||
from nonebot.matcher import MatcherProvider
|
from nonebot.matcher import MatcherProvider
|
||||||
|
|
||||||
class CustomProvider(MatcherProvider):
|
|
||||||
...
|
class CustomProvider(MatcherProvider): ...
|
||||||
```
|
```
|
||||||
|
|
||||||
## 设置存储提供者
|
## 设置存储提供者
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ NoneBot 兼容层定义了两个数据类 `HTTPServerSetup` 和 `WebSocketServer
|
|||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.drivers import URL, Request, Response, ASGIMixin, HTTPServerSetup
|
from nonebot.drivers import URL, Request, Response, ASGIMixin, HTTPServerSetup
|
||||||
|
|
||||||
|
|
||||||
async def hello(request: Request) -> Response:
|
async def hello(request: Request) -> Response:
|
||||||
return Response(200, content="Hello, world!")
|
return Response(200, content="Hello, world!")
|
||||||
|
|
||||||
|
|
||||||
if isinstance((driver := get_driver()), ASGIMixin):
|
if isinstance((driver := get_driver()), ASGIMixin):
|
||||||
driver.setup_http_server(
|
driver.setup_http_server(
|
||||||
HTTPServerSetup(
|
HTTPServerSetup(
|
||||||
@@ -78,6 +80,7 @@ if isinstance((driver := get_driver()), ASGIMixin):
|
|||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
from nonebot.drivers import URL, ASGIMixin, WebSocket, WebSocketServerSetup
|
from nonebot.drivers import URL, ASGIMixin, WebSocket, WebSocketServerSetup
|
||||||
|
|
||||||
|
|
||||||
async def ws_handler(ws: WebSocket):
|
async def ws_handler(ws: WebSocket):
|
||||||
await ws.accept()
|
await ws.accept()
|
||||||
try:
|
try:
|
||||||
@@ -92,6 +95,7 @@ async def ws_handler(ws: WebSocket):
|
|||||||
await websocket.close()
|
await websocket.close()
|
||||||
# do some cleanup
|
# do some cleanup
|
||||||
|
|
||||||
|
|
||||||
if isinstance((driver := get_driver()), ASGIMixin):
|
if isinstance((driver := get_driver()), ASGIMixin):
|
||||||
driver.setup_websocket_server(
|
driver.setup_websocket_server(
|
||||||
WebSocketServerSetup(
|
WebSocketServerSetup(
|
||||||
@@ -129,6 +133,7 @@ from fastapi import FastAPI
|
|||||||
|
|
||||||
app: FastAPI = nonebot.get_app()
|
app: FastAPI = nonebot.get_app()
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api")
|
@app.get("/api")
|
||||||
async def custom_api():
|
async def custom_api():
|
||||||
return {"message": "Hello, world!"}
|
return {"message": "Hello, world!"}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
async def do_something():
|
async def do_something():
|
||||||
pass
|
pass
|
||||||
@@ -43,6 +44,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_shutdown
|
@driver.on_shutdown
|
||||||
async def do_something():
|
async def do_something():
|
||||||
pass
|
pass
|
||||||
@@ -57,6 +59,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_connect
|
@driver.on_bot_connect
|
||||||
async def do_something(bot: Bot):
|
async def do_something(bot: Bot):
|
||||||
pass
|
pass
|
||||||
@@ -71,6 +74,7 @@ from nonebot import get_driver
|
|||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
|
|
||||||
|
|
||||||
@driver.on_bot_disconnect
|
@driver.on_bot_disconnect
|
||||||
async def do_something(bot: Bot):
|
async def do_something(bot: Bot):
|
||||||
pass
|
pass
|
||||||
@@ -88,6 +92,7 @@ async def do_something(bot: Bot):
|
|||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
from nonebot.message import event_preprocessor
|
from nonebot.message import event_preprocessor
|
||||||
|
|
||||||
|
|
||||||
@event_preprocessor
|
@event_preprocessor
|
||||||
async def do_something(event: Event):
|
async def do_something(event: Event):
|
||||||
if not event.is_tome():
|
if not event.is_tome():
|
||||||
@@ -101,6 +106,7 @@ async def do_something(event: Event):
|
|||||||
```python
|
```python
|
||||||
from nonebot.message import event_postprocessor
|
from nonebot.message import event_postprocessor
|
||||||
|
|
||||||
|
|
||||||
@event_postprocessor
|
@event_postprocessor
|
||||||
async def do_something(event: Event):
|
async def do_something(event: Event):
|
||||||
pass
|
pass
|
||||||
@@ -114,6 +120,7 @@ async def do_something(event: Event):
|
|||||||
from nonebot.message import run_preprocessor
|
from nonebot.message import run_preprocessor
|
||||||
from nonebot.exception import IgnoredException
|
from nonebot.exception import IgnoredException
|
||||||
|
|
||||||
|
|
||||||
@run_preprocessor
|
@run_preprocessor
|
||||||
async def do_something(event: Event, matcher: Matcher):
|
async def do_something(event: Event, matcher: Matcher):
|
||||||
if not event.is_tome():
|
if not event.is_tome():
|
||||||
@@ -127,6 +134,7 @@ async def do_something(event: Event, matcher: Matcher):
|
|||||||
```python
|
```python
|
||||||
from nonebot.message import run_postprocessor
|
from nonebot.message import run_postprocessor
|
||||||
|
|
||||||
|
|
||||||
@run_postprocessor
|
@run_postprocessor
|
||||||
async def do_something(event: Event, matcher: Matcher, exception: Optional[Exception]):
|
async def do_something(event: Event, matcher: Matcher, exception: Optional[Exception]):
|
||||||
pass
|
pass
|
||||||
@@ -140,6 +148,7 @@ async def do_something(event: Event, matcher: Matcher, exception: Optional[Excep
|
|||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
from nonebot.exception import MockApiException
|
from nonebot.exception import MockApiException
|
||||||
|
|
||||||
|
|
||||||
@Bot.on_calling_api
|
@Bot.on_calling_api
|
||||||
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
||||||
if api == "send_msg":
|
if api == "send_msg":
|
||||||
@@ -154,9 +163,14 @@ async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
|
|||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
from nonebot.exception import MockApiException
|
from nonebot.exception import MockApiException
|
||||||
|
|
||||||
|
|
||||||
@Bot.on_called_api
|
@Bot.on_called_api
|
||||||
async def handle_api_result(
|
async def handle_api_result(
|
||||||
bot: Bot, exception: Optional[Exception], api: str, data: Dict[str, Any], result: Any
|
bot: Bot,
|
||||||
|
exception: Optional[Exception],
|
||||||
|
api: str,
|
||||||
|
data: Dict[str, Any],
|
||||||
|
result: Any,
|
||||||
):
|
):
|
||||||
if not exception and api == "send_msg":
|
if not exception and api == "send_msg":
|
||||||
raise MockApiException(result={**result, "message_id": 123})
|
raise MockApiException(result={**result, "message_id": 123})
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ options:
|
|||||||
```python {3-5}
|
```python {3-5}
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.type_updater
|
@foo.type_updater
|
||||||
async def _() -> str:
|
async def _() -> str:
|
||||||
return "notice"
|
return "notice"
|
||||||
@@ -37,6 +38,7 @@ from nonebot.permission import User
|
|||||||
|
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.permission_updater
|
@foo.permission_updater
|
||||||
async def _(event: Event, matcher: Matcher) -> Permission:
|
async def _(event: Event, matcher: Matcher) -> Permission:
|
||||||
return Permission(User.from_event(event, perm=matcher.permission))
|
return Permission(User.from_event(event, perm=matcher.permission))
|
||||||
@@ -49,6 +51,7 @@ from nonebot.permission import USER
|
|||||||
|
|
||||||
foo = on_message()
|
foo = on_message()
|
||||||
|
|
||||||
|
|
||||||
@foo.permission_updater
|
@foo.permission_updater
|
||||||
async def _(matcher: Matcher) -> Permission:
|
async def _(matcher: Matcher) -> Permission:
|
||||||
return USER("session1", "session2", perm=matcher.permission)
|
return USER("session1", "session2", perm=matcher.permission)
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ NoneBotException
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
matcher = on_notice(block=True)
|
matcher = on_notice(block=True)
|
||||||
|
|
||||||
|
|
||||||
# 或者
|
# 或者
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handler(matcher: Matcher):
|
async def handler(matcher: Matcher):
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ description: nonebot 模块
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from nonebot.adapters.console import Adapter
|
from nonebot.adapters.console import Adapter
|
||||||
|
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -142,17 +142,18 @@ description: nonebot.params 模块
|
|||||||
def depend_func() -> Any:
|
def depend_func() -> Any:
|
||||||
return ...
|
return ...
|
||||||
|
|
||||||
|
|
||||||
def depend_gen_func():
|
def depend_gen_func():
|
||||||
try:
|
try:
|
||||||
yield ...
|
yield ...
|
||||||
finally:
|
finally:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
async def handler(
|
async def handler(
|
||||||
param_name: Any = Depends(depend_func),
|
param_name: Any = Depends(depend_func),
|
||||||
gen: Any = Depends(depend_gen_func),
|
gen: Any = Depends(depend_gen_func),
|
||||||
):
|
): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## _class_ `EventParam(*args, checker=None, **kwargs)` {#EventParam}
|
## _class_ `EventParam(*args, checker=None, **kwargs)` {#EventParam}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ logger.add(
|
|||||||
level=0,
|
level=0,
|
||||||
diagnose=True,
|
diagnose=True,
|
||||||
format="<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}",
|
format="<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}",
|
||||||
filter=default_filter
|
filter=default_filter,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ options:
|
|||||||
```python {4} title=weather/__init__.py
|
```python {4} title=weather/__init__.py
|
||||||
from nonebot.adapters.console import MessageEvent
|
from nonebot.adapters.console import MessageEvent
|
||||||
|
|
||||||
|
|
||||||
@weather.got("location", prompt="请输入地名")
|
@weather.got("location", prompt="请输入地名")
|
||||||
async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
||||||
await weather.finish(f"{event.time.strftime('%Y-%m-%d')} {location} 的天气是...")
|
await weather.finish(f"{event.time.strftime('%Y-%m-%d')} {location} 的天气是...")
|
||||||
@@ -39,10 +40,12 @@ async def got_location(event: MessageEvent, location: str = ArgPlainText()):
|
|||||||
```python {4,8}
|
```python {4,8}
|
||||||
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
|
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_private(event: PrivateMessageEvent):
|
async def handle_private(event: PrivateMessageEvent):
|
||||||
await matcher.finish("私聊消息")
|
await matcher.finish("私聊消息")
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_group(event: GroupMessageEvent):
|
async def handle_group(event: GroupMessageEvent):
|
||||||
await matcher.finish("群聊消息")
|
await matcher.finish("群聊消息")
|
||||||
@@ -54,10 +57,12 @@ async def handle_group(event: GroupMessageEvent):
|
|||||||
from nonebot.adapters.console import Bot as ConsoleBot
|
from nonebot.adapters.console import Bot as ConsoleBot
|
||||||
from nonebot.adapters.onebot.v11 import Bot as OneBot
|
from nonebot.adapters.onebot.v11 import Bot as OneBot
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_console(bot: ConsoleBot):
|
async def handle_console(bot: ConsoleBot):
|
||||||
await bot.bell()
|
await bot.bell()
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def handle_onebot(bot: OneBot):
|
async def handle_onebot(bot: OneBot):
|
||||||
await bot.send_group_message(group_id=123123, message="OneBot")
|
await bot.send_group_message(group_id=123123, message="OneBot")
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ from .config import Config
|
|||||||
|
|
||||||
plugin_config = get_plugin_config(Config)
|
plugin_config = get_plugin_config(Config)
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
weather = on_command("天气", rule=is_enable)
|
weather = on_command("天气", rule=is_enable)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -43,12 +45,15 @@ weather = on_command("天气", rule=is_enable)
|
|||||||
from nonebot.rule import Rule
|
from nonebot.rule import Rule
|
||||||
from nonebot.adapters import Event
|
from nonebot.adapters import Event
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
async def is_blacklisted(event: Event) -> bool:
|
async def is_blacklisted(event: Event) -> bool:
|
||||||
return event.get_user_id() not in BLACKLIST
|
return event.get_user_id() not in BLACKLIST
|
||||||
|
|
||||||
|
|
||||||
rule = Rule(is_enable, is_blacklisted)
|
rule = Rule(is_enable, is_blacklisted)
|
||||||
|
|
||||||
weather = on_command("天气", rule=rule)
|
weather = on_command("天气", rule=rule)
|
||||||
@@ -66,9 +71,11 @@ from .config import Config
|
|||||||
|
|
||||||
plugin_config = get_plugin_config(Config)
|
plugin_config = get_plugin_config(Config)
|
||||||
|
|
||||||
|
|
||||||
async def is_enable() -> bool:
|
async def is_enable() -> bool:
|
||||||
return plugin_config.weather_plugin_enabled
|
return plugin_config.weather_plugin_enabled
|
||||||
|
|
||||||
|
|
||||||
weather = on_command(
|
weather = on_command(
|
||||||
"天气",
|
"天气",
|
||||||
rule=to_me() & is_enable,
|
rule=to_me() & is_enable,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ NoneBot 中的会话状态是一个字典,可以通过类型 `T_State` 来获
|
|||||||
```python
|
```python
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
|
|
||||||
|
|
||||||
@matcher.got("key", prompt="请输入密码")
|
@matcher.got("key", prompt="请输入密码")
|
||||||
async def _(state: T_State, key: str = ArgPlainText()):
|
async def _(state: T_State, key: str = ArgPlainText()):
|
||||||
if key != "some password":
|
if key != "some password":
|
||||||
@@ -34,10 +35,12 @@ async def _(state: T_State, key: str = ArgPlainText()):
|
|||||||
```python
|
```python
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
state["key"] = "value"
|
state["key"] = "value"
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
await matcher.finish(state["key"])
|
await matcher.finish(state["key"])
|
||||||
@@ -49,10 +52,12 @@ async def _(state: T_State):
|
|||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
from nonebot.adapters import MessageTemplate
|
from nonebot.adapters import MessageTemplate
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(state: T_State):
|
async def _(state: T_State):
|
||||||
state["username"] = "user"
|
state["username"] = "user"
|
||||||
|
|
||||||
|
|
||||||
@matcher.got("password", prompt=MessageTemplate("请输入 {username} 的密码"))
|
@matcher.got("password", prompt=MessageTemplate("请输入 {username} 的密码"))
|
||||||
async def _():
|
async def _():
|
||||||
await matcher.finish(MessageTemplate("密码为 {password}"))
|
await matcher.finish(MessageTemplate("密码为 {password}"))
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ alc = Alconna(
|
|||||||
Args["package", str],
|
Args["package", str],
|
||||||
Option("-r|--requirement", Args["file", str]),
|
Option("-r|--requirement", Args["file", str]),
|
||||||
Option("-i|--index-url", Args["url", str]),
|
Option("-i|--index-url", Args["url", str]),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
res = alc.parse("pip install nonebot2 -i URL")
|
res = alc.parse("pip install nonebot2 -i URL")
|
||||||
@@ -383,20 +383,33 @@ alc = Alconna(..., meta=CommandMeta("foo", example="bar"))
|
|||||||
from arclet.alconna import Alconna, namespace, Namespace, Subcommand, Args, config
|
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方式创建命名空间
|
||||||
with namespace("bar") as np1:
|
with namespace("bar") as np1:
|
||||||
np1.prefixes = ["!"] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
np1.prefixes = [
|
||||||
|
"!"
|
||||||
|
] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
|
||||||
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
|
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
|
||||||
np1.builtin_option_name["help"] = {"帮助", "-h"} # 设置此命名空间下的命令的帮助选项名称
|
np1.builtin_option_name["help"] = {
|
||||||
|
"帮助",
|
||||||
|
"-h",
|
||||||
|
} # 设置此命名空间下的命令的帮助选项名称
|
||||||
|
|
||||||
# 你还可以使用config来管理所有命名空间并切换至任意命名空间
|
# 你还可以使用config来管理所有命名空间并切换至任意命名空间
|
||||||
config.namespaces["foo"] = ns # 将命名空间挂载到 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"命名空间
|
||||||
```
|
```
|
||||||
|
|
||||||
### 修改默认的命名空间
|
### 修改默认的命名空间
|
||||||
@@ -469,10 +482,12 @@ alc.shortcut("echo", {"command": "eval print(\\'{*}\\')"})
|
|||||||
alc.shortcut("echo", delete=True) # 删除快捷指令
|
alc.shortcut("echo", delete=True) # 删除快捷指令
|
||||||
# 'Alconna::eval 的快捷指令: "echo" 删除成功'
|
# 'Alconna::eval 的快捷指令: "echo" 删除成功'
|
||||||
|
|
||||||
|
|
||||||
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
@alc.bind() # 绑定一个命令执行器, 若匹配成功则会传入参数, 自动执行命令执行器
|
||||||
def cb(content: str):
|
def cb(content: str):
|
||||||
eval(content, {}, {})
|
eval(content, {}, {})
|
||||||
|
|
||||||
|
|
||||||
alc.parse('eval print(\\"hello world\\")')
|
alc.parse('eval print(\\"hello world\\")')
|
||||||
# hello world
|
# hello world
|
||||||
|
|
||||||
@@ -523,7 +538,12 @@ alc.parse("eval --shortcut list")
|
|||||||
from arclet.alconna import Alconna, Option, CommandMeta, Args
|
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
|
assert alc.parse("test123 BARabc").matched
|
||||||
```
|
```
|
||||||
@@ -534,7 +554,9 @@ assert alc.parse("test123 BARabc").matched
|
|||||||
from arclet.alconna import Alconna, Option, Args, append
|
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"))
|
print(alc.parse("gcc -Fabc -Fdef -Fxyz").query[list]("flag.content"))
|
||||||
# ['abc', 'def', 'xyz']
|
# ['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 = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar")
|
||||||
alc.parse("test --comp")
|
alc.parse("test --comp")
|
||||||
|
|
||||||
'''
|
"""
|
||||||
output
|
output
|
||||||
|
|
||||||
以下是建议的输入:
|
以下是建议的输入:
|
||||||
@@ -588,7 +610,7 @@ output
|
|||||||
* --shortcut
|
* --shortcut
|
||||||
* foo
|
* foo
|
||||||
* bar
|
* bar
|
||||||
'''
|
"""
|
||||||
```
|
```
|
||||||
|
|
||||||
## Duplication
|
## Duplication
|
||||||
@@ -600,7 +622,16 @@ output
|
|||||||
以pip为例,其对应的 Duplication 应如下构造:
|
以pip为例,其对应的 Duplication 应如下构造:
|
||||||
|
|
||||||
```python
|
```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):
|
class MyDup(Duplication):
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match
|
|||||||
|
|
||||||
echo = on_alconna(Alconna("echo", Args["msg", str]))
|
echo = on_alconna(Alconna("echo", Args["msg", str]))
|
||||||
|
|
||||||
|
|
||||||
@echo.handle()
|
@echo.handle()
|
||||||
async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
|
async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
|
||||||
await echo.finish(msg.result)
|
await echo.finish(msg.result)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
相比于 `on_alconna`, `funcommand` 增加了三个参数 `name`, `prefixes` 和 `description`。
|
相比于 `on_alconna`, `funcommand` 增加了三个参数 `name`, `prefixes` 和 `description`。
|
||||||
@@ -54,6 +54,7 @@ book = (
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@book.handle()
|
@book.handle()
|
||||||
async def _(arp: Arparma):
|
async def _(arp: Arparma):
|
||||||
await book.send(str(arp.options))
|
await book.send(str(arp.options))
|
||||||
|
|||||||
@@ -45,15 +45,12 @@ message = UniMessage(
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import Button, UniMessage
|
from nonebot_plugin_alconna import Button, UniMessage
|
||||||
|
|
||||||
message = (
|
message = UniMessage.text("hello world").keyboard(
|
||||||
UniMessage.text("hello world")
|
|
||||||
.keyboard(
|
|
||||||
Button("link1", url="https://example.com/1"),
|
Button("link1", url="https://example.com/1"),
|
||||||
Button("link2", url="https://example.com/2"),
|
Button("link2", url="https://example.com/2"),
|
||||||
Button("link3", url="https://example.com/3"),
|
Button("link3", url="https://example.com/3"),
|
||||||
row=3,
|
row=3,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 发送
|
### 发送
|
||||||
@@ -94,6 +91,7 @@ async def _():
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import message_recall, message_edit, message_reaction
|
from nonebot_plugin_alconna import message_recall, message_edit, message_reaction
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _():
|
async def _():
|
||||||
await message_edit(UniMessage.text("hello world"))
|
await message_edit(UniMessage.text("hello world"))
|
||||||
@@ -120,9 +118,9 @@ async def _():
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(msg: UniMsg):
|
async def _(msg: UniMsg): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
然后你可以通过 `UniMessage` 的方法来处理消息.
|
然后你可以通过 `UniMessage` 的方法来处理消息.
|
||||||
@@ -182,6 +180,7 @@ async def _(msg: UniMsg):
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMsg
|
from nonebot_plugin_alconna import UniMsg
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(msg: UniMsg):
|
async def _(msg: UniMsg):
|
||||||
data: list[dict] = msg.dump()
|
data: list[dict] = msg.dump()
|
||||||
@@ -193,6 +192,7 @@ async def _(msg: UniMsg):
|
|||||||
```python
|
```python
|
||||||
from nonebot_plugin_alconna import UniMessage
|
from nonebot_plugin_alconna import UniMessage
|
||||||
|
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _():
|
async def _():
|
||||||
data = [
|
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 = on_alconna(Alconna("make_meme", Args["name", str]["img", Image]))
|
||||||
|
|
||||||
|
|
||||||
@meme.handle()
|
@meme.handle()
|
||||||
async def _(img: Image):
|
async def _(img: Image): ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 模型定义
|
## 模型定义
|
||||||
@@ -24,6 +24,7 @@ async def _(img: Image):
|
|||||||
```python
|
```python
|
||||||
class Segment:
|
class Segment:
|
||||||
"""基类标注"""
|
"""基类标注"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> str: ...
|
def type(self) -> str: ...
|
||||||
@property
|
@property
|
||||||
@@ -31,29 +32,40 @@ class Segment:
|
|||||||
@property
|
@property
|
||||||
def children(self) -> list["Segment"]: ...
|
def children(self) -> list["Segment"]: ...
|
||||||
|
|
||||||
|
|
||||||
class Text(Segment):
|
class Text(Segment):
|
||||||
"""Text对象, 表示一类文本元素"""
|
"""Text对象, 表示一类文本元素"""
|
||||||
|
|
||||||
text: str
|
text: str
|
||||||
styles: dict[tuple[int, int], list[str]]
|
styles: dict[tuple[int, int], list[str]]
|
||||||
|
|
||||||
def cover(self, text: 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):
|
class At(Segment):
|
||||||
"""At对象, 表示一类提醒某用户的元素"""
|
"""At对象, 表示一类提醒某用户的元素"""
|
||||||
|
|
||||||
flag: Literal["user", "role", "channel"]
|
flag: Literal["user", "role", "channel"]
|
||||||
target: str
|
target: str
|
||||||
display: Optional[str]
|
display: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class AtAll(Segment):
|
class AtAll(Segment):
|
||||||
"""AtAll对象, 表示一类提醒所有人的元素"""
|
"""AtAll对象, 表示一类提醒所有人的元素"""
|
||||||
|
|
||||||
here: bool
|
here: bool
|
||||||
|
|
||||||
|
|
||||||
class Emoji(Segment):
|
class Emoji(Segment):
|
||||||
"""Emoji对象, 表示一类表情元素"""
|
"""Emoji对象, 表示一类表情元素"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
name: Optional[str]
|
name: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
class Media(Segment):
|
class Media(Segment):
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
url: Optional[str]
|
url: Optional[str]
|
||||||
@@ -64,53 +76,72 @@ class Media(Segment):
|
|||||||
|
|
||||||
to_url: ClassVar[Optional[MediaToUrl]]
|
to_url: ClassVar[Optional[MediaToUrl]]
|
||||||
|
|
||||||
|
|
||||||
class Image(Media):
|
class Image(Media):
|
||||||
"""Image对象, 表示一类图片元素"""
|
"""Image对象, 表示一类图片元素"""
|
||||||
|
|
||||||
width: Optional[int]
|
width: Optional[int]
|
||||||
height: Optional[int]
|
height: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
class Audio(Media):
|
class Audio(Media):
|
||||||
"""Audio对象, 表示一类音频元素"""
|
"""Audio对象, 表示一类音频元素"""
|
||||||
|
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class Voice(Media):
|
class Voice(Media):
|
||||||
"""Voice对象, 表示一类语音元素"""
|
"""Voice对象, 表示一类语音元素"""
|
||||||
|
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class Video(Media):
|
class Video(Media):
|
||||||
"""Video对象, 表示一类视频元素"""
|
"""Video对象, 表示一类视频元素"""
|
||||||
|
|
||||||
thumbnail: Optional[Image]
|
thumbnail: Optional[Image]
|
||||||
duration: Optional[float]
|
duration: Optional[float]
|
||||||
|
|
||||||
|
|
||||||
class File(Media):
|
class File(Media):
|
||||||
"""File对象, 表示一类文件元素"""
|
"""File对象, 表示一类文件元素"""
|
||||||
|
|
||||||
|
|
||||||
class Reply(Segment):
|
class Reply(Segment):
|
||||||
"""Reply对象,表示一类回复消息"""
|
"""Reply对象,表示一类回复消息"""
|
||||||
|
|
||||||
id: str
|
id: str
|
||||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||||
msg: Optional[Union[Message, str]]
|
msg: Optional[Union[Message, str]]
|
||||||
origin: Optional[Any]
|
origin: Optional[Any]
|
||||||
|
|
||||||
|
|
||||||
class Reference(Segment):
|
class Reference(Segment):
|
||||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||||
children: List[Union[RefNode, CustomNode]]
|
children: List[Union[RefNode, CustomNode]]
|
||||||
|
|
||||||
|
|
||||||
class Hyper(Segment):
|
class Hyper(Segment):
|
||||||
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
||||||
|
|
||||||
format: Literal["xml", "json"]
|
format: Literal["xml", "json"]
|
||||||
raw: Optional[str]
|
raw: Optional[str]
|
||||||
content: Optional[Union[dict, list]]
|
content: Optional[Union[dict, list]]
|
||||||
|
|
||||||
|
|
||||||
class Reference(Segment):
|
class Reference(Segment):
|
||||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
nodes: Sequence[Union[RefNode, CustomNode]]
|
nodes: Sequence[Union[RefNode, CustomNode]]
|
||||||
|
|
||||||
|
|
||||||
class Button(Segment):
|
class Button(Segment):
|
||||||
"""Button对象,表示一类按钮消息"""
|
"""Button对象,表示一类按钮消息"""
|
||||||
|
|
||||||
flag: Literal["action", "link", "input", "enter"]
|
flag: Literal["action", "link", "input", "enter"]
|
||||||
"""
|
"""
|
||||||
- 点击 action 类型的按钮时会触发一个关于 按钮回调 事件,该事件的 button 资源会包含上述 id
|
- 点击 action 类型的按钮时会触发一个关于 按钮回调 事件,该事件的 button 资源会包含上述 id
|
||||||
@@ -138,20 +169,26 @@ class Button(Segment):
|
|||||||
- list[At]: 指定用户/身份组可操作
|
- list[At]: 指定用户/身份组可操作
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Keyboard(Segment):
|
class Keyboard(Segment):
|
||||||
"""Keyboard对象,表示一行按钮元素"""
|
"""Keyboard对象,表示一行按钮元素"""
|
||||||
|
|
||||||
id: Optional[str]
|
id: Optional[str]
|
||||||
"""此处一般用来表示模板id,特殊情况下可能表示例如 bot_appid 等"""
|
"""此处一般用来表示模板id,特殊情况下可能表示例如 bot_appid 等"""
|
||||||
buttons: Optional[list[Button]]
|
buttons: Optional[list[Button]]
|
||||||
row: Optional[int]
|
row: Optional[int]
|
||||||
"""当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数"""
|
"""当消息中只写有一个 Keyboard 时可根据此参数约定按钮组的列数"""
|
||||||
|
|
||||||
|
|
||||||
class Other(Segment):
|
class Other(Segment):
|
||||||
"""其他 Segment"""
|
"""其他 Segment"""
|
||||||
|
|
||||||
origin: MessageSegment
|
origin: MessageSegment
|
||||||
|
|
||||||
|
|
||||||
class I18n(Segment):
|
class I18n(Segment):
|
||||||
"""特殊的 Segment,用于 i18n 消息"""
|
"""特殊的 Segment,用于 i18n 消息"""
|
||||||
|
|
||||||
item_or_scope: Union[LangItem, str]
|
item_or_scope: Union[LangItem, str]
|
||||||
type_: Optional[str] = None
|
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
|
from nonebot_plugin_alconna.builtins.uniseg.market_face import MarketFace
|
||||||
|
|
||||||
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 Image 的元素选出来并将第一个作为结果
|
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 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
|
# 表示这个指令需要的图片要么直接是 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#嵌套提取)
|
也可以参考通用消息的 [`嵌套提取`](./message.mdx#嵌套提取)
|
||||||
@@ -213,10 +254,13 @@ def mfbuild(builder: MessageBuilder, seg: BaseMessageSegment):
|
|||||||
|
|
||||||
|
|
||||||
@custom_handler(MarketFace)
|
@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:
|
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 的处理方法。
|
具体而言,你可以使用 `custom_register` 来增加一个从 MessageSegment 到 Segment 的处理方法;使用 `custom_handler` 来增加一个从 Segment 到 MessageSegment 的处理方法。
|
||||||
|
|||||||
@@ -155,7 +155,9 @@ op.create_table( # CREATE TABLE
|
|||||||
"weather_weather", # weather_weather
|
"weather_weather", # weather_weather
|
||||||
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
||||||
sa.Column("weather", sa.String(), nullable=False), # weather 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"},
|
info={"bind_key": "weather"},
|
||||||
)
|
)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
@@ -245,7 +247,9 @@ from nonebot.typing import T_State
|
|||||||
|
|
||||||
|
|
||||||
@weather.got("location", prompt="请输入地名")
|
@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)
|
wea = await session.get(Weather, location)
|
||||||
|
|
||||||
if not wea:
|
if not wea:
|
||||||
@@ -348,13 +352,16 @@ async def _(
|
|||||||
```python title=weather/__init__.py {5} showLineNumbers
|
```python title=weather/__init__.py {5} showLineNumbers
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
@weather.handle()
|
@weather.handle()
|
||||||
async def _(
|
async def _(
|
||||||
weas: Sequence[Weather] = SQLDepends(
|
weas: Sequence[Weather] = SQLDepends(
|
||||||
select(Weather).where(Weather.weather == Depends(extract_arg_plain_text))
|
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)。
|
支持的类型标注请参见 [依赖注入](dependency)。
|
||||||
@@ -364,6 +371,7 @@ async def _(
|
|||||||
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
class Weather(Model):
|
class Weather(Model):
|
||||||
location: Mapped[str] = mapped_column(primary_key=True)
|
location: Mapped[str] = mapped_column(primary_key=True)
|
||||||
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
||||||
|
|||||||
@@ -78,8 +78,7 @@ async def html_to_pic(
|
|||||||
img_fetch_fn: ImgFetchFn = combined_img_fetcher,
|
img_fetch_fn: ImgFetchFn = combined_img_fetcher,
|
||||||
css_fetch_fn: CSSFetchFn = combined_css_fetcher,
|
css_fetch_fn: CSSFetchFn = combined_css_fetcher,
|
||||||
urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin,
|
urljoin_fn: Callable[[str, str], str] = urllib3.parse.urljoin,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
最核心的渲染函数。
|
最核心的渲染函数。
|
||||||
@@ -107,8 +106,7 @@ async def text_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可用于渲染多行文本。
|
可用于渲染多行文本。
|
||||||
@@ -128,8 +126,7 @@ async def md_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。
|
可用于渲染 Markdown 文本。默认为 GitHub Markdown Light 风格,支持基于 `pygments` 的代码高亮。
|
||||||
@@ -153,8 +150,7 @@ async def template_to_pic(
|
|||||||
allow_refit: bool = True,
|
allow_refit: bool = True,
|
||||||
image_format: Literal["png", "jpeg"] = "png",
|
image_format: Literal["png", "jpeg"] = "png",
|
||||||
jpeg_quality: int = 100,
|
jpeg_quality: int = 100,
|
||||||
) -> bytes:
|
) -> bytes: ...
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
渲染 jinja2 模板。
|
渲染 jinja2 模板。
|
||||||
|
|||||||
@@ -44,15 +44,18 @@ require("nonebot_plugin_apscheduler")
|
|||||||
|
|
||||||
from nonebot_plugin_apscheduler import scheduler
|
from nonebot_plugin_apscheduler import scheduler
|
||||||
|
|
||||||
|
|
||||||
# 基于装饰器的方式
|
# 基于装饰器的方式
|
||||||
@scheduler.scheduled_job("cron", hour="*/2", id="job_0", args=[1], kwargs={arg2: 2})
|
@scheduler.scheduled_job("cron", hour="*/2", id="job_0", args=[1], kwargs={arg2: 2})
|
||||||
async def run_every_2_hour(arg1: int, arg2: int):
|
async def run_every_2_hour(arg1: int, arg2: int):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# 基于 add_job 方法的方式
|
# 基于 add_job 方法的方式
|
||||||
def run_every_day(arg1: int, arg2: int):
|
def run_every_day(arg1: int, arg2: int):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
scheduler.add_job(
|
scheduler.add_job(
|
||||||
run_every_day, "interval", days=1, id="job_1", args=[1], kwargs={arg2: 2}
|
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
|
```python {5,6} title=tests/test_http_server.py
|
||||||
from nonebug import App
|
from nonebug import App
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_http_server(app: App):
|
async def test_http_server(app: App):
|
||||||
async with app.test_server() as ctx:
|
async with app.test_server() as ctx:
|
||||||
@@ -45,6 +46,7 @@ import nonebot
|
|||||||
from nonebug import App
|
from nonebug import App
|
||||||
from nonebot.adapters.fake import Adapter
|
from nonebot.adapters.fake import Adapter
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_http_server(app: App):
|
async def test_http_server(app: App):
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
@@ -72,6 +74,7 @@ import nonebot
|
|||||||
from nonebug import App
|
from nonebug import App
|
||||||
from nonebot.adapters.fake import Adapter
|
from nonebot.adapters.fake import Adapter
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_ws_server(app: App):
|
async def test_ws_server(app: App):
|
||||||
adapter = nonebot.get_adapter(Adapter)
|
adapter = nonebot.get_adapter(Adapter)
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ except Exception as e:
|
|||||||
```python title=config.py
|
```python title=config.py
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseModel):
|
class Config(BaseModel):
|
||||||
xxx_id: str
|
xxx_id: str
|
||||||
xxx_token: str
|
xxx_token: str
|
||||||
@@ -102,6 +103,7 @@ from nonebot.adapters import Adapter as BaseAdapter
|
|||||||
|
|
||||||
from .config import Config
|
from .config import Config
|
||||||
|
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
def __init__(self, driver: Driver, **kwargs: Any):
|
def __init__(self, driver: Driver, **kwargs: Any):
|
||||||
@@ -208,9 +210,10 @@ from nonebot.drivers import (
|
|||||||
ASGIMixin,
|
ASGIMixin,
|
||||||
WebSocket,
|
WebSocket,
|
||||||
HTTPServerSetup,
|
HTTPServerSetup,
|
||||||
WebSocketServerSetup
|
WebSocketServerSetup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
def __init__(self, driver: Driver, **kwargs: Any):
|
def __init__(self, driver: Driver, **kwargs: Any):
|
||||||
@@ -242,7 +245,6 @@ class Adapter(BaseAdapter):
|
|||||||
)
|
)
|
||||||
self.setup_websocket_server(ws_setup)
|
self.setup_websocket_server(ws_setup)
|
||||||
|
|
||||||
|
|
||||||
async def _handle_http(self, request: Request) -> Response:
|
async def _handle_http(self, request: Request) -> Response:
|
||||||
"""HTTP 路由处理函数,只有一个类型为 Request 的参数,且返回值类型为 Response"""
|
"""HTTP 路由处理函数,只有一个类型为 Request 的参数,且返回值类型为 Response"""
|
||||||
...
|
...
|
||||||
@@ -270,8 +272,8 @@ class Adapter(BaseAdapter):
|
|||||||
```python {7,8,11} title=adapter.py
|
```python {7,8,11} title=adapter.py
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
def _handle_connect(self):
|
def _handle_connect(self):
|
||||||
bot_id = ... # 通过配置或者平台 API 等方式,获取到 Bot 的 ID
|
bot_id = ... # 通过配置或者平台 API 等方式,获取到 Bot 的 ID
|
||||||
bot = Bot(self, self_id=bot_id) # 实例化 Bot
|
bot = Bot(self, self_id=bot_id) # 实例化 Bot
|
||||||
@@ -295,8 +297,8 @@ from .bot import Bot
|
|||||||
from .event import Event
|
from .event import Event
|
||||||
from .log import log
|
from .log import log
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
@classmethod
|
@classmethod
|
||||||
def payload_to_event(cls, payload: Dict[str, Any]) -> Event:
|
def payload_to_event(cls, payload: Dict[str, Any]) -> Event:
|
||||||
"""根据平台事件的特性,转换平台 payload 为具体 Event
|
"""根据平台事件的特性,转换平台 payload 为具体 Event
|
||||||
@@ -316,7 +318,6 @@ class Adapter(BaseAdapter):
|
|||||||
# 也可以尝试转为基础 Event 进行处理
|
# 也可以尝试转为基础 Event 进行处理
|
||||||
return type_validate_python(Event, payload)
|
return type_validate_python(Event, payload)
|
||||||
|
|
||||||
|
|
||||||
async def _forward(self, bot: Bot):
|
async def _forward(self, bot: Bot):
|
||||||
|
|
||||||
payload: Dict[str, Any] # 接收到的事件数据
|
payload: Dict[str, Any] # 接收到的事件数据
|
||||||
@@ -337,8 +338,8 @@ from nonebot.drivers import Request, WebSocket
|
|||||||
|
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
|
|
||||||
class Adapter(BaseAdapter):
|
|
||||||
|
|
||||||
|
class Adapter(BaseAdapter):
|
||||||
@override
|
@override
|
||||||
async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
|
async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
|
||||||
log("DEBUG", f"Calling API <y>{api}</y>") # 给予日志提示
|
log("DEBUG", f"Calling API <y>{api}</y>") # 给予日志提示
|
||||||
@@ -356,7 +357,6 @@ class Adapter(BaseAdapter):
|
|||||||
# 发送请求,返回结果
|
# 发送请求,返回结果
|
||||||
return await self.driver.request(request)
|
return await self.driver.request(request)
|
||||||
|
|
||||||
|
|
||||||
# 采用 WebSocket 通信的方式,可以直接调用 send 方法发送数据
|
# 采用 WebSocket 通信的方式,可以直接调用 send 方法发送数据
|
||||||
# 通过某种方式获取到 bot 对应的 websocket 对象
|
# 通过某种方式获取到 bot 对应的 websocket 对象
|
||||||
ws: WebSocket = your_get_websocket_method(bot.self_id)
|
ws: WebSocket = your_get_websocket_method(bot.self_id)
|
||||||
@@ -450,8 +450,8 @@ from typing_extensions import override
|
|||||||
from nonebot.compat import model_dump
|
from nonebot.compat import model_dump
|
||||||
from nonebot.adapters import Event as BaseEvent
|
from nonebot.adapters import Event as BaseEvent
|
||||||
|
|
||||||
class Event(BaseEvent):
|
|
||||||
|
|
||||||
|
class Event(BaseEvent):
|
||||||
@override
|
@override
|
||||||
def get_event_name(self) -> str:
|
def get_event_name(self) -> str:
|
||||||
# 返回事件的名称,用于日志打印
|
# 返回事件的名称,用于日志打印
|
||||||
@@ -488,6 +488,7 @@ class Event(BaseEvent):
|
|||||||
```python {7,16,20,25,34,42} title=event.py
|
```python {7,16,20,25,34,42} title=event.py
|
||||||
from .message import Message
|
from .message import Message
|
||||||
|
|
||||||
|
|
||||||
class HeartbeatEvent(Event):
|
class HeartbeatEvent(Event):
|
||||||
"""心跳时间,通常为元事件"""
|
"""心跳时间,通常为元事件"""
|
||||||
|
|
||||||
@@ -495,8 +496,10 @@ class HeartbeatEvent(Event):
|
|||||||
def get_type(self) -> str:
|
def get_type(self) -> str:
|
||||||
return "meta_event"
|
return "meta_event"
|
||||||
|
|
||||||
|
|
||||||
class MessageEvent(Event):
|
class MessageEvent(Event):
|
||||||
"""消息事件"""
|
"""消息事件"""
|
||||||
|
|
||||||
message_id: str
|
message_id: str
|
||||||
user_id: str
|
user_id: str
|
||||||
|
|
||||||
@@ -513,8 +516,10 @@ class MessageEvent(Event):
|
|||||||
def get_user_id(self) -> str:
|
def get_user_id(self) -> str:
|
||||||
return self.user_id
|
return self.user_id
|
||||||
|
|
||||||
|
|
||||||
class JoinRoomEvent(Event):
|
class JoinRoomEvent(Event):
|
||||||
"""加入房间事件,通常为通知事件"""
|
"""加入房间事件,通常为通知事件"""
|
||||||
|
|
||||||
user_id: str
|
user_id: str
|
||||||
room_id: str
|
room_id: str
|
||||||
|
|
||||||
@@ -522,8 +527,10 @@ class JoinRoomEvent(Event):
|
|||||||
def get_type(self) -> str:
|
def get_type(self) -> str:
|
||||||
return "notice"
|
return "notice"
|
||||||
|
|
||||||
|
|
||||||
class ApplyAddFriendEvent(Event):
|
class ApplyAddFriendEvent(Event):
|
||||||
"""申请添加好友事件,通常为请求事件"""
|
"""申请添加好友事件,通常为请求事件"""
|
||||||
|
|
||||||
user_id: str
|
user_id: str
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -544,6 +551,7 @@ from nonebot.utils import escape_tag
|
|||||||
from nonebot.adapters import Message as BaseMessage
|
from nonebot.adapters import Message as BaseMessage
|
||||||
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
||||||
|
|
||||||
|
|
||||||
class MessageSegment(BaseMessageSegment["Message"]):
|
class MessageSegment(BaseMessageSegment["Message"]):
|
||||||
@classmethod
|
@classmethod
|
||||||
@override
|
@override
|
||||||
@@ -591,6 +599,7 @@ class Message(BaseMessage[MessageSegment]):
|
|||||||
```python title=tests/conftest.py
|
```python title=tests/conftest.py
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import nonebot.adapters
|
import nonebot.adapters
|
||||||
|
|
||||||
nonebot.adapters.__path__.append( # type: ignore
|
nonebot.adapters.__path__.append( # type: ignore
|
||||||
str((Path(__file__).parent.parent / "nonebot" / "adapters").resolve())
|
str((Path(__file__).parent.parent / "nonebot" / "adapters").resolve())
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ weather = on_command("天气")
|
|||||||
from nonebot import on_command
|
from nonebot import on_command
|
||||||
from nonebot.rule import to_me
|
from nonebot.rule import to_me
|
||||||
|
|
||||||
weather = on_command("天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True)
|
weather = on_command(
|
||||||
|
"天气", rule=to_me(), aliases={"weather", "查天气"}, priority=10, block=True
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
|
这样,我们就获得了一个可以响应 `天气`、`weather`、`查天气` 三个命令的响应规则,需要私聊或 `@bot` 时才会响应,优先级为 10(越小越优先),阻断事件向后续优先级传播的事件响应器了。这些内容的意义和使用方法将会在后续的章节中一一介绍。
|
||||||
|
|||||||
@@ -174,9 +174,7 @@ message = Message(
|
|||||||
# 索引
|
# 索引
|
||||||
message[0] == MessageSegment.text("test")
|
message[0] == MessageSegment.text("test")
|
||||||
# 切片
|
# 切片
|
||||||
message[0:2] == Message(
|
message[0:2] == Message([MessageSegment.text("test"), MessageSegment.markdown("test2")])
|
||||||
[MessageSegment.text("test"), MessageSegment.markdown("test2")]
|
|
||||||
)
|
|
||||||
# 类型过滤
|
# 类型过滤
|
||||||
message["markdown"] == Message(
|
message["markdown"] == Message(
|
||||||
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
[MessageSegment.markdown("test2"), MessageSegment.markdown("test3")]
|
||||||
@@ -262,7 +260,7 @@ msg = seg.join(
|
|||||||
MessageSegment.text("second"),
|
MessageSegment.text("second"),
|
||||||
MessageSegment.text("third"),
|
MessageSegment.text("third"),
|
||||||
]
|
]
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
msg == Message(
|
msg == Message(
|
||||||
|
|||||||
Reference in New Issue
Block a user