Compare commits

..

70 Commits

Author SHA1 Message Date
noneflow[bot]
b9392371c7 🔖 Release 2.1.3 2023-12-25 03:57:43 +00:00
Ju4tCode
d3c26a1548 🔖 bump version 2.1.3 (#2498) 2023-12-25 11:51:10 +08:00
noneflow[bot]
31c2a61cce 📝 Update changelog 2023-12-23 05:56:49 +00:00
XTxiaoting14332
f84ba9768b 🍻 publish plugin Phigros查分器(Adapter-qq) (#2496) 2023-12-23 05:55:59 +00:00
dependabot[bot]
1faa935527 ⬆️ Bump aiohttp from 3.9.0b1 to 3.9.0 (#2495)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-21 11:31:38 +08:00
noneflow[bot]
5f940ff309 📝 Update changelog 2023-12-18 07:30:21 +00:00
Cypas_Nya
4c4c0ea0ba ✏️ Plugin: 更新 splatoon3 插件地址 (#2494) 2023-12-18 15:29:23 +08:00
noneflow[bot]
787b40a99e 📝 Update changelog 2023-12-16 06:47:19 +00:00
lgc2333
fd6a0ae747 🍻 publish plugin Riffusion (#2492) 2023-12-16 06:46:29 +00:00
noneflow[bot]
298a32c096 📝 Update changelog 2023-12-15 02:53:22 +00:00
student_2333
aecff5ffd6 ✏️ Plugin: 删除不维护的插件 (#2491) 2023-12-15 10:52:30 +08:00
noneflow[bot]
c1a6b7b787 📝 Update changelog 2023-12-12 03:06:41 +00:00
Perseus037
0903f19f9c 🍻 publish plugin nonebot_plugin_longtu (#2489) 2023-12-12 03:05:45 +00:00
noneflow[bot]
51aa23817a 📝 Update changelog 2023-12-10 10:13:05 +00:00
Bryan不可思议
8f3f385cb6 🐛 Fix: 新增 Lifespan._on_ready() 供适配器使用 (#2483)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-12-10 18:12:10 +08:00
noneflow[bot]
915274081d 📝 Update changelog 2023-12-09 03:23:48 +00:00
lgc2333
a388c52b3f 🍻 publish plugin CNRail (#2487) 2023-12-09 03:22:56 +00:00
noneflow[bot]
b4d3cd4d4d 📝 Update changelog 2023-12-08 07:04:59 +00:00
Ju4tCode
50c03b0675 🚨 make pyright happy (#2486) 2023-12-08 15:03:59 +08:00
dependabot[bot]
fa3bb96417 ⬆️ Bump the actions group (#2484)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-08 13:26:19 +08:00
dependabot[bot]
09bde57835 ⬆️ Bump the actions group with 1 update (#2485)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-08 13:22:29 +08:00
noneflow[bot]
76ac2a8843 📝 Update changelog 2023-12-05 14:12:53 +00:00
Perseus037
f6ec6962ab 🍻 publish plugin ba塔罗牌,运势与魔法占卜! (#2480) 2023-12-05 14:11:43 +00:00
pre-commit-ci[bot]
28ad6829cd ⬆️ auto update by pre-commit hooks (#2482)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
2023-12-05 14:02:02 +08:00
noneflow[bot]
7f4b002a87 📝 Update changelog 2023-12-03 11:37:26 +00:00
iyume
7e073b6ff4 🍻 publish plugin 群聊 NSFW 图片检测 (#2476) 2023-12-03 11:36:22 +00:00
noneflow[bot]
fa3781efe5 📝 Update changelog 2023-11-30 08:10:45 +00:00
StarHeart
bec74d85cd 📝 Docs: 商店详情卡片添加宽度限制与文本省略 (#2473) 2023-11-30 16:09:29 +08:00
noneflow[bot]
abc3829c64 📝 Update changelog 2023-11-30 05:53:17 +00:00
Jigsaw
18f5d6eab9 ✏️ Plugin: 移除不再维护的插件 (#2474) 2023-11-30 13:52:17 +08:00
noneflow[bot]
00f3e30930 📝 Update changelog 2023-11-29 12:42:03 +00:00
worldmozara
97cd21d004 ✏️ Plugin: 移除不再维护的插件 (#2472) 2023-11-29 20:40:55 +08:00
noneflow[bot]
09b4d44f23 📝 Update changelog 2023-11-29 07:52:04 +00:00
MeetWq
3536bf56bd ✏️ Plugin: 移除不再维护的插件 (#2471) 2023-11-29 15:51:01 +08:00
noneflow[bot]
f8eaf5def0 📝 Update changelog 2023-11-24 02:24:57 +00:00
mobyw
6077f85e52 🍻 publish plugin sm.ms图床 (#2469) 2023-11-24 02:23:50 +00:00
noneflow[bot]
e2976a3859 📝 Update changelog 2023-11-24 02:11:57 +00:00
Ju4tCode
1e25fde22e Update assets/plugins.json 2023-11-24 02:10:51 +00:00
mnixry
55d88b7dae 🍻 publish plugin 文件托管支持 (#2467) 2023-11-24 02:10:51 +00:00
noneflow[bot]
de30f8917f 📝 Update changelog 2023-11-23 10:52:02 +00:00
StarHeartHunt
52653fa005 🍻 publish plugin 短链接服务支持 (#2465) 2023-11-23 10:50:57 +00:00
noneflow[bot]
4628358add 📝 Update changelog 2023-11-22 08:33:21 +00:00
StarHeart
117b08a73e 📝 Docs: 修复商店发布 上一步 按钮显示问题 (#2464) 2023-11-22 16:32:14 +08:00
noneflow[bot]
700888a8e0 📝 Update changelog 2023-11-22 06:05:31 +00:00
StarHeart
ef882927f3 📝 Docs: 添加商店表单支持 (#2460)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
2023-11-22 14:04:22 +08:00
noneflow[bot]
af9327de14 📝 Update changelog 2023-11-22 02:48:30 +00:00
he0119
2881d42bf5 🍻 publish plugin 用户 (#2462) 2023-11-22 02:47:21 +00:00
noneflow[bot]
dc3a49fe57 📝 Update changelog 2023-11-20 02:22:18 +00:00
student_2333
addabd6396 📝 Docs: 修复事件后处理函数类型 docstring 错误 (#2459) 2023-11-20 10:21:10 +08:00
noneflow[bot]
3341c641cc 📝 Update changelog 2023-11-12 04:18:10 +00:00
bingyue
363413e1e6 📝 Docs: 修改 QQ 频道为 QQ (#2457)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
2023-11-12 12:17:05 +08:00
noneflow[bot]
b675d27a30 📝 Update changelog 2023-11-10 12:05:29 +00:00
pre-commit-ci[bot]
796023408a 🚨 auto fix by pre-commit hooks 2023-11-10 12:03:46 +00:00
惜月
983a8512b2 📝 更新README 2023-11-10 12:03:46 +00:00
CMHopeSunshine
6593102632 🍻 publish adapter DoDo (#2455) 2023-11-10 12:03:46 +00:00
noneflow[bot]
65fff13150 📝 Update changelog 2023-11-09 06:14:11 +00:00
Alpaca4610
edd1a140d7 🍻 publish plugin DALL-E 3绘图 (#2451) 2023-11-09 06:12:43 +00:00
noneflow[bot]
18070baad4 📝 Update changelog 2023-11-08 02:47:58 +00:00
tiehu
acf729f6e7 🍻 publish plugin 局域网唤醒 (#2448) 2023-11-08 02:46:59 +00:00
noneflow[bot]
6dbc8eac03 📝 Update changelog 2023-11-08 02:31:28 +00:00
StarHeart
35944bcbdc 👷 CI: 测试矩阵添加 Python 3.12 (#2441) 2023-11-08 10:30:00 +08:00
noneflow[bot]
3f919f91c1 📝 Update changelog 2023-11-07 06:40:21 +00:00
Tarrailt
443a20d83d 📝 Docs: 更新最佳实践的 Alconna 部分 (#2443)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: StarHeartHunt <starheart233@gmail.com>
2023-11-07 14:39:05 +08:00
noneflow[bot]
2fca26eaae 📝 Update changelog 2023-11-07 06:29:54 +00:00
jiangyuxiaoxiao
ebc8141971 🍻 publish plugin nonebot-plugin-bertvits2 (#2445) 2023-11-07 06:28:15 +00:00
pre-commit-ci[bot]
5d6bcc9b9b ⬆️ auto update by pre-commit hooks (#2447)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-11-07 14:14:49 +08:00
noneflow[bot]
55fca332ba 📝 Update changelog 2023-11-05 09:08:57 +00:00
MelodyYuuka
6b65c5fe69 🍻 publish plugin Nonebot2 Any 多平台服务 (#2440) 2023-11-05 09:07:36 +00:00
noneflow[bot]
3e4dbe1015 📝 Update changelog 2023-11-05 03:23:34 +00:00
zhaomaoniu
20197e64b2 🍻 publish bot Sakiko (#2438) 2023-11-05 03:22:31 +00:00
139 changed files with 19339 additions and 870 deletions

View File

@@ -14,7 +14,7 @@ runs:
run: pipx install poetry
shell: bash
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
architecture: "x64"

View File

@@ -9,6 +9,10 @@ on:
- "nonebot/**"
- "packages/**"
- "tests/**"
- ".github/actions/setup-python/**"
- ".github/workflows/codecov.yml"
- "pyproject.toml"
- "poetry.lock"
jobs:
test:
@@ -19,7 +23,7 @@ jobs:
cancel-in-progress: true
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false
env:

View File

@@ -47,7 +47,7 @@ jobs:
run: pipx install poetry
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.x"

View File

@@ -7,7 +7,7 @@ ci:
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.0.292
rev: v0.1.6
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
@@ -20,7 +20,7 @@ repos:
stages: [commit]
- repo: https://github.com/psf/black
rev: 23.9.1
rev: 23.11.0
hooks:
- id: black
stages: [commit]

View File

@@ -122,6 +122,7 @@ NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架
| Red [仓库](https://github.com/nonebot/adapter-red)[协议](https://chrononeko.github.io/QQNTRedProtocol/) | ✅ | QQ 协议 |
| Satori[仓库](https://github.com/nonebot/adapter-satori)[协议](https://satori.js.org/zh-CN) | ✅ | 支持 Onebot、TG、飞书、微信公众号、Koishi 等 |
| Discord [仓库](https://github.com/nonebot/adapter-discord)[协议](https://discord.com/developers/docs/intro) | ✅ | Discord Bot 协议 |
| DoDo [仓库](https://github.com/nonebot/adapter-dodo)[协议](https://open.imdodo.com/) | ✅ | DoDo Bot 协议 |
| 开黑啦([仓库](https://github.com/Tian-que/nonebot-adapter-kaiheila)[协议](https://developer.kookapp.cn/) | ↗️ | 由社区贡献 |
| Mirai[仓库](https://github.com/ieew/nonebot_adapter_mirai2)[协议](https://docs.mirai.mamoe.net/mirai-api-http/) | ↗️ | QQ 协议,由社区贡献 |
| Ntchat[仓库](https://github.com/JustUndertaker/adapter-ntchat) | ↗️ | 微信协议,由社区贡献 |

View File

@@ -203,5 +203,15 @@
}
],
"is_official": true
},
{
"module_name": "nonebot.adapters.dodo",
"project_link": "nonebot-adapter-dodo",
"name": "DoDo",
"desc": "DoDo Bot 协议适配器",
"author": "CMHopeSunshine",
"homepage": "https://github.com/nonebot/adapter-dodo",
"tags": [],
"is_official": true
}
]

View File

@@ -590,5 +590,22 @@
"homepage": "https://github.com/MooncellWiki/BotFooChan",
"tags": [],
"is_official": false
},
{
"name": "Sakiko",
"desc": "基于 LiteLoaderBDS 的 Minecraft 基岩版 Bot",
"author": "zhaomaoniu",
"homepage": "https://github.com/zhaomaoniu/Sakiko",
"tags": [
{
"label": "Minecraft",
"color": "#6cc349"
},
{
"label": "BanGDream",
"color": "#e70050"
}
],
"is_official": false
}
]

View File

@@ -134,18 +134,6 @@
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_trpglogger",
"project_link": "nonebot-plugin-trpglogger",
"author": "j1g5awi",
"tags": [
{
"label": "TRPG",
"color": "#80070B"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_guess",
"project_link": "nonebot-plugin-guess",
@@ -167,18 +155,6 @@
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_manager",
"project_link": "nonebot-plugin-manager",
"author": "j1g5awi",
"tags": [
{
"label": "PluginManager",
"color": "#80070B"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_analysis_bilibili",
"project_link": "nonebot-plugin-analysis-bilibili",
@@ -337,20 +313,6 @@
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_asoulcnki",
"project_link": "nonebot-plugin-asoulcnki",
"author": "MeetWq",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_petpet",
"project_link": "nonebot-plugin-petpet",
"author": "MeetWq",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_shindan",
"project_link": "nonebot-plugin-shindan",
@@ -417,13 +379,6 @@
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_logo",
"project_link": "nonebot-plugin-logo",
"author": "MeetWq",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_memes",
"project_link": "nonebot-plugin-memes",
@@ -490,13 +445,6 @@
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_caiyunai",
"project_link": "nonebot-plugin-caiyunai",
"author": "MeetWq",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_weather_lite",
"project_link": "nonebot-plugin-weather-lite",
@@ -970,13 +918,6 @@
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_imageutils",
"project_link": "nonebot-plugin-imageutils",
"author": "MeetWq",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_mockingbird",
"project_link": "nonebot-plugin-mockingbird",
@@ -3749,22 +3690,6 @@
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_mixin",
"project_link": "nonebot-plugin-mixin",
"author": "NCBM",
"tags": [
{
"label": "dev",
"color": "#ea5252"
},
{
"label": "mixin",
"color": "#529fea"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_brainfuck",
"project_link": "nonebot-plugin-brainfuck",
@@ -3939,18 +3864,6 @@
],
"is_official": false
},
{
"module_name": "nonebot_plugin_msgbuf",
"project_link": "nonebot-plugin-msgbuf",
"author": "NCBM",
"tags": [
{
"label": "dev",
"color": "#ea5252"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_og",
"project_link": "nonebot-plugin-og",
@@ -4314,13 +4227,6 @@
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_eitherchoice",
"project_link": "nonebot-plugin-eitherchoice",
"author": "lgc2333",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_poke",
"project_link": "nonebot-plugin-poke",
@@ -4389,8 +4295,8 @@
"is_official": false
},
{
"module_name": "nonebot_plugin_splatoon3",
"project_link": "nonebot-plugin-splatoon3",
"module_name": "nonebot_plugin_splatoon3_schedule",
"project_link": "nonebot-plugin-splatoon3-schedule",
"author": "Cypas",
"tags": [
{
@@ -5255,5 +5161,159 @@
"author": "T0nyX1ang",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_any",
"project_link": "nonebot-plugin-any",
"author": "MelodyYuuka",
"tags": [
{
"label": "跨平台",
"color": "#36d399"
},
{
"label": "统一",
"color": "#fa5f55"
},
{
"label": "工具库",
"color": "#52eadd"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_bertvits2",
"project_link": "nonebot-plugin-bertvits2",
"author": "jiangyuxiaoxiao",
"tags": [
{
"label": "语音合成",
"color": "#9bf6ff"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_wake_on_lan",
"project_link": "nonebot-plugin-wake-on-lan",
"author": "tiehu",
"tags": [
{
"label": "局域网唤醒",
"color": "#1e90ff"
},
{
"label": "WOL",
"color": "#4682b4"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_bingimagecreator",
"project_link": "nonebot-plugin-bingimagecreator",
"author": "Alpaca4610",
"tags": [
{
"label": "AI",
"color": "#ea5252"
},
{
"label": "绘图",
"color": "#ea5252"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_user",
"project_link": "nonebot-plugin-user",
"author": "he0119",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_shorturl",
"project_link": "nonebot-plugin-shorturl",
"author": "StarHeartHunt",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_filehost",
"project_link": "nonebot-plugin-filehost",
"author": "mnixry",
"tags": [],
"is_official": true
},
{
"module_name": "nonebot_plugin_smms",
"project_link": "nonebot-plugin-smms",
"author": "mobyw",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_nsfw",
"project_link": "nonebot-plugin-nsfw",
"author": "iyume",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_batarot",
"project_link": "nonebot-plugin-batarot",
"author": "Perseus037",
"tags": [
{
"label": "碧蓝档案",
"color": "#15a0df"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_cnrail",
"project_link": "nonebot-plugin-cnrail",
"author": "lgc2333",
"tags": [],
"is_official": false
},
{
"module_name": "nonebot_plugin_longtu",
"project_link": "nonebot-plugin-longtu",
"author": "Perseus037",
"tags": [
{
"label": "龙图",
"color": "#e1122d"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_riffusion",
"project_link": "nonebot-plugin-riffusion",
"author": "lgc2333",
"tags": [
{
"label": "Riffusion",
"color": "#4b39cb"
}
],
"is_official": false
},
{
"module_name": "nonebot_plugin_phigros_qq",
"project_link": "nonebot-plugin-phigros-qq",
"author": "XTxiaoting14332",
"tags": [
{
"label": "Phigros",
"color": "#b0339a"
}
],
"is_official": false
}
]

View File

@@ -34,8 +34,6 @@ from nonebot.drivers import Request as BaseRequest
from nonebot.drivers import WebSocket as BaseWebSocket
from nonebot.drivers import HTTPServerSetup, WebSocketServerSetup
from ._lifespan import LIFESPAN_FUNC, Lifespan
try:
import uvicorn
from fastapi.responses import Response
@@ -97,8 +95,6 @@ class Driver(BaseDriver, ASGIMixin):
self.fastapi_config: Config = Config(**config.dict())
self._lifespan = Lifespan()
self._server_app = FastAPI(
lifespan=self._lifespan_manager,
openapi_url=self.fastapi_config.fastapi_openapi_url,
@@ -155,14 +151,6 @@ class Driver(BaseDriver, ASGIMixin):
name=setup.name,
)
@override
def on_startup(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
return self._lifespan.on_startup(func)
@override
def on_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
return self._lifespan.on_shutdown(func)
@contextlib.asynccontextmanager
async def _lifespan_manager(self, app: FastAPI):
await self._lifespan.startup()

View File

@@ -19,8 +19,6 @@ from nonebot.consts import WINDOWS
from nonebot.config import Env, Config
from nonebot.drivers import Driver as BaseDriver
from ._lifespan import LIFESPAN_FUNC, Lifespan
HANDLED_SIGNALS = (
signal.SIGINT, # Unix signal 2. Sent by Ctrl+C.
signal.SIGTERM, # Unix signal 15. Sent by `kill <pid>`.
@@ -35,8 +33,6 @@ class Driver(BaseDriver):
def __init__(self, env: Env, config: Config):
super().__init__(env, config)
self._lifespan = Lifespan()
self.should_exit: asyncio.Event = asyncio.Event()
self.force_exit: bool = False
@@ -52,16 +48,6 @@ class Driver(BaseDriver):
"""none driver 使用的 logger"""
return logger
@override
def on_startup(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
"""注册一个启动时执行的函数"""
return self._lifespan.on_startup(func)
@override
def on_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
"""注册一个停止时执行的函数"""
return self._lifespan.on_shutdown(func)
@override
def run(self, *args, **kwargs):
"""启动 none driver"""

View File

@@ -18,18 +18,7 @@ FrontMatter:
import asyncio
from functools import wraps
from typing_extensions import override
from typing import (
Any,
Dict,
List,
Tuple,
Union,
TypeVar,
Callable,
Optional,
Coroutine,
cast,
)
from typing import Any, Dict, List, Tuple, Union, Optional, cast
from pydantic import BaseSettings
@@ -57,8 +46,6 @@ except ModuleNotFoundError as e: # pragma: no cover
"Install with pip: `pip install nonebot2[quart]`"
) from e
_AsyncCallable = TypeVar("_AsyncCallable", bound=Callable[..., Coroutine])
def catch_closed(func):
@wraps(func)
@@ -102,6 +89,8 @@ class Driver(BaseDriver, ASGIMixin):
self._server_app = Quart(
self.__class__.__qualname__, **self.quart_config.quart_extra
)
self._server_app.before_serving(self._lifespan.startup)
self._server_app.after_serving(self._lifespan.shutdown)
@property
@override
@@ -150,16 +139,6 @@ class Driver(BaseDriver, ASGIMixin):
view_func=_handle,
)
@override
def on_startup(self, func: _AsyncCallable) -> _AsyncCallable:
"""参考文档: [`Startup and Shutdown`](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)"""
return self.server_app.before_serving(func) # type: ignore
@override
def on_shutdown(self, func: _AsyncCallable) -> _AsyncCallable:
"""参考文档: [`Startup and Shutdown`](https://pgjones.gitlab.io/quart/how_to_guides/startup_shutdown.html)"""
return self.server_app.after_serving(func) # type: ignore
@override
def run(
self,

View File

@@ -3,6 +3,7 @@ from contextlib import asynccontextmanager
from typing import Any, Dict, AsyncGenerator
from nonebot.config import Config
from nonebot.internal.driver._lifespan import LIFESPAN_FUNC
from nonebot.internal.driver import (
Driver,
Request,
@@ -97,6 +98,9 @@ class Adapter(abc.ABC):
async with self.driver.websocket(setup) as ws:
yield ws
def on_ready(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
return self.driver._lifespan.on_ready(func)
@abc.abstractmethod
async def _call_api(self, bot: Bot, api: str, **data: Any) -> Any:
"""`Adapter` 实际调用 api 的逻辑实现函数,实现该方法以调用 api。

View File

@@ -11,6 +11,7 @@ LIFESPAN_FUNC: TypeAlias = Union[SYNC_LIFESPAN_FUNC, ASYNC_LIFESPAN_FUNC]
class Lifespan:
def __init__(self) -> None:
self._startup_funcs: List[LIFESPAN_FUNC] = []
self._ready_funcs: List[LIFESPAN_FUNC] = []
self._shutdown_funcs: List[LIFESPAN_FUNC] = []
def on_startup(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
@@ -21,6 +22,10 @@ class Lifespan:
self._shutdown_funcs.append(func)
return func
def on_ready(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
self._ready_funcs.append(func)
return func
@staticmethod
async def _run_lifespan_func(
funcs: List[LIFESPAN_FUNC],
@@ -35,6 +40,9 @@ class Lifespan:
if self._startup_funcs:
await self._run_lifespan_func(self._startup_funcs)
if self._ready_funcs:
await self._run_lifespan_func(self._ready_funcs)
async def shutdown(self) -> None:
if self._shutdown_funcs:
await self._run_lifespan_func(self._shutdown_funcs)

View File

@@ -2,7 +2,7 @@ import abc
import asyncio
from typing_extensions import TypeAlias
from contextlib import AsyncExitStack, asynccontextmanager
from typing import TYPE_CHECKING, Any, Set, Dict, Type, Callable, AsyncGenerator
from typing import TYPE_CHECKING, Any, Set, Dict, Type, AsyncGenerator
from nonebot.log import logger
from nonebot.config import Env, Config
@@ -16,6 +16,7 @@ from nonebot.typing import (
T_BotDisconnectionHook,
)
from ._lifespan import LIFESPAN_FUNC, Lifespan
from .model import Request, Response, WebSocket, HTTPServerSetup, WebSocketServerSetup
if TYPE_CHECKING:
@@ -49,6 +50,7 @@ class Driver(abc.ABC):
"""全局配置对象"""
self._bots: Dict[str, "Bot"] = {}
self._bot_tasks: Set[asyncio.Task] = set()
self._lifespan = Lifespan()
def __repr__(self) -> str:
return (
@@ -100,15 +102,13 @@ class Driver(abc.ABC):
self.on_shutdown(self._cleanup)
@abc.abstractmethod
def on_startup(self, func: Callable) -> Callable:
"""注册一个在驱动器启动时执行的函数"""
raise NotImplementedError
def on_startup(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
"""注册一个启动时执行的函数"""
return self._lifespan.on_startup(func)
@abc.abstractmethod
def on_shutdown(self, func: Callable) -> Callable:
"""注册一个在驱动器停止时执行的函数"""
raise NotImplementedError
def on_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
"""注册一个停止时执行的函数"""
return self._lifespan.on_shutdown(func)
@classmethod
def on_bot_connect(cls, func: T_BotConnectionHook) -> T_BotConnectionHook:

View File

@@ -35,9 +35,9 @@ def combine_driver(
def type_(self: "CombinedDriver") -> str:
return (
driver.type.__get__(self)
driver.type.__get__(self) # type: ignore
+ "+"
+ "+".join(x.type.__get__(self) for x in mixins)
+ "+".join(x.type.__get__(self) for x in mixins) # type: ignore
)
return type(

View File

@@ -313,7 +313,7 @@ class Matcher(metaclass=MatcherMeta):
matchers[priority].append(NewMatcher)
return NewMatcher
return NewMatcher # type: ignore
@classmethod
def destroy(cls) -> None:

View File

@@ -94,7 +94,7 @@ T_EventPreProcessor: TypeAlias = _DependentCallable[Any]
- DefaultParam: 带有默认值的参数
"""
T_EventPostProcessor: TypeAlias = _DependentCallable[Any]
"""事件处理函数 EventPostProcessor 类型
"""事件处理函数 EventPostProcessor 类型
依赖参数:

1112
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "nonebot2"
version = "2.1.2"
version = "2.1.3"
description = "An asynchronous python bot framework."
authors = ["yanyongyu <yyy@nonebot.dev>"]
license = "MIT"
@@ -36,7 +36,7 @@ pydantic = { version = "^1.10.0", extras = ["dotenv"] }
websockets = { version = ">=10.0", optional = true }
Quart = { version = ">=0.18.0,<1.0.0", optional = true }
fastapi = { version = ">=0.93.0,<1.0.0", optional = true }
aiohttp = { version = "^3.7.4", extras = ["speedups"], optional = true }
aiohttp = { version = "^3.9.0b0", extras = ["speedups"], optional = true }
httpx = { version = ">=0.20.0,<1.0.0", extras = ["http2"], optional = true }
uvicorn = { version = ">=0.20.0,<1.0.0", extras = [
"standard",
@@ -53,7 +53,7 @@ ruff = ">=0.0.272,<1.0.0"
nonebug = "^0.3.0"
pytest-cov = "^4.0.0"
pytest-xdist = "^3.0.2"
pytest-asyncio = "^0.22.0"
pytest-asyncio = "^0.23.2"
werkzeug = ">=2.3.6,<4.0.0"
coverage-conditional-plugin = "^0.9.0"

View File

@@ -12,7 +12,7 @@ exclude_lines =
@(abc\.)?abstractmethod
raise NotImplementedError
warnings\.warn
\.\.\.
^\.\.\.$
pass
if __name__ == .__main__.:

View File

@@ -5,11 +5,11 @@ from typing import Any, Set, Optional
import pytest
from nonebug import App
from utils import FakeAdapter
from nonebot.adapters import Bot
from nonebot.params import Depends
from nonebot.dependencies import Dependent
from nonebot.exception import WebSocketClosed
from nonebot.drivers._lifespan import Lifespan
from nonebot.drivers import (
URL,
Driver,
@@ -25,34 +25,50 @@ from nonebot.drivers import (
@pytest.mark.asyncio
async def test_lifespan():
lifespan = Lifespan()
@pytest.mark.parametrize(
"driver", [pytest.param("nonebot.drivers.none:Driver", id="none")], indirect=True
)
async def test_lifespan(driver: Driver):
adapter = FakeAdapter(driver)
start_log = []
ready_log = []
shutdown_log = []
@lifespan.on_startup
@driver.on_startup
async def _startup1():
assert start_log == []
start_log.append(1)
@lifespan.on_startup
@driver.on_startup
async def _startup2():
assert start_log == [1]
start_log.append(2)
@lifespan.on_shutdown
@adapter.on_ready
def _ready1():
assert start_log == [1, 2]
assert ready_log == []
ready_log.append(1)
@adapter.on_ready
def _ready2():
assert ready_log == [1]
ready_log.append(2)
@driver.on_shutdown
async def _shutdown1():
assert shutdown_log == []
shutdown_log.append(1)
@lifespan.on_shutdown
@driver.on_shutdown
async def _shutdown2():
assert shutdown_log == [1]
shutdown_log.append(2)
async with lifespan:
async with driver._lifespan:
assert start_log == [1, 2]
assert ready_log == [1, 2]
assert shutdown_log == [1, 2]

View File

@@ -5,11 +5,14 @@ description: Alconna 命令解析拓展
slug: /best-practice/alconna/
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
# Alconna 插件
[`nonebot-plugin-alconna`](https://github.com/nonebot/plugin-alconna) 是一类提供了拓展响应规则的插件。
该插件使用 [Alconna](https://github.com/ArcletProject/Alconna) 作为命令解析器,
是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
是一个简单、灵活、高效的命令参数解析器并且不局限于解析命令式字符串。
该插件提供了一类新的事件响应器辅助函数 `on_alconna`,以及 `AlconnaResult` 等依赖注入函数。
@@ -31,16 +34,31 @@ slug: /best-practice/alconna/
在**项目目录**下执行以下命令:
<Tabs groupId="install">
<TabItem value="cli" label="使用 nb-cli">
```shell
nb plugin install nonebot-plugin-alconna
```
</TabItem>
<TabItem value="pip" label="使用 pip">
```shell
pip install nonebot-plugin-alconna
```
</TabItem>
<TabItem value="pdm" label="使用 pdm">
```shell
pdm add nonebot-plugin-alconna
```
</TabItem>
</Tabs>
## 导入插件
由于 `nonebot-plugin-alconna` 作为插件,因此需要在使用前对其进行**加载**并**导入**其中的 `on_alconna` 来使用命令拓展。使用 `require` 方法可轻松完成这一过程,可参考 [跨插件访问](../../advanced/requiring.md) 一节进行了解。
@@ -87,7 +105,7 @@ async def got_location(location: str = ArgPlainText()):
```python {5-10,14-16,18-19}
from nonebot.rule import to_me
from arclet.alconna import Alconna, Args
from nonebot_plugin_alconna import Match, AlconnaMatcher, on_alconna
from nonebot_plugin_alconna import Match, on_alconna
weather = on_alconna(
Alconna("天气", Args["location?", str]),
@@ -98,9 +116,9 @@ weather.shortcut("天气预报", {"command": "天气"})
@weather.handle()
async def handle_function(matcher: AlconnaMatcher, location: Match[str]):
async def handle_function(location: Match[str]):
if location.available:
matcher.set_path_arg("location", location.result)
weather.set_path_arg("location", location.result)
@weather.got_path("location", prompt="请输入地名")
async def got_location(location: str):

View File

@@ -6,9 +6,9 @@ description: Alconna 基本介绍
# Alconna 命令解析
[Alconna](https://github.com/ArcletProject/Alconna) 作为命令解析器,
是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
是一个简单、灵活、高效的命令参数解析器并且不局限于解析命令式字符串。
特点包括:
特点包括
- 高效
- 直观的命令组件创建方式
@@ -16,10 +16,94 @@ description: Alconna 基本介绍
- 自定义的帮助信息格式
- 多语言支持
- 易用的快捷命令创建与使用
- 可创建命令补全会话, 以实现多轮连续的补全提示
- 可创建命令补全会话以实现多轮连续的补全提示
- 可嵌套的多级子命令
- 正则匹配支持
## 命令示范
```python
import sys
from io import StringIO
from arclet.alconna import Alconna, Args, Field, Option, CommandMeta, MultiVar, Arparma
from nepattern import AnyString
alc = Alconna(
"exec",
Args["code", MultiVar(AnyString), Field(completion=lambda: "print(1+1)")] / "\n",
Option("纯文本"),
Option("无输出"),
Option("目标", Args["name", str, "res"]),
meta=CommandMeta("exec python code", example="exec\\nprint(1+1)"),
)
alc.shortcut(
"echo",
{"command": "exec 纯文本\nprint(\\'{*}\\')"},
)
alc.shortcut(
"sin(\d+)",
{"command": "exec 纯文本\nimport math\nprint(math.sin({0}*math.pi/180))"},
)
def exec_code(result: Arparma):
if result.find("纯文本"):
codes = list(result.code)
else:
codes = str(result.origin).split("\n")[1:]
output = result.query[str]("目标.name", "res")
if not codes:
return ""
lcs = {}
_stdout = StringIO()
_to = sys.stdout
sys.stdout = _stdout
try:
exec(
"def rc(__out: str):\n "
+ " ".join(_code + "\n" for _code in codes)
+ " return locals().get(__out)",
{**globals(), **locals()},
lcs,
)
code_res = lcs["rc"](output)
sys.stdout = _to
if result.find("无输出"):
return ""
if code_res is not None:
return f"{output}: {code_res}"
_out = _stdout.getvalue()
return f"输出: {_out}"
except Exception as e:
sys.stdout = _to
return str(e)
finally:
sys.stdout = _to
print(exec_code(alc.parse("echo 1234")))
print(exec_code(alc.parse("sin30")))
print(
exec_code(
alc.parse(
"""\
exec
print(
exec_code(
alc.parse(
"exec\\n"
"import sys;print(sys.version)"
)
)
)
"""
)
)
)
```
## 命令编写
### 命令头
@@ -115,7 +199,7 @@ alc = Alconna(
- `help_text`: 传入该组件的帮助信息
- `dest`: 被指定为解析完成时标注匹配结果的标识符,不传入时默认为选项或子命令的名称 (name)
- `requires`: 一段指定顺序的字符串列表,作为唯一的前置序列与命令嵌套替换
对于命令 `test foo bar baz qux <a:int>` 来讲,因为`foo bar baz` 仅需要判断是否相等, 所以可以这么编写:
对于命令 `test foo bar baz qux <a:int>` 来讲,因为`foo bar baz` 仅需要判断是否相等所以可以这么编写:
```python
Alconna("test", Option("qux", Args["a", int], requires=["foo", "bar", "baz"]))
@@ -263,7 +347,7 @@ args = Args["foo", BasePattern("@\d+")]
### 紧凑命令
`Alconna`, `Option` 与 `Subcommand` 可以设置 `compact=True` 使得解析命令时允许名称与后随参数之间没有分隔:
`Alconna``Option` 可以设置 `compact=True` 使得解析命令时允许名称与后随参数之间没有分隔:
```python
from arclet.alconna import Alconna, Option, CommandMeta, Args
@@ -390,14 +474,14 @@ class ShortcutArgs(TypedDict):
- `{%X}`: 如 `setu {%0}`,表示此处必须填入快捷指令后随的第 X 个参数。
例如,若快捷指令为 `涩图`, 配置为 `{"command": "setu {%0}"}`, 则指令 `涩图 1` 相当于 `setu 1`
例如,若快捷指令为 `涩图`配置为 `{"command": "setu {%0}"}`则指令 `涩图 1` 相当于 `setu 1`
- `{*}`: 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。
- `{X}`: 表示此处填入可能的正则匹配的组:
- 若 `command` 中存在匹配组 `(xxx)`,则 `{X}` 表示第 X 个匹配组的内容
- 若 `command` 中存储匹配组 `(?P<xxx>...)`, 则 `{X}` 表示名字为 X 的匹配结果
- 若 `command` 中存储匹配组 `(?P<xxx>...)`则 `{X}` 表示名字为 X 的匹配结果
除此之外, 通过内置选项 `--shortcut` 可以动态操作快捷指令。
除此之外通过内置选项 `--shortcut` 可以动态操作快捷指令。
例如:
@@ -444,17 +528,17 @@ alc.parse("test_fuzy")
`path` 支持如下:
- `main_args`, `options`, ...: 返回对应的属性
- `main_args``options`...: 返回对应的属性
- `args`: 返回 all_matched_args
- `main_args.xxx`, `options.xxx`, ...: 返回字典中 `xxx`键对应的值
- `main_args.xxx``options.xxx`...: 返回字典中 `xxx`键对应的值
- `args.xxx`: 返回 all_matched_args 中 `xxx`键对应的值
- `options.foo`, `foo`: 返回选项 `foo` 的解析结果 (OptionResult)
- `options.foo.value`, `foo.value`: 返回选项 `foo` 的解析值
- `options.foo.args`, `foo.args`: 返回选项 `foo` 的解析参数字典
- `options.foo.args.bar`, `foo.bar`: 返回选项 `foo` 的参数字典中 `bar` 键对应的值
- `options.foo``foo`: 返回选项 `foo` 的解析结果 (OptionResult)
- `options.foo.value``foo.value`: 返回选项 `foo` 的解析值
- `options.foo.args``foo.args`: 返回选项 `foo` 的解析参数字典
- `options.foo.args.bar``foo.bar`: 返回选项 `foo` 的参数字典中 `bar` 键对应的值
...
同样, `Arparma["foo.bar"]` 的表现与 `query()` 一致
同样`Arparma["foo.bar"]` 的表现与 `query()` 一致
## Duplication

View File

@@ -31,14 +31,7 @@ description: 配置项
- **类型**: `bool`
- **默认值**: `False`
是否全局使用原始消息 (即未经过 to_me 等处理的), 该选项会影响到 Alconna 的匹配行为。
## alconna_use_param
- **类型**: `bool`
- **默认值**: `True`
是否使用特制的 Param 提供更好的依赖注入,该选项不会对使用依赖注入函数形式造成影响
是否全局使用原始消息 (即未经过 to_me 等处理的)该选项会影响到 Alconna 的匹配行为。
## alconna_use_command_sep
@@ -52,4 +45,4 @@ description: 配置项
- **类型**: `List[str]`
- **默认值**: `[]`
全局加载的扩展, 路径以 . 分隔, 如 foo.bar.baz:DemoExtension
全局加载的扩展路径以 . 分隔,如 `foo.bar.baz:DemoExtension`

View File

@@ -9,7 +9,7 @@ description: 响应规则的使用
```python
from nonebot_plugin_alconna.adapters.onebot12 import Image
from nonebot_plugin_alconna import At, AlconnaMatches, on_alconna
from nonebot_plugin_alconna import At, on_alconna
from arclet.alconna import Args, Option, Alconna, Arparma, MultiVar, Subcommand
alc = Alconna(
@@ -27,9 +27,9 @@ rg = on_alconna(alc, auto_send_output=True)
@rg.handle()
async def _(result: Arparma = AlconnaMatches()):
async def _(result: Arparma):
if result.find("list"):
img = await gen_role_group_list_image()
img = await ob12_gen_role_group_list_image()
await rg.finish(Image(img))
if result.find("add"):
group = await create_role_group(result.query[str]("add.name"))
@@ -46,10 +46,10 @@ async def _(result: Arparma = AlconnaMatches()):
- `command: Alconna | str`: Alconna 命令
- `skip_for_unmatch: bool = True`: 是否在命令不匹配时跳过该响应
- `auto_send_output: bool = False`: 是否自动发送输出信息并跳过响应
- `aliases: set[str | tuple[str, ...]] | None = None`: 命令别名, 作用类似于 `on_command` 中的 aliases
- `comp_config: CompConfig | None = None`: 补全会话配置, 不传入则不启用补全会话
- `extensions: list[type[Extension] | Extension] | None = None`: 需要加载的匹配扩展, 可以是扩展类或扩展实例
- `exclude_ext: list[type[Extension] | str] | None = None`: 需要排除的匹配扩展, 可以是扩展类或扩展的id
- `aliases: set[str | tuple[str, ...]] | None = None`: 命令别名,作用类似于 `on_command` 中的 aliases
- `comp_config: CompConfig | None = None`: 补全会话配置,不传入则不启用补全会话
- `extensions: list[type[Extension] | Extension] | None = None`: 需要加载的匹配扩展可以是扩展类或扩展实例
- `exclude_ext: list[type[Extension] | str] | None = None`: 需要排除的匹配扩展可以是扩展类或扩展的 id
- `use_origin: bool = False`: 是否使用未经 to_me 等处理过的消息
- `use_cmd_start: bool = False`: 是否使用 COMMAND_START 作为命令前缀
- `use_cmd_sep: bool = False`: 是否使用 COMMAND_SEP 作为命令分隔符
@@ -67,7 +67,7 @@ async def _(result: Arparma = AlconnaMatches()):
```python
from arclet.alconna import Alconna, Option, Args
from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match, AlconnaMatcher, AlconnaArg, UniMessage
from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match, UniMessage
login = on_alconna(Alconna(["/"], "login", Args["password?", str], Option("-r|--recall")))
@@ -76,11 +76,12 @@ async def login_exit():
await login.finish("已退出")
@login.assign("password")
async def login_handle(matcher: AlconnaMatcher, pw: Match[str] = AlconnaMatch("password")):
matcher.set_path_arg("password", pw.result)
async def login_handle(pw: Match[str] = AlconnaMatch("password")):
if pw.available:
login.set_path_arg("password", pw.result)
@login.got_path("password", prompt=UniMessage.template("{:At(user, $event.get_user_id())} 请输入密码"))
async def login_got(password: str = AlconnaArg("password")):
async def login_got(password: str):
assert password
await login.send("登录成功")
```
@@ -89,50 +90,14 @@ async def login_got(password: str = AlconnaArg("password")):
`Alconna` 的解析结果会放入 `Arparma` 类中,或用户指定的 `Duplication` 类。
`nonebot_plugin_alconna` 提供了一系列的依赖注入函数,他们包括
- `AlconnaResult`: `CommandResult` 类型的依赖注入函数
- `AlconnaMatches`: `Arparma` 类型的依赖注入函数
- `AlconnaDuplication`: `Duplication` 类型的依赖注入函数
- `AlconnaMatch`: `Match` 类型的依赖注入函数,其能够额外传入一个 middleware 函数来处理得到的参数
- `AlconnaQuery`: `Query` 类型的依赖注入函数,其能够额外传入一个 middleware 函数来处理得到的参数
- `AlconnaExecResult`: 提供挂载在命令上的 callback 的返回结果 (`Dict[str, Any]`) 的依赖注入函数
- `AlconnaExtension`: 提供指定类型的 `Extension` 的依赖注入函数
可以看到,本插件提供了几类额外的模型:
- `CommandResult`: 解析结果,包括了源命令 `source: Alconna` ,解析结果 `result: Arparma`,以及可能的输出信息 `output: str | None` 字段
- `Match`: 匹配项,表示参数是否存在于 `all_matched_args` 内,可用 `Match.available` 判断是否匹配,`Match.result` 获取匹配的值
- `Query`: 查询项,表示参数是否可由 `Arparma.query` 查询并获得结果,可用 `Query.available` 判断是否查询成功,`Query.result` 获取查询结果
同时,基于 [`Annotated` 支持](https://github.com/nonebot/nonebot2/pull/1832), 添加了三类注解:
- `AlcMatches`:同 `AlconnaMatches`
- `AlcResult`:同 `AlconnaResult`
- `AlcExecResult`: 同 `AlconnaExecResult`
而若设置配置项 **ALCONNA_USE_PARAM** (默认为 True) 为 True则上述依赖注入的目标参数皆不需要使用依赖注入函数
`AlconnaMatcher` 在原有 Matcher 的基础上拓展了允许的依赖注入
```python
...
@cmd.handle()
async def handle1(
result: CommandResult = AlconnaResult(),
arp: Arparma = AlconnaMatches(),
dup: Duplication = AlconnaDuplication(Duplication),
ext: Extension = AlconnaExtension(Extension),
foo: Match[str] = AlconnaMatch("foo"),
bar: Query[int] = AlconnaQuery("ttt.bar", 0)
):
...
# ALCONNA_USE_PARAM 为 True 后
@cmd.handle()
async def handle2(
async def handle(
result: CommandResult,
arp: Arparma,
dup: Duplication,
dup: Duplication, # 基类或子类都可以
ext: Extension,
source: Alconna,
abc: str, # 类似 Match, 但是若匹配结果不存在对应字段则跳过该 handler
@@ -142,7 +107,25 @@ async def handle2(
...
```
该效果对于 `got_path` 下的 Arg 同样有效
可以看到,本插件提供了几类额外的模型:
- `CommandResult`: 解析结果,包括了源命令 `source: Alconna` ,解析结果 `result: Arparma`,以及可能的输出信息 `output: str | None` 字段
- `Match`: 匹配项,表示参数是否存在于 `all_matched_args` 内,可用 `Match.available` 判断是否匹配,`Match.result` 获取匹配的值
- `Query`: 查询项,表示参数是否可由 `Arparma.query` 查询并获得结果,可用 `Query.available` 判断是否查询成功,`Query.result` 获取查询结果
:::note
如果你更喜欢 Depends 式的依赖注入,`nonebot_plugin_alconna` 同时提供了一系列的依赖注入函数,他们包括:
- `AlconnaResult`: `CommandResult` 类型的依赖注入函数
- `AlconnaMatches`: `Arparma` 类型的依赖注入函数
- `AlconnaDuplication`: `Duplication` 类型的依赖注入函数
- `AlconnaMatch`: `Match` 类型的依赖注入函数,其能够额外传入一个 middleware 函数来处理得到的参数
- `AlconnaQuery`: `Query` 类型的依赖注入函数,其能够额外传入一个 middleware 函数来处理得到的参数
- `AlconnaExecResult`: 提供挂载在命令上的 callback 的返回结果 (`Dict[str, Any]`) 的依赖注入函数
- `AlconnaExtension`: 提供指定类型的 `Extension` 的依赖注入函数
:::
实例:
@@ -152,13 +135,7 @@ from nonebot import require
require("nonebot_plugin_alconna")
...
from nonebot_plugin_alconna import (
on_alconna,
Match,
Query,
AlconnaQuery,
AlcResult
)
from nonebot_plugin_alconna import on_alconna, Match, Query, AlconnaQuery
from arclet.alconna import Alconna, Args, Option, Arparma
test = on_alconna(
@@ -177,17 +154,12 @@ async def handle_test1(result: AlcResult):
await test.send(f"maybe output: {result.output}")
@test.handle()
async def handle_test2(result: Arparma):
await test.send(f"head result: {result.header_result}")
await test.send(f"args: {result.all_matched_args}")
@test.handle()
async def handle_test3(bar: Match[int]):
async def handle_test2(bar: Match[int]):
if bar.available:
await test.send(f"foo={bar.result}")
@test.handle()
async def handle_test4(qux: Query[bool] = AlconnaQuery("baz.qux", False)):
async def handle_test3(qux: Query[bool] = AlconnaQuery("baz.qux", False)):
if qux.available:
await test.send(f"baz.qux={qux.result}")
```
@@ -220,7 +192,7 @@ group.extend(member.target for member in ats)
| [飞书](https://github.com/nonebot/adapter-feishu) | adapters.feishu |
| [GitHub](https://github.com/nonebot/adapter-github) | adapters.github |
| [QQ bot](https://github.com/nonebot/adapter-qq) | adapters.qq |
| [QQ 频道bot](https://github.com/nonebot/adapter-qq) | adapters.qqguild |
| [QQ 频道 bot](https://github.com/nonebot/adapter-qq) | adapters.qqguild |
| [钉钉](https://github.com/nonebot/adapter-ding) | adapters.ding |
| [Console](https://github.com/nonebot/adapter-console) | adapters.console |
| [开黑啦](https://github.com/Tian-que/nonebot-adapter-kaiheila) | adapters.kook |
@@ -232,6 +204,7 @@ group.extend(member.target for member in ats)
| [Villa](https://github.com/CMHopeSunshine/nonebot-adapter-villa) | adapters.villa |
| [Discord](https://github.com/nonebot/adapter-discord) | adapters.discord |
| [Red 协议](https://github.com/nonebot/adapter-red) | adapters.red |
| [Satori 协议](https://github.com/nonebot/adapter-satori) | adapters.satori |
## 条件控制
@@ -258,6 +231,7 @@ pip = Alconna(
pip_cmd = on_alconna(pip)
# 仅在命令为 `pip install pip` 时响应
@pip_cmd.assign("install.pak", "pip")
async def update(res: CommandResult):
...
@@ -267,7 +241,7 @@ async def update(res: CommandResult):
async def list_(res: CommandResult):
...
# 在命令为 `pip install` 时响应
# 在命令为 `pip install xxx` 时响应
@pip_cmd.assign("install")
async def install(res: CommandResult):
...
@@ -279,28 +253,28 @@ async def install(res: CommandResult):
update_cmd = pip_cmd.dispatch("install.pak", "pip")
@update_cmd.handle()
async def update(arp: CommandResult = AlconnaResult()):
async def update(arp: CommandResult):
...
```
另外,`AlconnaMatcher` 有类似于 `got``got_path`
```python
from nonebot_plugin_alconna import At, Match, UniMessage, AlconnaMatcher, on_alconna
from nonebot_plugin_alconna import At, Match, UniMessage, on_alconna
test_cmd = on_alconna(Alconna("test", Args["target?", Union[str, At]]))
@test_cmd.handle()
async def tt_h(matcher: AlconnaMatcher, target: Match[Union[str, At]]):
async def tt_h(target: Match[Union[str, At]]):
if target.available:
matcher.set_path_arg("target", target.result)
test_cmd.set_path_arg("target", target.result)
@test_cmd.got_path("target", prompt="请输入目标")
async def tt(target: Union[str, At]):
await test_cmd.send(UniMessage(["ok\n", target]))
```
`got_path``assign`, `Match`, `Query` 等地方一样,都需要指明 `path` 参数 (即对应 Arg 验证的路径)
`got_path``assign``Match``Query` 等地方一样,都需要指明 `path` 参数 (即对应 Arg 验证的路径)
`got_path` 会获取消息的最后一个消息段并转为 path 对应的类型,例如示例中 `target` 对应的 Arg 里要求 str 或 At则 got 后用户输入的消息只有为 text 或 at 才能进入处理函数。
@@ -338,7 +312,7 @@ async def tt(target: Union[str, At]):
例如 `LLMExtension` (仅举例)
```python
from nonebot_plugin_alconna import Extension, Alconna, on_alconna
from nonebot_plugin_alconna import Extension, Alconna, on_alconna, Interface
class LLMExtension(Extension):
@property
@@ -359,6 +333,13 @@ class LLMExtension(Extension):
resp = await self.llm.input(str(receive))
return receive.__class__(resp.content)
def before_catch(self, name, annotation, default):
return name == "llm"
def catch(self, interface: Interface):
if interface.name == "llm":
return self.llm
matcher = on_alconna(
Alconna(...),
extensions=[LLMExtension(LLM)]
@@ -366,20 +347,22 @@ matcher = on_alconna(
...
```
那么使用了 `LLMExtension` 的响应器便能接受任何能通过 llm 翻译为具体命令的自然语言消息
那么使用了 `LLMExtension` 的响应器便能接受任何能通过 llm 翻译为具体命令的自然语言消息,同时可以在响应器中为所有 `llm` 参数注入模型变量
目前 `Extension` 的功能有:
- 对于事件的来源适配器或 bot 选择是否接受响应
- 输出信息的自定义转换方法
- 从传入事件中自定义提取消息的方法
- 对于传入的alc对象的追加的自定义处理
- 对传入的消息 (Message 或 UniMessage) 的额外处理
- 对命令解析结果的额外处理
- 对发送的消息 (Message 或 UniMessage) 的额外处理
- 自定义额外的matcher api
- `validate`: 对于事件的来源适配器或 bot 选择是否接受响应
- `output_converter`: 输出信息的自定义转换方法
- `message_provider`: 从传入事件中自定义提取消息的方法
- `receive_provider`: 对传入的消息 (Message 或 UniMessage) 的额外处理
- `permission_check`: 命令对消息解析并确认头部匹配(即确认选择响应)时对发送者的权限判断
- `parse_wrapper`: 对命令解析结果的额外处理
- `send_wrapper`: 对发送的消息 (Message 或 UniMessage) 的额外处理
- `before_catch`: 自定义依赖注入的绑定确认函数
- `catch`: 自定义依赖注入处理函数
- `post_init`: 响应器创建后对命令对象的额外除了
例如内置的 `DiscordSlashExtension`,其可自动将 Alconna 对象翻译成 slash指令并注册且将收到的指令交互事件转为指令供命令解析:
例如内置的 `DiscordSlashExtension`,其可自动将 Alconna 对象翻译成 slash 指令并注册,且将收到的指令交互事件转为指令供命令解析:
```python
from nonebot_plugin_alconna import Match, on_alconna
@@ -404,3 +387,9 @@ async def add(plugin: Match[str], priority: Match[int]):
async def remove(plugin: Match[str], time: Match[int]):
await matcher.finish(f"removed {plugin.result} with {time.result if time.available else -1}")
```
:::tip
全局的 Extension 可延迟加载 (即若有全局拓展加载于部分 AlconnaMatcher 之后,这部分响应器会被追加拓展)
:::

View File

@@ -3,6 +3,9 @@ sidebar_position: 5
description: 通用消息组件
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
# 通用消息组件
`uniseg` 模块属于 `nonebot-plugin-alconna` 的子插件,其提供了一套通用的消息组件,用于在 `nonebot-plugin-alconna` 下构建通用消息。
@@ -58,9 +61,16 @@ class File(Segment):
class Reply(Segment):
"""Reply对象表示一类回复消息"""
origin: Any
id: str
"""此处不一定是消息ID可能是其他ID如消息序号等"""
msg: Optional[Union[Message, str]]
origin: Optional[Any]
class Reference(Segment):
"""Reference对象表示一类引用消息。转发消息 (Forward) 也属于此类"""
id: Optional[str]
"""此处不一定是消息ID可能是其他ID如消息序号等"""
content: Optional[Union[Message, str, List[Union[RefNode, CustomNode]]]]
class Card(Segment):
type: Literal["xml", "json"]
@@ -76,7 +86,12 @@ class Other(Segment):
`nonebot-plugin-alconna.uniseg` 同时提供了一个类似于 `Message` 的 `UniMessage` 类型,其元素为经过通用标注转换后的通用消息段。
你可以通过提供的 `UniversalMessage` 或 `UniMsg` 依赖注入器来获取 `UniMessage`
你可以用如下方式获取 `UniMessage`
<Tabs groupId="get_unimsg">
<TabItem value="depend" label="使用依赖注入">
通过提供的 `UniversalMessage` 或 `UniMsg` 依赖注入器来获取 `UniMessage`。
```python
from nonebot_plugin_alconna.uniseg import UniMsg, At, Reply
@@ -93,9 +108,28 @@ async def _(msg: UniMsg):
...
```
不仅如此,你还可以通过 `UniMessage` 的 `export` 方法来**跨平台发送消息**。
</TabItem>
<TabItem value="method" label="使用 UniMessage.generate">
`UniMessage.export` 会通过传入 `bot: Bot` 参数读取适配器信息,并使用对应的生成方法把通用消息转为适配器对应的消息序列:
注意,`generate` 方法在响应器以外的地方如果不传入 `event` 与 `bot` 则无法处理 reply。
```python
from nonebot import Message, EventMessage
from nonebot_plugin_alconna.uniseg import UniMessage
matcher = on_xxx(...)
@matcher.handle()
async def _(message: Message = EventMessage()):
msg = await UniMessage.generate(message=message)
```
</TabItem>
</Tabs>
不仅如此,你还可以通过 `UniMessage` 的 `export` 与 `send` 方法来**跨平台发送消息**。
`UniMessage.export` 会通过传入的 `bot: Bot` 参数,或上下文中的 `Bot` 对象读取适配器信息,并使用对应的生成方法把通用消息转为适配器对应的消息序列
```python
from nonebot import Bot, on_command
@@ -104,8 +138,8 @@ from nonebot_plugin_alconna.uniseg import Image, UniMessage
test = on_command("test")
@test.handle()
async def handle_test(bot: Bot):
await test.send(await UniMessage(Image(path="path/to/img")).export(bot))
async def handle_test():
await test.send(await UniMessage(Image(path="path/to/img")).export())
```
而在 `AlconnaMatcher` 下,`got`, `send`, `reject` 等可以发送消息的方法皆支持使用 `UniMessage`,不需要手动调用 export 方法:
@@ -127,6 +161,49 @@ async def tt(target: At):
await test_cmd.send(UniMessage([target, "\ndone."]))
```
除此之外 `UniMessage.send` 方法基于 `UniMessage.export` 并调用各适配器下的发送消息方法,返回一个 `Receipt` 对象,用于修改/撤回消息:
```python
from nonebot import Bot, on_command
from nonebot_plugin_alconna.uniseg import UniMessage
test = on_command("test")
@test.handle()
async def handle():
receipt = await UniMessage.text("hello!").send(at_sender=True, reply_to=True)
await receipt.recall(delay=1)
```
:::caution
在响应器以外的地方,`bot` 参数必须手动传入。
:::
### 构造
如同 `Message`, `UniMessage` 可以传入单个字符串/消息段,或可迭代的字符串/消息段:
```python
from nonebot_plugin_alconna.uniseg import UniMessage, At
msg = UniMessage("Hello")
msg1 = UniMessage(At("user", "124"))
msg2 = UniMessage(["Hello", At("user", "124")])
```
`UniMessage` 上同时存在便捷方法,令其可以链式地添加消息段:
```python
from nonebot_plugin_alconna.uniseg import UniMessage, At, Image
msg = UniMessage.text("Hello").at("124").image(path="/path/to/img")
assert msg == UniMessage(
["Hello", At("user", "124"), Image(path="/path/to/img")]
)
```
### 获取消息纯文本
类似于 `Message.extract_plain_text()`,用于获取通用消息的纯文本。
@@ -277,7 +354,7 @@ UniMessage(At("user", "123"))
而在 `AlconnaMatcher` 中,{:XXX} 更进一步地提供了获取 `event` 和 `bot` 中的属性的功能
```python title=在 AlconnaMatcher 中使用通用消息段的拓展控制符
```python title=在AlconnaMatcher中使用通用消息段的拓展控制符
from arclet.alconna import Alconna, Args
from nonebot_plugin_alconna import At, Match, UniMessage, AlconnaMatcher, on_alconna
@@ -297,3 +374,57 @@ async def tt():
UniMessage.template("{:At(user, $event.get_user_id())} 已确认目标为 {target}")
)
```
另外也有 `$message_id` 与 `$target` 两个特殊值。
## 消息发送
前面提到,通用消息可用 `UniMessage.send` 发送自身:
```python
async def send(
self,
target: Union[Event, Target, None] = None,
bot: Optional[Bot] = None,
fallback: bool = True,
at_sender: Union[str, bool] = False,
reply_to: Union[str, bool] = False,
) -> Receipt:
```
实际上,`UniMessage` 同时提供了获取消息事件 id 与消息发送对象的方法:
```python
from nonebot import Event, Bot
from nonebot_plugin_alconna.uniseg import UniMessage, Target
matcher = on_xxx(...)
@matcher.handle()
asycn def _(bot: Bot, event: Event):
target: Target = UniMessage.get_target(event, bot)
msg_id: str = UniMessage.get_message_id(event, bot)
```
`send`, `get_target`, `get_message_id` 中与 `event`, `bot` 相关的参数都会尝试从上下文中获取对象。
其中,`Target`:
```python
class Target:
id: str
"""目标id若为群聊则为group_id或者channel_id若为私聊则为user_id"""
parent_id: str = ""
"""父级id若为频道则为guild_id其他情况为空字符串"""
channel: bool = False
"""是否为频道,仅当目标平台同时支持群聊和频道时有效"""
private: bool = False
"""是否为私聊"""
source: str = ""
"""可能的事件id"""
```
是用来描述响应消息时的发送对象。
同样的,你可以通过依赖注入的方式在响应器中直接获取它们。

View File

@@ -7,7 +7,7 @@ description: 杂项
## 特殊装饰器
`nonebot_plugin_alconna` 提供 了一个 `funcommand` 装饰器, 其用于将一个接受任意参数,
`nonebot_plugin_alconna` 提供 了一个 `funcommand` 装饰器其用于将一个接受任意参数,
返回 `str``Message``MessageSegment` 的函数转换为命令响应器。
```python

View File

@@ -37,6 +37,7 @@
"devDependencies": {
"@docusaurus/module-type-aliases": "^2.4.1",
"@tsconfig/docusaurus": "^1.0.5",
"@types/react-color": "^3.0.10",
"asciinema-player": "^3.5.0",
"typescript": "^4.7.4"
},

View File

@@ -0,0 +1,51 @@
import React from "react";
import { Form } from ".";
export default function AdapterForm(): JSX.Element {
const formItems = [
{
name: "基本信息",
items: [
{
type: "text",
name: "name",
labelText: "适配器名称",
},
{ type: "text", name: "description", labelText: "适配器描述" },
{
type: "text",
name: "homepage",
labelText: "适配器项目仓库/主页链接",
},
],
},
{
name: "包信息",
items: [
{ type: "text", name: "pypi", labelText: "PyPI 项目名" },
{ type: "text", name: "module", labelText: "适配器 import 包名" },
],
},
{
name: "其他信息",
items: [{ type: "tag", name: "tags", labelText: "标签" }],
},
];
const handleSubmit = (result: Record<string, string>) => {
window.open(
`https://github.com/nonebot/nonebot2/issues/new?${new URLSearchParams({
assignees: "",
labels: "Adapter",
projects: "",
template: "adapter_publish.yml",
title: `Adapter: ${result.name}`,
...result,
})}`
);
};
return (
<Form type="adapter" formItems={formItems} handleSubmit={handleSubmit} />
);
}

View File

@@ -0,0 +1,43 @@
import React from "react";
import { Form } from ".";
export default function BotForm(): JSX.Element {
const formItems = [
{
name: "基本信息",
items: [
{
type: "text",
name: "name",
labelText: "机器人名称",
},
{ type: "text", name: "description", labelText: "机器人描述" },
{
type: "text",
name: "homepage",
labelText: "机器人项目仓库/主页链接",
},
],
},
{
name: "其他信息",
items: [{ type: "tag", name: "tags", labelText: "标签" }],
},
];
const handleSubmit = (result: Record<string, string>) => {
window.open(
`https://github.com/nonebot/nonebot2/issues/new?${new URLSearchParams({
assignees: "",
labels: "Bot",
projects: "",
template: "bot_publish.yml",
title: `Bot: ${result.name}`,
...result,
})}`
);
};
return <Form type="bot" formItems={formItems} handleSubmit={handleSubmit} />;
}

View File

@@ -0,0 +1,110 @@
import React, { useState } from "react";
import clsx from "clsx";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ChromePicker, type ColorResult } from "react-color";
import "./styles.css";
import TagComponent from "@/components/Tag";
import { Tag as TagType } from "@/types/tag";
export type Props = {
allowTags: TagType[];
onTagUpdate: (tags: TagType[]) => void;
};
export default function TagFormItem({
allowTags,
onTagUpdate,
}: Props): JSX.Element {
const [tags, setTags] = useState<TagType[]>([]);
const [label, setLabel] = useState<TagType["label"]>("");
const [color, setColor] = useState<TagType["color"]>("#ea5252");
const slicedTags = Array.from(
new Set(
allowTags
.filter((tag) => tag.label.toLocaleLowerCase().includes(label))
.map((e) => e.label)
)
).slice(0, 5);
const validateTag = () => {
return label.length >= 1 && label.length <= 10;
};
const newTag = () => {
if (tags.length >= 3) {
return;
}
if (validateTag()) {
const tag: TagType = { label, color };
setTags([...tags, tag]);
onTagUpdate(tags);
}
};
const delTag = (index: number) => {
setTags(tags.filter((_, i) => i !== index));
onTagUpdate(tags);
};
const onChangeColor = (color: ColorResult) => {
setColor(color.hex as TagType["color"]);
};
return (
<>
<label className="flex flex-wrap gap-x-1 gap-y-1">
{tags.map((tag, index) => (
<TagComponent
key={index}
{...tag}
className="cursor-pointer"
onClick={() => delTag(index)}
/>
))}
{tags.length < 3 && (
<span
className={clsx("add-btn", { "add-btn-disabled": !validateTag() })}
onClick={() => newTag()}
>
<FontAwesomeIcon className="pr-1" icon={["fas", "plus"]} />
</span>
)}
</label>
<div className="form-item-container">
<span className="form-item-title"></span>
<div className="dropdown dropdown-bottom w-full">
<input
type="text"
value={label}
className="form-item form-item-input"
placeholder="请输入"
onChange={(e) => setLabel(e.target.value)}
/>
{slicedTags.length > 0 && (
<ul
tabIndex={0}
className="dropdown-content z-10 menu p-2 shadow bg-base-100 rounded-box w-52"
>
{slicedTags.map((tag) => (
<li key={tag}>
<a onClick={() => setLabel(tag)}>{tag}</a>
</li>
))}
</ul>
)}
</div>
</div>
<div className="form-item-container">
<span className="form-item-title"></span>
<ChromePicker
className="my-4 fix-input-color"
color={color}
disableAlpha={true}
onChangeComplete={onChangeColor}
/>
</div>
</>
);
}

View File

@@ -0,0 +1,35 @@
.add-btn {
@apply px-2 select-none cursor-pointer min-w-[64px] rounded-full hover:bg-opacity-[.08];
@apply flex justify-center items-center border-dashed border-2;
&-disabled {
@apply pointer-events-none opacity-60;
}
}
.form-item {
@apply basis-3/4;
&-title {
@apply basis-1/4 label-text;
}
&-input {
@apply input input-sm input-bordered;
}
&-select {
@apply select select-sm select-bordered;
}
&-container {
@apply flex items-center mt-2;
}
}
.fix-input-color {
@apply !text-base-content !bg-base-100;
input {
@apply !text-base-content !bg-base-100;
}
}

View File

@@ -0,0 +1,35 @@
import React from "react";
import { Form } from ".";
export default function PluginForm(): JSX.Element {
const formItems = [
{
name: "包信息",
items: [
{ type: "text", name: "pypi", labelText: "PyPI 项目名" },
{ type: "text", name: "module", labelText: "插件 import 包名" },
],
},
{
name: "其他信息",
items: [{ type: "tag", name: "tags", labelText: "标签" }],
},
];
const handleSubmit = (result: Record<string, string>) => {
window.open(
`https://github.com/nonebot/nonebot2/issues/new?${new URLSearchParams({
assignees: "",
labels: "Plugin",
projects: "",
template: "plugin_publish.yml",
title: `Plugin: ${result.pypi}`,
...result,
})}`
);
};
return (
<Form type="plugin" formItems={formItems} handleSubmit={handleSubmit} />
);
}

View File

@@ -0,0 +1,159 @@
import React, { useEffect, useState } from "react";
import clsx from "clsx";
import "./styles.css";
import TagFormItem from "./Items/Tag";
import { fetchRegistryData, Resource } from "@/libs/store";
import { Tag as TagType } from "@/types/tag";
export type FormItemData = {
type: string;
name: string;
labelText: string;
};
export type FormItemGroup = {
name: string;
items: FormItemData[];
};
export type Props = {
children?: React.ReactNode;
type: Resource["resourceType"];
formItems: FormItemGroup[];
handleSubmit: (result: Record<string, string>) => void;
};
export function Form({
type,
children,
formItems,
handleSubmit,
}: Props): JSX.Element {
const [currentStep, setCurrentStep] = useState<number>(0);
const [result, setResult] = useState<Record<string, string>>({});
const [allowTags, setAllowTags] = useState<TagType[]>([]);
// load tags asynchronously
useEffect(() => {
fetchRegistryData(type)
.then((data) =>
setAllowTags(
data
.filter((item) => item.tags.length > 0)
.map((ele) => ele.tags)
.flat()
)
)
.catch((e) => {
console.error(e);
});
}, [type]);
const setFormValue = (key: string, value: string) => {
setResult({ ...result, [key]: value });
};
const handleNextStep = () => {
const currentStepNames = formItems[currentStep].items.map(
(item) => item.name
);
if (currentStepNames.every((name) => result[name]))
setCurrentStep(currentStep + 1);
else return;
};
const onPrev = () => currentStep > 0 && setCurrentStep(currentStep - 1);
const onNext = () =>
currentStep < formItems.length - 1
? handleNextStep()
: handleSubmit(result);
return (
<>
<ul className="steps">
{formItems.map((item, index) => (
<li
key={index}
className={clsx("step", currentStep === index && "step-primary")}
>
{item.name}
</li>
))}
</ul>
<div className="form-control w-full min-h-[300px]">
{children ||
formItems[currentStep].items.map((item) => (
<FormItem
key={item.name}
type={item.type}
name={item.name}
labelText={item.labelText}
allowTags={allowTags}
result={result}
setResult={setFormValue}
/>
))}
</div>
<div className="flex justify-between">
<button
className={clsx("form-btn form-btn-prev", {
"form-btn-hidden": currentStep === 0,
})}
onClick={onPrev}
>
</button>
<button className="form-btn form-btn-next" onClick={onNext}>
{currentStep === formItems.length - 1 ? "提交" : "下一步"}
</button>
</div>
</>
);
}
export function FormItem({
type,
name,
labelText,
allowTags,
result,
setResult,
}: FormItemData & {
allowTags: TagType[];
result: Record<string, string>;
setResult: (key: string, value: string) => void;
}): JSX.Element {
return (
<>
<label className="label">
<span className="label-text">{labelText}</span>
</label>
{type === "text" && (
<input
value={result[name] || ""}
type="text"
name={name}
onChange={(e) => setResult(name, e.target.value)}
placeholder="请输入"
className={clsx("form-input", {
"form-input-error": !result[name],
})}
/>
)}
{type === "text" && !result[name] && (
<label className="label">
<span className="form-label form-label-error">{labelText}</span>
</label>
)}
{type === "tag" && (
<TagFormItem
allowTags={allowTags}
onTagUpdate={(tags) => setResult(name, JSON.stringify(tags))}
/>
)}
</>
);
}

View File

@@ -0,0 +1,31 @@
.form-btn {
@apply btn btn-sm btn-primary no-animation;
&-prev {
@apply mr-auto;
}
&-next {
@apply ml-auto;
}
&-hidden {
@apply !hidden;
}
}
.form-input {
@apply input input-bordered w-full;
&-error {
@apply input-error;
}
}
.form-label {
@apply text-xs;
&-error {
@apply text-error;
}
}

View File

@@ -120,11 +120,11 @@ function HomeFeatures(): JSX.Element {
"import nonebot",
"# OneBot",
"from nonebot.adapters.onebot.v11 import Adapter as OneBotAdapter",
"# QQ 频道",
"from nonebot.adapters.qqguild import Adapter as QQGuildAdapter",
"# QQ 机器人",
"from nonebot.adapters.qq import Adapter as QQAdapter",
"driver = nonebot.get_driver()",
"driver.register_adapter(OneBotAdapter)",
"driver.register_adapter(QQGuildAdapter)",
"driver.register_adapter(QQAdapter)",
].join("\n")}
</CodeBlock>
</HomeFeatureDoubleColumn>

View File

@@ -0,0 +1,61 @@
import React, { useEffect, useState } from "react";
import clsx from "clsx";
import IconClose from "@theme/Icon/Close";
import "./styles.css";
export type Props = {
children?: React.ReactNode;
className?: string;
title: string;
useCustomTitle?: boolean;
backdropExit?: boolean;
setOpenModal: (isOpen: boolean) => void;
};
export default function Modal({
setOpenModal,
className,
children,
useCustomTitle,
backdropExit,
title,
}: Props): JSX.Element {
const [transitionClass, setTransitionClass] = useState<string>("");
const onFadeIn = () => setTransitionClass("fade-in");
const onFadeOut = () => setTransitionClass("fade-out");
const onTransitionEnd = () =>
transitionClass === "fade-out" && setOpenModal(false);
useEffect(onFadeIn, []);
return (
<div className={clsx("nb-modal-root", className)}>
<div
className={clsx("nb-modal-backdrop", transitionClass)}
onTransitionEnd={onTransitionEnd}
onClick={() => backdropExit && onFadeOut()}
/>
<div className={clsx("nb-modal-container", transitionClass)}>
<div className="card bg-base-100 shadow-xl max-w-2xl">
<div className="card-body">
{!useCustomTitle && (
<div className="nb-modal-title">
{title}
<div className="card-actions ml-auto">
<button className="btn btn-square btn-sm" onClick={onFadeOut}>
<IconClose />
</button>
</div>
</div>
)}
{children}
</div>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,36 @@
.nb-modal {
&-title {
@apply flex items-center font-bold;
}
&-root {
@apply fixed z-[1300] inset-0 flex items-center justify-center;
}
&-container {
@apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 min-w-[400px] lg:min-w-[600px];
@apply opacity-0;
@apply transition-opacity duration-[225ms] ease-in-out delay-0;
&.fade-in {
@apply opacity-100;
}
&.fade-out {
@apply opacity-0;
}
}
&-backdrop {
@apply fixed flex right-0 bottom-0 top-0 left-0 bg-transparent opacity-0;
@apply transition-all duration-[225ms] ease-in-out delay-0 -z-[1];
&.fade-in {
@apply opacity-100 bg-black/50;
}
&.fade-out {
@apply opacity-0 bg-transparent;
}
}
}

View File

@@ -37,14 +37,7 @@ export default function ResourceCard({
const authorAvatar = `${authorLink}.png?size=80`;
return (
<div
className={clsx(
"resource-card-container",
onClick && "resource-card-container-clickable",
className
)}
onClick={onClick}
>
<div className={clsx("resource-card-container", className)}>
<div className="resource-card-header">
<div className="resource-card-header-title">
{resource.name}
@@ -55,7 +48,7 @@ export default function ResourceCard({
/>
)}
</div>
<div className="resource-card-header-expand">
<div className="resource-card-header-expand" onClick={onClick}>
<FontAwesomeIcon icon={["fas", "expand"]} />
</div>
</div>

View File

@@ -3,10 +3,7 @@
@apply flex flex-col gap-y-2 w-full min-h-[12rem] p-4;
@apply transition-colors duration-500 bg-base-200;
@apply border-2 border-base-200 rounded-lg;
&-clickable {
@apply cursor-pointer hover:border-primary;
}
@apply hover:border-primary;
}
&-header {
@@ -21,7 +18,7 @@
}
&-expand {
@apply flex-none fill-current;
@apply flex-none fill-current cursor-pointer;
}
}

View File

@@ -0,0 +1,154 @@
import React, { useEffect, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
// @ts-expect-error: we need to make package have type: module
import copy from "copy-text-to-clipboard";
import { PyPIData } from "./types";
import Tag from "@/components/Resource/Tag";
import type { Resource } from "@/libs/store";
import "./styles.css";
export type Props = {
resource: Resource;
};
export default function ResourceDetailCard({ resource }: Props) {
const [pypiData, setPypiData] = useState<PyPIData | null>(null);
const [copied, setCopied] = useState<boolean>(false);
const authorLink = `https://github.com/${resource.author}`;
const authorAvatar = `${authorLink}.png?size=100`;
const getProjectLink = (resource: Resource) => {
switch (resource.resourceType) {
case "plugin":
case "adapter":
case "driver":
return resource.project_link;
default:
return null;
}
};
const getModuleName = (resource: Resource) => {
switch (resource.resourceType) {
case "plugin":
case "adapter":
return resource.module_name;
case "driver":
return resource.module_name.replace(/~/, "nonebot.drivers.");
default:
return null;
}
};
const fetchPypiProject = (projectName: string) =>
fetch(`https://pypi.org/pypi/${projectName}/json`)
.then((response) => response.json())
.then((data) => setPypiData(data));
const copyCommand = (resource: Resource) => {
const projectLink = getProjectLink(resource);
if (projectLink) {
copy(`nb ${resource.resourceType} install ${projectLink}`);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
useEffect(() => {
const fetchingTasks: Promise<void>[] = [];
if (resource.resourceType === "bot" || resource.resourceType === "driver")
return;
if (resource.project_link)
fetchingTasks.push(fetchPypiProject(resource.project_link));
Promise.all(fetchingTasks);
}, [resource]);
const projectLink = getProjectLink(resource) || "无";
const moduleName = getModuleName(resource) || "无";
return (
<>
<div className="detail-card-header">
<img
src={authorAvatar}
className="detail-card-avatar"
decoding="async"
/>
<div className="detail-card-title">
<span className="detail-card-title-main">{resource.name}</span>
<span className="detail-card-title-sub">{resource.author}</span>
</div>
<button
className="detail-card-copy-button detail-card-copy-button-desktop"
onClick={() => copyCommand(resource)}
>
{copied ? "复制成功" : "复制安装命令"}
</button>
</div>
<div className="detail-card-body">
<div className="detail-card-body-left">
<span className="h-full">{resource.desc}</span>
<div className="resource-card-footer-tags mb-4">
{resource.tags.map((tag, index) => (
<Tag className="align-bottom" key={index} {...tag} />
))}
</div>
</div>
<div className="detail-card-body-divider" />
<div className="detail-card-body-right">
<div className="detail-card-meta-item">
<span>
<FontAwesomeIcon fixedWidth icon={["fab", "python"]} />{" "}
{(pypiData && pypiData.info.requires_python) || "无"}
</span>
</div>
<div className="detail-card-meta-item">
<FontAwesomeIcon fixedWidth icon={["fas", "file-zipper"]} />{" "}
{(pypiData &&
pypiData.releases[pypiData.info.version] &&
`${
pypiData.releases[pypiData.info.version].reduce(
(acc, curr) => acc + curr.size,
0
) / 1000
}K`) ||
"无"}
</div>
<div className="detail-card-meta-item">
<span>
<FontAwesomeIcon fixedWidth icon={["fas", "scale-balanced"]} />{" "}
{(pypiData && pypiData.info.license) || "无"}
</span>
</div>
<div className="detail-card-meta-item">
<FontAwesomeIcon fixedWidth icon={["fas", "tag"]} />{" "}
{(pypiData && pypiData.info.version) || "无"}
</div>
<div className="detail-card-meta-item">
<FontAwesomeIcon fixedWidth icon={["fas", "fingerprint"]} />{" "}
<span>{moduleName}</span>
</div>
<div className="detail-card-meta-item">
<FontAwesomeIcon fixedWidth icon={["fas", "cubes"]} />{" "}
<span>{projectLink}</span>
</div>
<button
className="detail-card-copy-button detail-card-copy-button-mobile w-full"
onClick={() => copyCommand(resource)}
>
{copied ? "复制成功" : "复制安装命令"}
</button>
</div>
</div>
</>
);
}

View File

@@ -0,0 +1,53 @@
.detail-card {
&-header {
@apply flex items-center align-middle;
}
&-avatar {
@apply mr-3 w-12 h-12 rounded-full;
}
&-title {
@apply inline-flex flex-col h-12 justify-start;
&-main {
@apply font-bold;
}
&-sub {
@apply text-sm;
}
}
&-copy-button {
@apply ml-auto btn btn-sm;
&-mobile {
@apply lg:hidden;
}
&-desktop {
@apply max-lg:hidden;
}
}
&-body {
@apply flex flex-col w-full lg:flex-row;
&-left {
@apply flex flex-col min-h-[150px] lg:basis-3/4 max-w-[65%];
}
&-divider {
@apply divider lg:divider-horizontal;
}
&-right {
@apply flex flex-col justify-start gap-y-2 lg:basis-1/4 max-w-[45%];
}
}
&-meta-item {
@apply text-sm truncate;
}
}

View File

@@ -0,0 +1,64 @@
export type Downloads = {
last_day: number;
last_month: number;
last_week: number;
};
export type Info = {
author: string;
author_email: string;
bugtrack_url: null;
classifiers: string[];
description: string;
description_content_type: string;
docs_url: null;
download_url: string;
downloads: Downloads;
home_page: string;
keywords: string;
license: string;
maintainer: string;
maintainer_email: string;
name: string;
package_url: string;
platform: null;
project_url: string;
release_url: string;
requires_dist: string[];
requires_python: string;
summary: string;
version: string;
yanked: boolean;
yanked_reason: null;
};
export interface Digests {
blake2b_256: string;
md5: string;
sha256: string;
}
export type Releases = {
comment_text: string;
digests: Digests;
downloads: number;
filename: string;
has_sig: boolean;
md5_digest: string;
packagetype: string;
python_version: string;
requires_python: string;
size: number;
upload_time: Date;
upload_time_iso_8601: Date;
url: string;
yanked: boolean;
yanked_reason: null;
};
export type PyPIData = {
info: Info;
last_serial: number;
releases: { [key: string]: Releases[] };
urls: URL[];
vulnerabilities: unknown[];
};

View File

@@ -5,8 +5,11 @@ import { usePagination } from "react-use-pagination";
import Admonition from "@theme/Admonition";
import AdapterForm from "@/components/Form/Adapter";
import Modal from "@/components/Modal";
import Paginate from "@/components/Paginate";
import ResourceCard from "@/components/Resource/Card";
import ResourceDetailCard from "@/components/Resource/DetailCard";
import Searcher from "@/components/Searcher";
import StoreToolbar, { type Action } from "@/components/Store/Toolbar";
import { authorFilter, tagFilter } from "@/libs/filter";
@@ -21,6 +24,9 @@ export default function AdapterPage(): JSX.Element {
const loading = adapters === null;
const [error, setError] = useState<Error | null>(null);
const [isOpenModal, setIsOpenModal] = useState<boolean>(false);
const [isOpenCardModal, setIsOpenCardModal] = useState<boolean>(false);
const [clickedAdapter, setClickedAdapter] = useState<Adapter | null>(null);
const {
filteredResources: filteredAdapters,
@@ -69,16 +75,13 @@ export default function AdapterPage(): JSX.Element {
label: "发布适配器",
icon: ["fas", "plus"],
onClick: () => {
// TODO: open adapter release modal
window.open(
"https://github.com/nonebot/nonebot2/issues/new?template=adapter_publish.yml&title=Adapter%3A+%7Bname%7D&labels=Adapter"
);
setIsOpenModal(true);
},
};
const onCardClick = useCallback((adapter: Adapter) => {
// TODO: open adapter modal
console.log(adapter, "clicked");
setClickedAdapter(adapter);
setIsOpenCardModal(true);
}, []);
const onCardTagClick = useCallback(
@@ -170,6 +173,25 @@ export default function AdapterPage(): JSX.Element {
nextEnabled={nextEnabled}
previousEnabled={previousEnabled}
/>
{isOpenModal && (
<Modal
className="not-prose"
title="发布适配器"
setOpenModal={setIsOpenModal}
>
<AdapterForm />
</Modal>
)}
{isOpenCardModal && (
<Modal
className="not-prose"
title="适配器详情"
backdropExit
setOpenModal={setIsOpenCardModal}
>
{clickedAdapter && <ResourceDetailCard resource={clickedAdapter} />}
</Modal>
)}
</>
);
}

View File

@@ -5,6 +5,8 @@ import { usePagination } from "react-use-pagination";
import Admonition from "@theme/Admonition";
import BotForm from "@/components/Form/Bot";
import Modal from "@/components/Modal";
import Paginate from "@/components/Paginate";
import ResourceCard from "@/components/Resource/Card";
import Searcher from "@/components/Searcher";
@@ -21,6 +23,7 @@ export default function PluginPage(): JSX.Element {
const loading = bots === null;
const [error, setError] = useState<Error | null>(null);
const [isOpenModal, setIsOpenModal] = useState<boolean>(false);
const {
filteredResources: filteredBots,
@@ -69,10 +72,7 @@ export default function PluginPage(): JSX.Element {
label: "发布机器人",
icon: ["fas", "plus"],
onClick: () => {
// TODO: open bot release modal
window.open(
"https://github.com/nonebot/nonebot2/issues/new?template=bot_publish.yml&title=Bot%3A+%7Bname%7D&labels=Bot"
);
setIsOpenModal(true);
},
};
@@ -164,6 +164,15 @@ export default function PluginPage(): JSX.Element {
nextEnabled={nextEnabled}
previousEnabled={previousEnabled}
/>
{isOpenModal && (
<Modal
className="not-prose"
title="发布机器人"
setOpenModal={setIsOpenModal}
>
<BotForm />
</Modal>
)}
</>
);
}

View File

@@ -5,8 +5,10 @@ import { usePagination } from "react-use-pagination";
import Admonition from "@theme/Admonition";
import Modal from "@/components/Modal";
import Paginate from "@/components/Paginate";
import ResourceCard from "@/components/Resource/Card";
import ResourceDetailCard from "@/components/Resource/DetailCard";
import Searcher from "@/components/Searcher";
import { authorFilter, tagFilter } from "@/libs/filter";
import { useSearchControl } from "@/libs/search";
@@ -19,6 +21,8 @@ export default function DriverPage(): JSX.Element {
const loading = drivers === null;
const [error, setError] = useState<Error | null>(null);
const [isOpenCardModal, setIsOpenCardModal] = useState<boolean>(false);
const [clickedDriver, setClickedDriver] = useState<Driver | null>(null);
const {
filteredResources: filteredDrivers,
@@ -59,8 +63,8 @@ export default function DriverPage(): JSX.Element {
}, []);
const onCardClick = useCallback((driver: Driver) => {
// TODO: open driver modal
console.log(driver, "clicked");
setClickedDriver(driver);
setIsOpenCardModal(true);
}, []);
const onCardTagClick = useCallback(
@@ -146,6 +150,17 @@ export default function DriverPage(): JSX.Element {
nextEnabled={nextEnabled}
previousEnabled={previousEnabled}
/>
{isOpenCardModal && (
<Modal
className="not-prose"
useCustomTitle
backdropExit
title="驱动器详情"
setOpenModal={setIsOpenCardModal}
>
{clickedDriver && <ResourceDetailCard resource={clickedDriver} />}
</Modal>
)}
</>
);
}

View File

@@ -5,8 +5,11 @@ import { usePagination } from "react-use-pagination";
import Admonition from "@theme/Admonition";
import PluginForm from "@/components/Form/Plugin";
import Modal from "@/components/Modal";
import Paginate from "@/components/Paginate";
import ResourceCard from "@/components/Resource/Card";
import ResourceDetailCard from "@/components/Resource/DetailCard";
import Searcher from "@/components/Searcher";
import StoreToolbar, { type Action } from "@/components/Store/Toolbar";
import { authorFilter, tagFilter } from "@/libs/filter";
@@ -21,6 +24,9 @@ export default function PluginPage(): JSX.Element {
const loading = plugins === null;
const [error, setError] = useState<Error | null>(null);
const [isOpenModal, setIsOpenModal] = useState<boolean>(false);
const [isOpenCardModal, setIsOpenCardModal] = useState<boolean>(false);
const [clickedPlugin, setClickedPlugin] = useState<Plugin | null>(null);
const {
filteredResources: filteredPlugins,
@@ -69,16 +75,13 @@ export default function PluginPage(): JSX.Element {
label: "发布插件",
icon: ["fas", "plus"],
onClick: () => {
// TODO: open plugin release modal
window.open(
"https://github.com/nonebot/nonebot2/issues/new?template=plugin_publish.yml&title=Plugin%3A+%7Bname%7D&labels=Plugin"
);
setIsOpenModal(true);
},
};
const onCardClick = useCallback((plugin: Plugin) => {
// TODO: open plugin modal
console.log(plugin, "clicked");
setClickedPlugin(plugin);
setIsOpenCardModal(true);
}, []);
const onCardTagClick = useCallback(
@@ -167,6 +170,26 @@ export default function PluginPage(): JSX.Element {
nextEnabled={nextEnabled}
previousEnabled={previousEnabled}
/>
{isOpenModal && (
<Modal
className="not-prose"
title="发布插件"
setOpenModal={setIsOpenModal}
>
<PluginForm />
</Modal>
)}
{isOpenCardModal && (
<Modal
className="not-prose"
useCustomTitle
backdropExit
title="插件详情"
setOpenModal={setIsOpenCardModal}
>
{clickedPlugin && <ResourceDetailCard resource={clickedPlugin} />}
</Modal>
)}
</>
);
}

View File

@@ -0,0 +1,31 @@
import React from "react";
import clsx from "clsx";
import "./styles.css";
import { pickTextColor } from "@/libs/color";
import { Tag as TagType } from "@/types/tag";
export default function Tag({
label,
color,
className,
onClick,
}: TagType & {
className?: string;
onClick?: React.MouseEventHandler<HTMLSpanElement>;
}): JSX.Element {
return (
<span
className={clsx("tag", className)}
style={{
backgroundColor: color,
color: pickTextColor(color, "#fff", "#000"),
}}
onClick={onClick}
>
{label}
</span>
);
}

View File

@@ -0,0 +1,3 @@
.tag {
@apply font-mono inline-flex px-3 rounded-full items-center align-middle;
}

View File

@@ -5,6 +5,56 @@ toc_max_heading_level: 2
# 更新日志
## v2.1.3
### 🐛 Bug 修复
- Fix: 新增 `Lifespan.on_ready()` 供适配器使用 [@ProgramRipper](https://github.com/ProgramRipper) ([#2483](https://github.com/nonebot/nonebot2/pull/2483))
- Fix: 忽略 Pyright 对动态类创建的检查错误 [@yanyongyu](https://github.com/yanyongyu) ([#2486](https://github.com/nonebot/nonebot2/pull/2486))
### 📝 文档
- Docs: 商店详情卡片添加宽度限制与文本省略 [@StarHeartHunt](https://github.com/StarHeartHunt) ([#2473](https://github.com/nonebot/nonebot2/pull/2473))
- Docs: 修复商店发布 上一步 按钮显示问题 [@StarHeartHunt](https://github.com/StarHeartHunt) ([#2464](https://github.com/nonebot/nonebot2/pull/2464))
- Docs: 添加商店表单支持 [@StarHeartHunt](https://github.com/StarHeartHunt) ([#2460](https://github.com/nonebot/nonebot2/pull/2460))
- Docs: 修复事件后处理函数类型 docstring 错误 [@lgc2333](https://github.com/lgc2333) ([#2459](https://github.com/nonebot/nonebot2/pull/2459))
- Docs: 修改 QQ 频道为 QQ [@bingqiu456](https://github.com/bingqiu456) ([#2457](https://github.com/nonebot/nonebot2/pull/2457))
- Docs: 更新最佳实践的 Alconna 部分 [@RF-Tar-Railt](https://github.com/RF-Tar-Railt) ([#2443](https://github.com/nonebot/nonebot2/pull/2443))
### 💫 杂项
- Plugin: 更新 splatoon3 插件地址 [@Cypas](https://github.com/Cypas) ([#2494](https://github.com/nonebot/nonebot2/pull/2494))
- Plugin: 删除不维护的 `eitherchoice` 插件 [@lgc2333](https://github.com/lgc2333) ([#2491](https://github.com/nonebot/nonebot2/pull/2491))
- Plugin: 移除不再维护的插件 [@j1g5awi](https://github.com/j1g5awi) ([#2474](https://github.com/nonebot/nonebot2/pull/2474))
- Plugin: 移除不再维护的插件 [@NCBM](https://github.com/NCBM) ([#2472](https://github.com/nonebot/nonebot2/pull/2472))
- Plugin: 移除不再维护的插件 [@MeetWq](https://github.com/MeetWq) ([#2471](https://github.com/nonebot/nonebot2/pull/2471))
- CI: 测试矩阵添加 Python 3.12 [@StarHeartHunt](https://github.com/StarHeartHunt) ([#2441](https://github.com/nonebot/nonebot2/pull/2441))
### 🍻 插件发布
- Plugin: Phigros查分器(Adapter-qq) [@noneflow](https://github.com/noneflow) ([#2497](https://github.com/nonebot/nonebot2/pull/2497))
- Plugin: Riffusion [@noneflow](https://github.com/noneflow) ([#2493](https://github.com/nonebot/nonebot2/pull/2493))
- Plugin: nonebot_plugin_longtu [@noneflow](https://github.com/noneflow) ([#2490](https://github.com/nonebot/nonebot2/pull/2490))
- Plugin: CNRail [@noneflow](https://github.com/noneflow) ([#2488](https://github.com/nonebot/nonebot2/pull/2488))
- Plugin: ba塔罗牌运势与魔法占卜 [@noneflow](https://github.com/noneflow) ([#2481](https://github.com/nonebot/nonebot2/pull/2481))
- Plugin: 群聊 NSFW 图片检测 [@noneflow](https://github.com/noneflow) ([#2477](https://github.com/nonebot/nonebot2/pull/2477))
- Plugin: sm.ms图床 [@noneflow](https://github.com/noneflow) ([#2470](https://github.com/nonebot/nonebot2/pull/2470))
- Plugin: 文件托管支持 [@noneflow](https://github.com/noneflow) ([#2468](https://github.com/nonebot/nonebot2/pull/2468))
- Plugin: 短链接服务支持 [@noneflow](https://github.com/noneflow) ([#2466](https://github.com/nonebot/nonebot2/pull/2466))
- Plugin: 用户 [@noneflow](https://github.com/noneflow) ([#2463](https://github.com/nonebot/nonebot2/pull/2463))
- Plugin: DALL-E 3绘图 [@noneflow](https://github.com/noneflow) ([#2452](https://github.com/nonebot/nonebot2/pull/2452))
- Plugin: 局域网唤醒 [@noneflow](https://github.com/noneflow) ([#2449](https://github.com/nonebot/nonebot2/pull/2449))
- Plugin: nonebot-plugin-bertvits2 [@noneflow](https://github.com/noneflow) ([#2446](https://github.com/nonebot/nonebot2/pull/2446))
- Plugin: Nonebot2 Any 多平台服务 [@noneflow](https://github.com/noneflow) ([#2442](https://github.com/nonebot/nonebot2/pull/2442))
### 🍻 机器人发布
- Bot: Sakiko [@noneflow](https://github.com/noneflow) ([#2439](https://github.com/nonebot/nonebot2/pull/2439))
### 🍻 适配器发布
- Adapter: DoDo [@noneflow](https://github.com/noneflow) ([#2456](https://github.com/nonebot/nonebot2/pull/2456))
## v2.1.2
### 🚀 新功能

View File

@@ -0,0 +1,49 @@
---
sidebar_position: 0
id: index
slug: /
---
# 概览
NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架(下称 NoneBot它基于 Python 的类型注解和异步优先特性兼容同步能够为你的需求实现提供便捷灵活的支持。同时NoneBot 拥有大量的开发者为其开发插件,用户无需编写任何代码,仅需完成环境配置及插件安装,就可以正常使用 NoneBot。
需要注意的是NoneBot 仅支持 **Python 3.8 以上版本**
## 特色
### 异步优先
NoneBot 基于 Python [asyncio](https://docs.python.org/zh-cn/3/library/asyncio.html) 编写,并在异步机制的基础上进行了一定程度的同步函数兼容。
### 完整的类型注解
NoneBot 参考 [PEP 484](https://www.python.org/dev/peps/pep-0484/) 等 PEP 完整实现了类型注解,通过 PyrightPylance 检查。配合编辑器的类型推导功能,能将绝大多数的 Bug 杜绝在编辑器中([编辑器支持](./editor-support))。
### 开箱即用
NoneBot 提供了使用便捷、具有交互式功能的命令行工具--`nb-cli`,使得用户初次接触 NoneBot 时更容易上手。使用方法请阅读本文档[指南](./quick-start.mdx)以及 [CLI 文档](https://cli.nonebot.dev/)。
### 插件系统
插件系统是 NoneBot 的核心,通过它可以实现机器人的模块化以及功能扩展,便于维护和管理。
### 依赖注入系统
NoneBot 采用了一套自行定义的依赖注入系统,可以让事件的处理过程更加的简洁、清晰,增加代码的可读性,减少代码冗余。
#### 什么是依赖注入
[**『依赖注入』**](https://zh.m.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC)意思是,在编程中,有一种方法可以让你的代码声明它工作和使用所需要的东西,即**『依赖』**。
系统(在这里是指 NoneBot将负责做任何需要的事情为你的代码提供这些必要依赖即**『注入』**依赖性)
这在你有以下情形的需求时非常有用:
- 这部分代码拥有共享的逻辑(同样的代码逻辑多次重复)
- 共享数据库以及网络请求连接会话
- 比如 `httpx.AsyncClient``aiohttp.ClientSession``sqlalchemy.Session`
- 机器人用户权限检查以及认证
- 还有更多...
它在完成上述工作的同时,还能尽量减少代码的耦合和重复

View File

@@ -0,0 +1,161 @@
---
sidebar_position: 1
description: 注册适配器与指定平台交互
options:
menu:
- category: advanced
weight: 20
---
# 使用适配器
适配器 (Adapter) 是机器人与平台交互的核心桥梁,它负责在驱动器和机器人插件之间转换与传递消息。
## 适配器功能与组成
适配器通常有两种功能,分别是**接收事件**和**调用平台接口**。其中,接收事件是指将驱动器收到的事件消息转换为 NoneBot 定义的事件模型,然后交由机器人插件处理;调用平台接口是指将机器人插件调用平台接口的数据转换为平台指定的格式,然后交由驱动器发送,并接收接口返回数据。
为了实现这两种功能,适配器通常由四个部分组成:
- **Adapter**:负责转换事件和调用接口,正确创建 Bot 对象并注册到 NoneBot 中。
- **Bot**:负责存储平台机器人相关信息,并提供回复事件的方法。
- **Event**:负责定义事件内容,以及事件主体对象。
- **Message**:负责正确序列化消息,以便机器人插件处理。
## 注册适配器
在使用适配器之前,我们需要先将适配器注册到驱动器中,这样适配器就可以通过驱动器接收事件和调用接口了。我们以 Console 适配器为例,来看看如何注册适配器:
```python {2,5} title=bot.py
import nonebot
from nonebot.adapters.console import Adapter
driver = nonebot.get_driver()
driver.register_adapter(Adapter)
```
我们首先需要从适配器模块中导入所需要的适配器类,然后通过驱动器的 `register_adapter` 方法将适配器注册到驱动器中即可。如果我们需要多平台支持,可以多次调用 `register_adapter` 方法来注册多个适配器。
## 获取已注册的适配器
NoneBot 提供了 `get_adapter` 方法来获取已注册的适配器,我们可以通过适配器的名称或类型来获取指定的适配器实例:
```python
import nonebot
from nonebot.adapters.console import Adapter
adapters = nonebot.get_adapters()
console_adapter = nonebot.get_adapter(Adapter)
console_adapter = nonebot.get_adapter(Adapter.get_name())
```
## 获取 Bot 对象
当前所有适配器已连接的 Bot 对象可以通过 `get_bots` 方法获取,这是一个以机器人 ID 为键的字典:
```python
import nonebot
bots = nonebot.get_bots()
```
我们也可以通过 `get_bot` 方法获取指定 ID 的 Bot 对象。如果省略 ID 参数,将会返回所有 Bot 中的第一个:
```python
import nonebot
bot = nonebot.get_bot("bot_id")
```
如果需要获取指定适配器连接的 Bot 对象,我们可以通过适配器的 `bots` 属性获取,这也是一个以机器人 ID 为键的字典:
```python
import nonebot
from nonebot.adapters.console import Adapter
console_adapter = nonebot.get_adapter(Adapter)
bots = console_adapter.bots
```
Bot 对象都具有一个 `self_id` 属性,它是机器人的唯一 ID由适配器填写通常为机器人的帐号 ID 或者 APP ID。
## 获取事件通用信息
适配器的所有事件模型均继承自 `Event` 基类,在[事件类型与重载](../appendices/overload.md)一节中,我们也提到了如何使用基类抽象方法来获取事件通用信息。基类能提供如下信息:
### 事件类型
事件类型通常为 `meta_event`、`message`、`notice`、`request`。
```python
type: str = event.get_type()
```
### 事件名称
事件名称由适配器定义,通常用于日志记录。
```python
name: str = event.get_event_name()
```
### 事件描述
事件描述由适配器定义,通常用于日志记录。
```python
description: str = event.get_event_description()
```
### 事件日志字符串
事件日志字符串由事件名称和事件描述组成,用于日志记录。
```python
log: str = event.get_log_string()
```
### 事件主体 ID
事件主体 ID 通常为机器人用户 ID。
```python
user_id: str = event.get_user_id()
```
### 事件会话 ID
事件会话 ID 通常为机器人用户 ID 与群聊/频道 ID 组合而成。
```python
session_id: str = event.get_session_id()
```
### 事件消息
如果事件包含消息,则可以通过该方法获取,否则会产生异常。
```python
message: Message = event.get_message()
```
### 事件纯文本消息
通常为事件消息的纯文本内容,如果事件不包含消息,则会产生异常。
```python
text: str = event.get_plaintext()
```
### 事件是否与机器人有关
由适配器实现的判断,通常将事件目标主体为机器人、消息中包含“@机器人”或以“机器人的昵称”开始视为与机器人有关。
```python
is_tome: bool = event.is_tome()
```
## 更多
官方支持的适配器和社区贡献的适配器均可在[商店](/store/adapters)中查看。如果你想要开发自己的适配器,可以参考[开发文档](../developer/adapter-writing.md)。欢迎通过商店发布你的适配器。

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,287 @@
---
sidebar_position: 0
description: 选择合适的驱动器运行机器人
options:
menu:
- category: advanced
weight: 10
---
# 选择驱动器
驱动器 (Driver) 是机器人运行的基石,它是机器人初始化的第一步,主要负责数据收发。
:::important 提示
驱动器的选择通常与机器人所使用的协议适配器相关,如果不知道该选择哪个驱动器,可以先阅读相关协议适配器文档说明。
:::
:::tip 提示
如何**安装**驱动器请参考[安装驱动器](../tutorial/store.mdx#安装驱动器)。
:::
## 驱动器类型
驱动器类型大体上可以分为两种:
- `Forward`:即客户端型驱动器,多用于使用 HTTP 轮询,连接 WebSocket 服务器等情形。
- `Reverse`:即服务端型驱动器,多用于使用 WebHook接收 WebSocket 客户端连接等情形。
客户端型驱动器可以分为以下两种:
1. 异步发送 HTTP 请求,自定义 `HTTP Method``URL``Header``Body``Cookie``Proxy``Timeout` 等。
2. 异步建立 WebSocket 连接上下文,自定义 `WebSocket URL``Header``Cookie``Proxy``Timeout` 等。
服务端型驱动器目前有:
1. ASGI 应用框架,具有以下功能:
- 协议适配器自定义 HTTP 上报地址以及对上报数据处理的回调函数。
- 协议适配器自定义 WebSocket 连接请求地址以及对 WebSocket 请求处理的回调函数。
- 用户可以向 ASGI 应用添加任何服务端相关功能,如:[添加自定义路由](./routing.md)。
## 配置驱动器
驱动器的配置方法已经在[配置](../appendices/config.mdx)章节中简单进行了介绍,这里将详细介绍驱动器配置的格式。
NoneBot 中的客户端和服务端型驱动器可以相互配合使用,但服务端型驱动器**仅能选择一个**。所有驱动器模块都会包含一个 `Driver` 子类,即驱动器类,他可以作为驱动器单独运行。同时,客户端驱动器模块中还会提供一个 `Mixin` 子类,用于在与其他驱动器配合使用时加载。因此,驱动器配置格式采用特殊语法:`<module>[:<Driver>][+<module>[:<Mixin>]]*`
其中,`<module>` 代表**驱动器模块路径**`<Driver>` 代表**驱动器类名**,默认为 `Driver``<Mixin>` 代表**驱动器混入类名**,默认为 `Mixin`。即,我们需要选择一个主要驱动器,然后在其基础上配合使用其他驱动器的功能。主要驱动器可以为客户端或服务端类型,但混入类驱动器只能为客户端类型。
特别的,为了简化内置驱动器模块路径,我们可以使用 `~` 符号作为内置驱动器模块路径的前缀,如 `~fastapi` 代表使用内置驱动器 `fastapi`。NoneBot 内置了多个驱动器适配,但需要安装额外依赖才能使用,具体请参考[安装驱动器](../tutorial/store.mdx#安装驱动器)。常见的驱动器配置如下:
```dotenv
DRIVER=~fastapi
DRIVER=~aiohttp
DRIVER=~httpx+~websockets
DRIVER=~fastapi+~httpx+~websockets
```
## 获取驱动器
在 NoneBot 框架初始化完成后,我们就可以通过 `get_driver()` 方法获取全局驱动器实例:
```python
from nonebot import get_driver
driver = get_driver()
```
## 内置驱动器
### None
**类型:**服务端驱动器
NoneBot 内置的空驱动器,不提供任何收发数据功能,可以在不需要外部网络连接时使用。
```env
DRIVER=~none
```
### FastAPI默认
**类型:**ASGI 服务端驱动器
> FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints.
[FastAPI](https://fastapi.tiangolo.com/) 是一个易上手、高性能的异步 Web 框架,具有极佳的编写体验。 FastAPI 可以通过类型注解、依赖注入等方式实现输入参数校验、自动生成 API 文档等功能,也可以挂载其他 ASGI、WSGI 应用。
```env
DRIVER=~fastapi
```
#### FastAPI 配置项
##### `fastapi_openapi_url`
类型:`str | None`
默认值:`None`
说明:`FastAPI` 提供的 `OpenAPI` JSON 定义地址,如果为 `None`,则不提供 `OpenAPI` JSON 定义。
##### `fastapi_docs_url`
类型:`str | None`
默认值:`None`
说明:`FastAPI` 提供的 `Swagger` 文档地址,如果为 `None`,则不提供 `Swagger` 文档。
##### `fastapi_redoc_url`
类型:`str | None`
默认值:`None`
说明:`FastAPI` 提供的 `ReDoc` 文档地址,如果为 `None`,则不提供 `ReDoc` 文档。
##### `fastapi_include_adapter_schema`
类型:`bool`
默认值:`True`
说明:`FastAPI` 提供的 `OpenAPI` JSON 定义中是否包含适配器路由的 `Schema`
##### `fastapi_reload`
:::caution 警告
不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。
```bash
nb run --reload
```
开启该功能后,在 uvicorn 运行时FastAPI 提供的 ASGI 底层,即 reload 功能的实际来源asyncio 使用的事件循环会被 uvicorn 从默认的 `ProactorEventLoop` 强制切换到 `SelectorEventLoop`
> 相关信息参考 [uvicorn#529](https://github.com/encode/uvicorn/issues/529)[uvicorn#1070](https://github.com/encode/uvicorn/pull/1070)[uvicorn#1257](https://github.com/encode/uvicorn/pull/1257)
后者(`SelectorEventLoop`)在 Windows 平台的可使用性不如前者(`ProactorEventLoop`),包括但不限于
1. 不支持创建子进程
2. 最多只支持 512 个套接字
3. ...
> 具体信息参考 [Python 文档](https://docs.python.org/zh-cn/3/library/asyncio-platforms.html#windows)
所以,一些使用了 asyncio 的库因此可能无法正常工作,如:
1. [playwright](https://playwright.dev/python/docs/library#incompatible-with-selectoreventloop-of-asyncio-on-windows)
如果在开启该功能后,原本**正常运行**的代码报错,且打印的异常堆栈信息和 asyncio 有关(异常一般为 `NotImplementedError`
你可能就需要考虑相关库对事件循环的支持,以及是否启用该功能。
:::
类型:`bool`
默认值:`False`
说明:是否开启 `uvicorn``reload` 功能,需要在机器人入口文件提供 ASGI 应用路径。
```python title=bot.py
app = nonebot.get_asgi()
nonebot.run(app="bot:app")
```
##### `fastapi_reload_dirs`
类型:`List[str] | None`
默认值:`None`
说明:重载监控文件夹列表,默认为 uvicorn 默认值
##### `fastapi_reload_delay`
类型:`float | None`
默认值:`None`
说明:重载延迟,默认为 uvicorn 默认值
##### `fastapi_reload_includes`
类型:`List[str] | None`
默认值:`None`
说明:要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
##### `fastapi_reload_excludes`
类型:`List[str] | None`
默认值:`None`
说明:不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
##### `fastapi_extra`
类型:`Dist[str, Any]`
默认值:`{}`
说明:传递给 `FastAPI` 的其他参数
### Quart
**类型:**ASGI 服务端驱动器
> Quart is an asyncio reimplementation of the popular Flask microframework API.
[Quart](https://quart.palletsprojects.com/) 是一个类 Flask 的异步版本,拥有与 Flask 非常相似的接口和使用方法。
```env
DRIVER=~quart
```
#### Quart 配置项
##### `quart_reload`
:::caution 警告
不推荐开启该配置项,在 Windows 平台上开启该功能有可能会造成预料之外的影响!替代方案:使用 `nb-cli` 命令行工具以及参数 `--reload` 启动 NoneBot。
```bash
nb run --reload
```
:::
类型:`bool`
默认值:`False`
说明:是否开启 `uvicorn` 的 `reload` 功能,需要在机器人入口文件提供 ASGI 应用路径。
```python title=bot.py
app = nonebot.get_asgi()
nonebot.run(app="bot:app")
```
##### `quart_reload_dirs`
类型:`List[str] | None`
默认值:`None`
说明:重载监控文件夹列表,默认为 uvicorn 默认值
##### `quart_reload_delay`
类型:`float | None`
默认值:`None`
说明:重载延迟,默认为 uvicorn 默认值
##### `quart_reload_includes`
类型:`List[str] | None`
默认值:`None`
说明:要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
##### `quart_reload_excludes`
类型:`List[str] | None`
默认值:`None`
说明:不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
##### `quart_extra`
类型:`Dist[str, Any]`
默认值:`{}`
说明:传递给 `Quart` 的其他参数
### HTTPX
**类型:**HTTP 客户端驱动器
:::caution 注意
本驱动器仅支持 HTTP 请求,不支持 WebSocket 连接请求。
:::
> [HTTPX](https://www.python-httpx.org/) is a fully featured HTTP client for Python 3, which provides sync and async APIs, and support for both HTTP/1.1 and HTTP/2.
```env
DRIVER=~httpx
```
### websockets
**类型:**WebSocket 客户端驱动器
:::caution 注意
本驱动器仅支持 WebSocket 连接请求,不支持 HTTP 请求。
:::
> [websockets](https://websockets.readthedocs.io/) is a library for building WebSocket servers and clients in Python with a focus on correctness, simplicity, robustness, and performance.
```env
DRIVER=~websockets
```
### AIOHTTP
**类型:**HTTP/WebSocket 客户端驱动器
> [AIOHTTP](https://docs.aiohttp.org/): Asynchronous HTTP Client/Server for asyncio and Python.
```env
DRIVER=~aiohttp
```

View File

@@ -0,0 +1,40 @@
---
sidebar_position: 10
description: 自定义事件响应器存储
options:
menu:
- category: advanced
weight: 110
---
# 事件响应器存储
事件响应器是 NoneBot 处理事件的核心,它们默认存储在一个字典中。在进入会话状态后,事件响应器将会转为临时响应器,作为最高优先级同样存储于该字典中。因此,事件响应器的存储类似于会话存储,它决定了整个 NoneBot 对事件的处理行为。
NoneBot 默认使用 Python 的字典将事件响应器存储于内存中,但是我们也可以自定义事件响应器存储,将事件响应器存储于其他地方,例如 Redis 等。这样我们就可以实现持久化、在多实例间共享会话状态等功能。
## 编写存储提供者
事件响应器的存储提供者 `MatcherProvider` 抽象类继承自 `MutableMapping[int, list[type[Matcher]]]`,即以优先级为键,以事件响应器列表为值的映射。我们可以方便地进行逐优先级事件传播。
编写一个自定义的存储提供者,只需要继承并实现 `MatcherProvider` 抽象类:
```python
from nonebot.matcher import MatcherProvider
class CustomProvider(MatcherProvider):
...
```
## 设置存储提供者
我们可以通过 `matchers.set_provider` 方法设置存储提供者:
```python {3}
from nonebot.matcher import matchers
matchers.set_provider(CustomProvider)
assert isinstance(matchers.provider, CustomProvider)
```

View File

@@ -0,0 +1,338 @@
---
sidebar_position: 5
description: 事件响应器组成与内置响应规则
options:
menu:
- category: advanced
weight: 60
---
# 事件响应器进阶
在[指南](../tutorial/matcher.md)与[深入](../appendices/rule.md)中,我们已经介绍了事件响应器的基本用法以及响应规则、权限控制等功能。在这一节中,我们将介绍事件响应器的组成,内置的响应规则,与第三方响应规则拓展。
:::tip 提示
事件响应器允许继承,你可以通过直接继承 `Matcher` 类来创建一个新的事件响应器。
:::
## 事件响应器组成
### 事件响应器类型
事件响应器类型 `type` 即是该响应器所要响应的事件类型,只有在接收到的事件类型与该响应器的类型相同时,才会触发该响应器。如果类型为空字符串 `""`,则响应器将会响应所有类型的事件。事件响应器类型的检查在所有其他检查(权限控制、响应规则)之前进行。
NoneBot 内置了四种常用事件类型:`meta_event``message``notice``request`,分别对应元事件、消息、通知、请求。通常情况下,协议适配器会将事件合理地分类至这四种类型中。如果有其他类型的事件需要响应,可以自行定义新的类型。
### 事件触发权限
事件触发权限 `permission` 是一个 `Permission` 对象,这在[权限控制](../appendices/permission.mdx)一节中已经介绍过。事件触发权限会在事件响应器的类型检查通过后进行检查,如果权限检查通过,则执行响应规则检查。
### 事件响应规则
事件响应规则 `rule` 是一个 `Rule` 对象,这在[响应规则](../appendices/rule.md)一节中已经介绍过。事件响应器的响应规则会在事件响应器的权限检查通过后进行匹配,如果响应规则检查通过,则触发该响应器。
### 响应优先级
响应优先级 `priority` 是一个正整数,用于指定响应器的优先级。响应器的优先级越小,越先被触发。如果响应器的优先级相同,则按照响应器的注册顺序进行触发。
### 阻断
阻断 `block` 是一个布尔值,用于指定响应器是否阻断事件的传播。如果阻断为 `True`,则在该响应器被触发后,事件将不会再传播给其他下一优先级的响应器。
NoneBot 内置的事件响应器中,所有非 `command` 规则的 `message` 类型的事件响应器都会阻断事件传递,其他则不会。
在部分情况中,可以使用 [`stop_propagation`](../appendices/session-control.mdx#stop_propagation) 方法动态阻止事件传播,该方法需要 handler 在参数中获取 matcher 实例后调用方法。
### 有效期
事件响应器的有效期分为 `temp``expire_time``temp` 是一个布尔值,用于指定响应器是否为临时响应器。如果为 `True`,则该响应器在被触发后会被自动销毁。`expire_time` 是一个 `datetime` 对象,用于指定响应器的过期时间。如果 `expire_time` 不为 `None`,则在该时间点后,该响应器会被自动销毁。
### 默认状态
事件响应器的默认状态 `default_state` 是一个 `dict` 对象,用于指定响应器的默认状态。在响应器被触发时,响应器将会初始化默认状态然后开始执行事件处理流程。
## 基本辅助函数
NoneBot 为四种类型的事件响应器提供了五个基本的辅助函数:
- `on`:创建任何类型的事件响应器。
- `on_metaevent`:创建元事件响应器。
- `on_message`:创建消息事件响应器。
- `on_request`:创建请求事件响应器。
- `on_notice`:创建通知事件响应器。
除了 `on` 函数具有一个 `type` 参数外,其余参数均相同:
- `rule`:响应规则,可以是 `Rule` 对象或者 `RuleChecker` 函数。
- `permission`:事件触发权限,可以是 `Permission` 对象或者 `PermissionChecker` 函数。
- `handlers`:事件处理函数列表。
- `temp`:是否为临时响应器。
- `expire_time`:响应器的过期时间。
- `priority`:响应器的优先级。
- `block`:是否阻断事件传播。
- `state`:响应器的默认状态。
在消息类型的事件响应器的基础上NoneBot 还内置了一些常用的响应规则,并结合为辅助函数来方便我们快速创建指定功能的响应器。下面我们逐个介绍。
## 内置响应规则
### `startswith`
`startswith` 响应规则用于匹配消息纯文本部分的开头是否与指定字符串(或一系列字符串)相同。可选参数 `ignorecase` 用于指定是否忽略大小写,默认为 `False`
例如,我们可以创建一个匹配消息开头为 `!` 或者 `/` 的规则:
```python
from nonebot.rule import startswith
rule = startswith(("!", "/"), ignorecase=False)
```
也可以直接使用辅助函数新建一个响应器:
```python
from nonebot import on_startswith
matcher = on_startswith(("!", "/"), ignorecase=False)
```
### `endswith`
`endswith` 响应规则用于匹配消息纯文本部分的结尾是否与指定字符串(或一系列字符串)相同。可选参数 `ignorecase` 用于指定是否忽略大小写,默认为 `False`
例如,我们可以创建一个匹配消息结尾为 `.` 或者 `。` 的规则:
```python
from nonebot.rule import endswith
rule = endswith((".", "。"), ignorecase=False)
```
也可以直接使用辅助函数新建一个响应器:
```python
from nonebot import on_endswith
matcher = on_endswith((".", "。"), ignorecase=False)
```
### `fullmatch`
`fullmatch` 响应规则用于匹配消息纯文本部分是否与指定字符串(或一系列字符串)完全相同。可选参数 `ignorecase` 用于指定是否忽略大小写,默认为 `False`
例如,我们可以创建一个匹配消息为 `ping` 或者 `pong` 的规则:
```python
from nonebot.rule import fullmatch
rule = fullmatch(("ping", "pong"), ignorecase=False)
```
也可以直接使用辅助函数新建一个响应器:
```python
from nonebot import on_fullmatch
matcher = on_fullmatch(("ping", "pong"), ignorecase=False)
```
### `keyword`
`keyword` 响应规则用于匹配消息纯文本部分是否包含指定字符串(或一系列字符串)。
例如,我们可以创建一个匹配消息中包含 `hello` 或者 `hi` 的规则:
```python
from nonebot.rule import keyword
rule = keyword("hello", "hi")
```
也可以直接使用辅助函数新建一个响应器:
```python
from nonebot import on_keyword
matcher = on_keyword("hello", "hi")
```
### `command`
`command` 是最常用的响应规则,它用于匹配消息是否为命令。它会根据配置中的 [Command Start 和 Command Separator](../appendices/config.mdx#command-start-和-command-separator) 来判断消息是否为命令。
例如,当我们配置了 `Command Start``/``Command Separator``.` 时:
```python
from nonebot.rule import command
# 匹配 "/help" 或者 "/帮助" 开头的消息
rule = command("help", "帮助")
# 匹配 "/help.cmd" 开头的消息
rule = command(("help", "cmd"))
```
也可以直接使用辅助函数新建一个响应器:
```python
from nonebot import on_command
matcher = on_command("help", aliases={"帮助"})
```
此外,`command` 响应规则默认允许消息命令与参数间不加空格,如果需要严格匹配命令与参数间的空白符,可以使用 `command` 函数的 `force_whitespace` 参数。`force_whitespace` 参数可以是 bool 类型或者具体的字符串,默认为 `False`。如果为 `True`,则命令与参数间必须有任意个数的空白符;如果为字符串,则命令与参数间必须有且与给定字符串一致的空白符。
```python
rule = command("help", force_whitespace=True)
rule = command("help", force_whitespace=" ")
```
命令解析后的结果可以通过 [`Command`](./dependency.mdx#command)、[`RawCommand`](./dependency.mdx#rawcommand)、[`CommandArg`](./dependency.mdx#commandarg)、[`CommandStart`](./dependency.mdx#commandstart)、[`CommandWhitespace`](./dependency.mdx#commandwhitespace) 依赖注入获取。
### `shell_command`
`shell_command` 响应规则用于匹配类 shell 命令形式的消息。它首先与 [`command`](#command) 响应规则一样进行命令匹配,如果匹配成功,则会进行进一步的参数解析。参数解析采用 `argparse` 标准库进行,在此基础上添加了消息序列 `Message` 支持。
例如,我们可以创建一个匹配 `/cmd` 命令并且带有 `-v` 选项与默认 `-h` 帮助选项的规则:
```python
from nonebot.rule import shell_command, ArgumentParser
parser = ArgumentParser()
parser.add_argument("-v", "--verbose", action="store_true")
rule = shell_command("cmd", parser=parser)
```
更多关于 `argparse` 的使用方法请参考 [argparse 文档](https://docs.python.org/zh-cn/3/library/argparse.html)。我们也可以选择不提供 `parser` 参数,这样 `shell_command` 将不会解析参数,但会提供参数列表 `argv`
直接使用辅助函数新建一个响应器:
```python
from nonebot import on_shell_command
from nonebot.rule import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-v", "--verbose", action="store_true")
matcher = on_shell_command("cmd", parser=parser)
```
参数解析后的结果可以通过 [`ShellCommandArgv`](./dependency.mdx#shellcommandargv)、[`ShellCommandArgs`](./dependency.mdx#shellcommandargs) 依赖注入获取。
### `regex`
`regex` 响应规则用于匹配消息是否与指定正则表达式匹配。
:::tip 提示
正则表达式匹配使用 search 而非 match如需从头匹配请使用 `r"^xxx"` 模式来确保匹配开头。
:::
例如,我们可以创建一个匹配消息中包含字母并且忽略大小写的规则:
```python
from nonebot.rule import regex
rule = regex(r"[a-z]+", flags=re.IGNORECASE)
```
也可以直接使用辅助函数新建一个响应器:
```python
from nonebot import on_regex
matcher = on_regex(r"[a-z]+", flags=re.IGNORECASE)
```
正则匹配后的结果可以通过 [`RegexStr`](./dependency.mdx#regexstr)、[`RegexGroup`](./dependency.mdx#regexgroup)、[`RegexDict`](./dependency.mdx#regexdict) 依赖注入获取。
### `to_me`
`to_me` 响应规则用于匹配事件是否与机器人相关。
例如:
```python
from nonebot.rule import to_me
rule = to_me()
```
### `is_type`
`is_type` 响应规则用于匹配事件类型是否为指定类型(或者一系列类型)。
例如,我们可以创建一个匹配 OneBot v11 私聊和群聊消息事件的规则:
```python
from nonebot.rule import is_type
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
rule = is_type(PrivateMessageEvent, GroupMessageEvent)
```
## 响应器组
为了更方便的管理一系列功能相近的响应器NoneBot 提供了两种响应器组,它们可以帮助我们进行响应器的统一管理。
### `CommandGroup`
`CommandGroup` 可以用于管理一系列具有相同前置命令的子命令响应器。
例如,我们创建 `/cmd``/cmd.sub``/cmd.help` 三个命令,他们具有相同的优先级:
```python
from nonebot import CommandGroup
group = CommandGroup("cmd", priority=10)
cmd = group.command(tuple())
sub_cmd = group.command("sub")
help_cmd = group.command("help")
```
命令别名 aliases 默认不会添加 `CommandGroup` 设定的前缀,如果需要为 aliases 添加前缀,可以添加 `prefix_aliases=True` 参数:
```python
from nonebot import CommandGroup
group = CommandGroup("cmd", prefix_aliases=True)
cmd = group.command(tuple())
help_cmd = group.command("help", aliases={"帮助"})
```
这样就能成功匹配 `/cmd``/cmd.help``/cmd.帮助` 命令。如果未设置,将默认匹配 `/cmd``/cmd.help``/帮助` 命令。
### `MatcherGroup`
`MatcherGroup` 可以用于管理一系列具有相同属性的响应器。
例如,我们创建一个具有相同响应规则的响应器组:
```python
from nonebot.rule import to_me
from nonebot import MatcherGroup
group = MatcherGroup(rule=to_me())
matcher1 = group.on_message()
matcher2 = group.on_message()
```
## 第三方响应规则
### Alconna
[`nonebot-plugin-alconna`](https://github.com/nonebot/plugin-alconna) 是一类提供了拓展响应规则的插件。
该插件使用 [Alconna](https://github.com/ArcletProject/Alconna) 作为命令解析器,
是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
该插件提供了一类新的事件响应器辅助函数 `on_alconna`,以及 `AlconnaResult` 等依赖注入函数。
基于 `Alconna` 的特性,该插件同时提供了一系列便捷的消息段标注。
标注可用于在 `Alconna` 中匹配消息中除 text 外的其他消息段,也可用于快速创建各适配器下的消息段。所有标注位于 `nonebot_plugin_alconna.adapters` 中。
该插件同时通过提供 `UniMessage` (通用消息模型) 实现了**跨平台接收和发送消息**的功能。
详情请阅读最佳实践中的 [命令解析拓展](../best-practice/alconna/README.mdx) 章节。

View File

@@ -0,0 +1,104 @@
---
sidebar_position: 2
description: 填写与获取插件相关的信息
options:
menu:
- category: advanced
weight: 30
---
# 插件信息
NoneBot 是一个插件化的框架,可以通过加载插件来扩展功能。同时,我们也可以通过 NoneBot 的插件系统来获取相关信息,例如插件的名称、使用方法,用于收集帮助信息等。下面我们将介绍如何为插件添加元数据,以及如何获取插件信息。
## 插件元数据
在 NoneBot 中,插件 [`Plugin`](../api/plugin/model.md#Plugin) 对象中存储了插件系统所需要的一系列信息。包括插件的索引名称、插件模块、插件中的事件响应器、插件父子关系等。通常,只有插件开发者才需要关心这些信息,而插件使用者或者机器人用户想要看到的是插件使用方法等帮助信息。因此,我们可以为插件添加插件元数据 `PluginMetadata`,它允许插件开发者为插件添加一些额外的信息。这些信息编写于插件模块的顶层,可以直接通过源码查看,或者通过 NoneBot 插件系统获取收集到的信息,通过其他方式发送给机器人用户等。
现在,假设我们有一个插件 `example`, 它的模块结构如下:
```tree {4-6} title=Project
📦 awesome-bot
├── 📂 awesome_bot
│ └── 📂 plugins
| └── 📂 example
| ├── 📜 __init__.py
| └── 📜 config.py
├── 📜 pyproject.toml
└── 📜 README.md
```
我们需要在插件顶层模块 `example/__init__.py` 中添加插件元数据,如下所示:
```python {1,5-12} title=example/__init__.py
from nonebot.plugin import PluginMetadata
from .config import Config
__plugin_meta__ = PluginMetadata(
name="示例插件",
description="这是一个示例插件",
usage="没什么用",
type="application",
config=Config,
extra={},
)
```
我们可以看到,插件元数据 `PluginMetadata` 有三个基本属性:插件名称、插件描述、插件使用方法。除此之外,还有几个可选的属性(具体填写见[发布插件](../developer/plugin-publishing.mdx#填写插件元数据)章节):
- `type`:插件类别,发布插件必填。当前有效类别有:`library`(为其他插件编写提供功能),`application`(向机器人用户提供功能);
- `homepage`:插件项目主页,发布插件必填;
- `config`:插件的[配置类](../appendices/config.mdx#插件配置),如无配置类可不填;
- `supported_adapters`:支持的适配器模块名集合,若插件可以保证兼容所有适配器(即仅使用基本适配器功能)可不填写;
- `extra`:一个字典,可以用于存储任意信息。其他插件可以通过约定 `extra` 字典的键名来达成收集某些特殊信息的目的。
请注意,这里的**插件名称**是供使用者或机器人用户查看的,与插件索引名称无关。**插件索引名称(插件模块名称)**仅用于 NoneBot 插件系统**内部索引**。
## 获取插件信息
NoneBot 提供了多种获取插件对象的方法,例如获取当前所有已导入的插件:
```python
import nonebot
plugins: set[Plugin] = nonebot.get_loaded_plugins()
```
也可以通过插件索引名称获取插件对象:
```python
import nonebot
plugin: Plugin | None = nonebot.get_plugin("example")
```
或者通过模块路径获取插件对象:
```python
import nonebot
plugin: Plugin | None = nonebot.get_plugin_by_module_name("awesome_bot.plugins.example")
```
如果需要获取所有当前声明的插件名称(可能还未加载),可以使用 `get_available_plugin_names` 函数:
```python
import nonebot
plugin_names: set[str] = nonebot.get_available_plugin_names()
```
插件对象 `Plugin` 中包含了多个属性:
- `name`:插件索引名称
- `module`:插件模块
- `module_name`:插件模块路径
- `manager`:插件管理器
- `matcher`:插件中定义的事件响应器
- `parent_plugin`:插件的父插件
- `sub_plugins`:插件的子插件集合
- `metadata`:插件元数据
通过这些属性以及插件元数据,我们就可以收集所需要的插件信息了。

View File

@@ -0,0 +1,41 @@
---
sidebar_position: 3
description: 编写与加载嵌套插件
options:
menu:
- category: advanced
weight: 40
---
# 嵌套插件
NoneBot 支持嵌套插件,即一个插件可以包含其他插件。通过这种方式,我们可以将一个大型插件拆分成多个功能子插件,使得插件更加清晰、易于维护。我们可以直接在插件中使用 NoneBot 加载插件的方法来加载子插件。
## 创建嵌套插件
我们可以在使用 `nb-cli` 命令[创建插件](../tutorial/create-plugin.md#创建插件)时,选择直接通过模板创建一个嵌套插件:
```bash
$ nb plugin create
[?] 插件名称: parent
[?] 使用嵌套插件? (y/N) Y
[?] 输出目录: awesome_bot/plugins
```
或者使用 `nb plugin create --sub-plugin` 选项直接创建一个嵌套插件。
## 已有插件
如果你已经有一个插件,想要在其中嵌套加载子插件,可以在插件的 `__init__.py` 中添加如下代码:
```python title=parent/__init__.py
import nonebot
from pathlib import Path
sub_plugins = nonebot.load_plugins(
str(Path(__file__).parent.joinpath("plugins").resolve())
)
```
这样,`parent` 插件就会加载 `parent/plugins` 目录下的所有插件。NoneBot 会正确识别这些插件的父子关系,你可以在 `parent` 的插件信息中看到这些子插件的信息,也可以在子插件信息中看到它们的父插件信息。

View File

@@ -0,0 +1,37 @@
---
sidebar_position: 4
description: 使用其他插件提供的功能
options:
menu:
- category: advanced
weight: 50
---
# 跨插件访问
NoneBot 插件化系统的设计使得插件之间可以功能独立、各司其职我们可以更好地维护和扩展插件。但是有时候我们可能需要在不同插件之间调用功能。NoneBot 生态中就有一类插件,它们专为其他插件提供功能支持,如:[定时任务插件](../best-practice/scheduler.md)、[数据存储插件](../best-practice/data-storing.md)等。这时候我们就需要在插件之间进行跨插件访问。
## 插件跟踪
由于 NoneBot 插件系统通过 [Import Hooks](https://docs.python.org/3/reference/import.html#import-hooks) 的方式实现插件加载与跟踪管理,因此我们**不能**在 NoneBot 跟踪插件前进行模块 import这会导致插件加载失败。即我们不能在使用 NoneBot 提供的加载插件方法前,直接使用 `import` 语句导入插件。
对于在项目目录下的插件,我们通常直接使用 `load_from_toml` 等方法一次性加载所有插件。由于这些插件已经被声明即便插件导入顺序不同NoneBot 也能正确跟踪插件。此时我们不需要对跨插件访问进行特殊处理。但当我们使用了外部插件如果没有事先声明或加载插件NoneBot 并不会将其当作插件进行跟踪,可能会出现意料之外的错误出现。
简单来说,我们必须在 `import` 外部插件之前,确保依赖的外部插件已经被声明或加载。
## 插件依赖声明
NoneBot 提供了一种方法来确保我们依赖的插件已经被正确加载,即使用 `require` 函数。通过 `require` 函数我们可以在当前插件中声明依赖的插件NoneBot 会在加载当前插件时,检查依赖的插件是否已经被加载,如果没有,会尝试优先加载依赖的插件。
假设我们有一个插件 `a` 依赖于插件 `b`,我们可以在插件 `a` 中使用 `require` 函数声明其依赖于插件 `b`
```python {3} title=a/__init__.py
from nonebot import require
require("b")
from b import some_function
```
其中,`require` 函数的参数为插件索引名称或者外部插件的模块名称。在完成依赖声明后,我们可以在插件 `a` 中直接导入插件 `b` 所提供的功能。

View File

@@ -0,0 +1,135 @@
---
sidebar_position: 9
description: 添加服务端路由规则
options:
menu:
- category: advanced
weight: 100
---
# 添加路由
在[驱动器](./driver.md)一节中,我们了解了驱动器的两种类型。既然驱动器可以作为服务端运行,那么我们就可以向驱动器添加路由规则,从而实现自定义的 API 接口等功能。在添加路由规则时,我们需要注意驱动器的类型,详情可以参考[选择驱动器](./driver.md#配置驱动器)。
NoneBot 中,我们可以通过两种途径向 ASGI 驱动器添加路由规则:
1. 通过 NoneBot 的兼容层建立路由规则。
2. 直接向 ASGI 应用添加路由规则。
这两种途径各有优劣,前者可以在各种服务端型驱动器下运行,但并不能直接使用 ASGI 应用框架提供的特性与功能;后者直接使用 ASGI 应用,更自由、功能完整,但只能在特定类型驱动器下运行。
在向驱动器添加路由规则时,我们需要注意驱动器是否为服务端类型,我们可以通过以下方式判断:
```python
from nonebot import get_driver
from nonebot.drivers import ASGIMixin
# highlight-next-line
can_use = isinstance(get_driver(), ASGIMixin)
```
## 通过兼容层添加路由
NoneBot 兼容层定义了两个数据类 `HTTPServerSetup``WebSocketServerSetup`,分别用于定义 HTTP 服务端和 WebSocket 服务端的路由规则。
### HTTP 路由
`HTTPServerSetup` 具有四个属性:
- `path`:路由路径,不支持特殊占位表达式。类型为 `URL`
- `method`:请求方法。类型为 `str`
- `name`:路由名称,不可重复。类型为 `str`
- `handle_func`:路由处理函数。类型为 `Callable[[Request], Awaitable[Response]]`
例如,我们添加一个 `/hello` 的路由,当请求方法为 `GET` 时,返回 `200 OK` 以及返回体信息:
```python
from nonebot import get_driver
from nonebot.drivers import URL, Request, Response, ASGIMixin, HTTPServerSetup
async def hello(request: Request) -> Response:
return Response(200, content="Hello, world!")
if isinstance((driver := get_driver()), ASGIMixin):
driver.setup_http_server(
HTTPServerSetup(
path=URL("/hello"),
method="GET",
name="hello",
handle_func=hello,
)
)
```
对于 `Request``Response` 的详细信息,可以参考 [API 文档](../api/drivers/index.md)。
### WebSocket 路由
`WebSocketServerSetup` 具有三个属性:
- `path`:路由路径,不支持特殊占位表达式。类型为 `URL`
- `name`:路由名称,不可重复。类型为 `str`
- `handle_func`:路由处理函数。类型为 `Callable[[WebSocket], Awaitable[Any]]`
例如,我们添加一个 `/ws` 的路由,发送所有接收到的数据:
```python
from nonebot import get_driver
from nonebot.drivers import URL, ASGIMixin, WebSocket, WebSocketServerSetup
async def ws_handler(ws: WebSocket):
await ws.accept()
try:
while True:
data = await ws.receive()
await ws.send(data)
except WebSocketClosed as e:
# handle closed
...
finally:
with contextlib.suppress(Exception):
await websocket.close()
# do some cleanup
if isinstance((driver := get_driver()), ASGIMixin):
driver.setup_websocket_server(
WebSocketServerSetup(
path=URL("/ws"),
name="ws",
handle_func=ws_handler,
)
)
```
对于 `WebSocket` 的详细信息,可以参考 [API 文档](../api/drivers/index.md)。
## 使用 ASGI 应用添加路由
### 获取 ASGI 应用
NoneBot 服务端类型的驱动器具有两个属性 `server_app``asgi`,分别对应驱动框架应用和 ASGI 应用。通常情况下,这两个应用是同一个对象。我们可以通过 `get_app()` 方法快速获取:
```python
import nonebot
app = nonebot.get_app()
asgi = nonebot.get_asgi()
```
### 添加路由规则
在获取到了 ASGI 应用后,我们就可以直接使用 ASGI 应用框架提供的功能来添加路由规则了。这里我们以 [FastAPI](./driver.md#fastapi默认) 为例,演示如何添加路由规则。
在下面的代码中,我们添加了一个 `GET` 类型的 `/api` 路由,具体方法参考 [FastAPI 文档](https://fastapi.tiangolo.com/)。
```python
import nonebot
from fastapi import FastAPI
app: FastAPI = nonebot.get_app()
@app.get("/api")
async def custom_api():
return {"message": "Hello, world!"}
```

View File

@@ -0,0 +1,159 @@
---
sidebar_position: 8
description: 在特定的生命周期中执行代码
options:
menu:
- category: advanced
weight: 90
---
# 钩子函数
> [钩子编程](https://zh.wikipedia.org/wiki/%E9%92%A9%E5%AD%90%E7%BC%96%E7%A8%8B)hooking也称作“挂钩”是计算机程序设计术语指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码被称为钩子hook
在 NoneBot 中有一系列预定义的钩子函数,可以分为两类:**全局钩子函数**和**事件处理钩子函数**,这些钩子函数可以用装饰器的形式来使用。
## 全局钩子函数
全局钩子函数是指 NoneBot 针对其本身运行过程的钩子函数。
这些钩子函数是由驱动器来运行的,故需要先[获得全局驱动器](./driver.md#获取驱动器)。
### 启动准备
这个钩子函数会在 NoneBot 启动时运行。很多时候,我们并不希望在模块被导入时就执行一些耗时操作,如:连接数据库,这时候我们可以在这个钩子函数中进行这些操作。
```python
from nonebot import get_driver
driver = get_driver()
@driver.on_startup
async def do_something():
pass
```
### 终止处理
这个钩子函数会在 NoneBot 终止时运行。我们可以在这个钩子函数中进行一些清理工作,如:关闭数据库连接。
```python
from nonebot import get_driver
driver = get_driver()
@driver.on_shutdown
async def do_something():
pass
```
### Bot 连接处理
这个钩子函数会在任何协议适配器连接 `Bot` 对象至 NoneBot 时运行。支持依赖注入,可以直接注入 `Bot` 对象。
```python
from nonebot import get_driver
driver = get_driver()
@driver.on_bot_connect
async def do_something(bot: Bot):
pass
```
### Bot 断开处理
这个钩子函数会在 `Bot` 断开与 NoneBot 的连接时运行。支持依赖注入,可以直接注入 `Bot` 对象。
```python
from nonebot import get_driver
driver = get_driver()
@driver.on_bot_disconnect
async def do_something(bot: Bot):
pass
```
## 事件处理钩子函数
这些钩子函数指的是影响 NoneBot 进行**事件处理**的函数, 这些函数可以跟普通的事件处理函数一样接受相应的参数。
### 事件预处理
这个钩子函数会在 NoneBot 接收到新的事件时运行。支持依赖注入,可以注入 `Bot` 对象、事件、会话状态。
```python
from nonebot.message import event_preprocessor
@event_preprocessor
async def do_something(event: Event):
pass
```
### 事件后处理
这个钩子函数会在 NoneBot 处理事件完成后运行。支持依赖注入,可以注入 `Bot` 对象、事件、会话状态。
```python
from nonebot.message import event_postprocessor
@event_postprocessor
async def do_something(event: Event):
pass
```
### 运行预处理
这个钩子函数会在 NoneBot 运行事件响应器前运行。支持依赖注入,可以注入 `Bot` 对象、事件、事件响应器、会话状态。
```python
from nonebot.message import run_preprocessor
@run_preprocessor
async def do_something(event: Event, matcher: Matcher):
pass
```
### 运行后处理
这个钩子函数会在 NoneBot 运行事件响应器后运行。支持依赖注入,可以注入 `Bot` 对象、事件、事件响应器、会话状态、运行中产生的异常。
```python
from nonebot.message import run_postprocessor
@run_postprocessor
async def do_something(event: Event, matcher: Matcher, exception: Optional[Exception]):
pass
```
### 平台接口调用钩子
这个钩子函数会在 `Bot` 对象调用平台接口时运行。在这个钩子函数中,我们可以通过引起 `MockApiException` 异常来阻止 `Bot` 对象调用平台接口并返回指定的结果。
```python
from nonebot.adapters import Bot
from nonebot.exception import MockApiException
@Bot.on_calling_api
async def handle_api_call(bot: Bot, api: str, data: Dict[str, Any]):
if api == "send_msg":
raise MockApiException(result={"message_id": 123})
```
### 平台接口调用后钩子
这个钩子函数会在 `Bot` 对象调用平台接口后运行。在这个钩子函数中,我们可以通过引起 `MockApiException` 异常来忽略平台接口返回的结果并返回指定的结果。
```python
from nonebot.adapters import Bot
from nonebot.exception import MockApiException
@Bot.on_called_api
async def handle_api_result(
bot: Bot, exception: Optional[Exception], api: str, data: Dict[str, Any], result: Any
):
if not exception and api == "send_msg":
raise MockApiException(result={**result, "message_id": 123})
```

View File

@@ -0,0 +1,59 @@
---
sidebar_position: 7
description: 控制会话响应对象
options:
menu:
- category: advanced
weight: 80
---
# 会话更新
在 NoneBot 中在某个事件响应器对事件响应后即是进入了会话状态会话状态会持续到整个事件响应流程结束。会话过程中机器人可以与用户进行多次交互。每次需要等待用户事件时NoneBot 将会复制一个新的临时事件响应器,并更新该事件响应器使其响应当前会话主体的消息,这个过程称为会话更新。
会话更新分为两部分:**更新[事件响应器类型](./matcher.md#事件响应器类型)**和**更新[事件触发权限](./matcher.md#事件触发权限)**。
## 更新事件响应器类型
通常情况下,与机器人用户进行的会话都是通过消息事件进行的,因此会话更新后的默认响应事件类型为 `message`。如果希望接收一个特定类型的消息,比如 `notice` 等,我们需要自定义响应事件类型更新函数。响应事件类型更新函数是一个 `Dependent`,可以使用依赖注入。
```python {3-5}
foo = on_message()
@foo.type_updater
async def _() -> str:
return "notice"
```
在注册了上述响应事件类型更新函数后,当我们需要等待用户事件时,将只会响应 `notice` 类型的事件。如果希望在会话过程中的不同阶段响应不同类型的事件,我们就需要使用更复杂的逻辑来更新响应事件类型(如:根据会话状态),这里将不再展示。
## 更新事件触发权限
会话通常是由机器人与用户进行的一对一交互,因此会话更新后的默认触发权限为当前事件的会话 ID。这个会话 ID 由协议适配器生成,通常由用户 ID 和群 ID 等组成。如果希望实现更复杂的会话功能(如:多用户同时参与的会话),我们需要自定义触发权限更新函数。触发权限更新函数是一个 `Dependent`,可以使用依赖注入。
```python {5-7}
from nonebot.permission import User
foo = on_message()
@foo.permission_updater
async def _(event: Event, matcher: Matcher) -> Permission:
return Permission(User.from_event(event, perm=matcher.permission))
```
上述权限更新函数是默认的权限更新函数,它将会话的触发权限更新为当前事件的会话 ID。如果我们希望响应多个用户的消息我们可以如下修改
```python {5-7}
from nonebot.permission import USER
foo = on_message()
@foo.permission_updater
async def _(matcher: Matcher) -> Permission:
return USER("session1", "session2", perm=matcher.permission)
```
请注意,此处为全大写字母的 `USER` 权限,它可以匹配多个会话 ID。通过这种方式我们可以实现多用户同时参与的会话。
我们已经了解了如何控制会话的更新,相信你已经能够实现更复杂的会话功能了,例如多人小游戏等等。欢迎将你的作品分享到[插件商店](/store/plugins)。

View File

@@ -0,0 +1,3 @@
{
"position": 15
}

View File

@@ -0,0 +1,872 @@
---
sidebar_position: 0
description: nonebot.adapters 模块
---
# nonebot.adapters
本模块定义了协议适配基类,各协议请继承以下基类。
使用 [Driver.register_adapter](../drivers/index.md#Driver-register-adapter) 注册适配器。
## _abstract class_ `Bot(adapter, self_id)` {#Bot}
- **说明**
Bot 基类。
用于处理上报消息,并提供 API 调用接口。
- **参数**
- `adapter` ([Adapter](#Adapter)): 协议适配器实例
- `self_id` (str): 机器人 ID
### _instance-var_ `adapter` {#Bot-adapter}
- **类型:** [Adapter](#Adapter)
- **说明:** 协议适配器实例
### _instance-var_ `self_id` {#Bot-self-id}
- **类型:** str
- **说明:** 机器人 ID
### _property_ `type` {#Bot-type}
- **类型:** str
- **说明:** 协议适配器名称
### _property_ `config` {#Bot-config}
- **类型:** [Config](../config.md#Config)
- **说明:** 全局 NoneBot 配置
### _async method_ `call_api(api, **data)` {#Bot-call-api}
- **说明:** 调用机器人 API 接口,可以通过该函数或直接通过 bot 属性进行调用
- **参数**
- `api` (str): API 名称
- `**data` (Any): API 数据
- **返回**
- Any
- **用法**
```python
await bot.call_api("send_msg", message="hello world")
await bot.send_msg(message="hello world")
```
### _abstract async method_ `send(event, message, **kwargs)` {#Bot-send}
- **说明:** 调用机器人基础发送消息接口
- **参数**
- `event` ([Event](#Event)): 上报事件
- `message` (str | [Message](#Message) | [MessageSegment](#MessageSegment)): 要发送的消息
- `**kwargs` (Any): 任意额外参数
- **返回**
- Any
### _classmethod_ `on_calling_api(func)` {#Bot-on-calling-api}
- **说明**
调用 api 预处理。
钩子函数参数:
- bot: 当前 bot 对象
- api: 调用的 api 名称
- data: api 调用的参数字典
- **参数**
- `func` ([T_CallingAPIHook](../typing.md#T-CallingAPIHook))
- **返回**
- [T_CallingAPIHook](../typing.md#T-CallingAPIHook)
### _classmethod_ `on_called_api(func)` {#Bot-on-called-api}
- **说明**
调用 api 后处理。
钩子函数参数:
- bot: 当前 bot 对象
- exception: 调用 api 时发生的错误
- api: 调用的 api 名称
- data: api 调用的参数字典
- result: api 调用的返回
- **参数**
- `func` ([T_CalledAPIHook](../typing.md#T-CalledAPIHook))
- **返回**
- [T_CalledAPIHook](../typing.md#T-CalledAPIHook)
## _abstract class_ `Event(<auto>)` {#Event}
- **说明:** Event 基类。提供获取关键信息的方法,其余信息可直接获取。
- **参数**
auto
### _classmethod_ `validate(value)` {#Event-validate}
- **参数**
- `value` (Any)
- **返回**
- E
### _abstract method_ `get_type()` {#Event-get-type}
- **说明:** 获取事件类型的方法,类型通常为 NoneBot 内置的四种类型。
- **参数**
empty
- **返回**
- str
### _abstract method_ `get_event_name()` {#Event-get-event-name}
- **说明:** 获取事件名称的方法。
- **参数**
empty
- **返回**
- str
### _abstract method_ `get_event_description()` {#Event-get-event-description}
- **说明:** 获取事件描述的方法,通常为事件具体内容。
- **参数**
empty
- **返回**
- str
### _method_ `get_log_string()` {#Event-get-log-string}
- **说明**
获取事件日志信息的方法。
通常你不需要修改这个方法,只有当希望 NoneBot 隐藏该事件日志时,
可以抛出 `NoLogException` 异常。
- **参数**
empty
- **返回**
- str
- **异常**
- NoLogException: 希望 NoneBot 隐藏该事件日志
### _abstract method_ `get_user_id()` {#Event-get-user-id}
- **说明:** 获取事件主体 id 的方法,通常是用户 id 。
- **参数**
empty
- **返回**
- str
### _abstract method_ `get_session_id()` {#Event-get-session-id}
- **说明:** 获取会话 id 的方法,用于判断当前事件属于哪一个会话, 通常是用户 id、群组 id 组合。
- **参数**
empty
- **返回**
- str
### _abstract method_ `get_message()` {#Event-get-message}
- **说明:** 获取事件消息内容的方法。
- **参数**
empty
- **返回**
- [Message](#Message)
### _method_ `get_plaintext()` {#Event-get-plaintext}
- **说明**
获取消息纯文本的方法。
通常不需要修改,默认通过 `get_message().extract_plain_text` 获取。
- **参数**
empty
- **返回**
- str
### _abstract method_ `is_tome()` {#Event-is-tome}
- **说明:** 获取事件是否与机器人有关的方法。
- **参数**
empty
- **返回**
- bool
## _abstract class_ `Adapter(driver, **kwargs)` {#Adapter}
- **说明**
协议适配器基类。
通常,在 Adapter 中编写协议通信相关代码,如: 建立通信连接、处理接收与发送 data 等。
- **参数**
- `driver` ([Driver](../drivers/index.md#Driver)): [Driver](../drivers/index.md#Driver) 实例
- `**kwargs` (Any): 其他由 [Driver.register_adapter](../drivers/index.md#Driver-register-adapter) 传入的额外参数
### _instance-var_ `driver` {#Adapter-driver}
- **类型:** [Driver](../drivers/index.md#Driver)
- **说明:** 实例
### _instance-var_ `bots` {#Adapter-bots}
- **类型:** dict[str, [Bot](#Bot)]
- **说明:** 本协议适配器已建立连接的 [Bot](#Bot) 实例
### _abstract classmethod_ `get_name()` {#Adapter-get-name}
- **说明:** 当前协议适配器的名称
- **参数**
empty
- **返回**
- str
### _property_ `config` {#Adapter-config}
- **类型:** [Config](../config.md#Config)
- **说明:** 全局 NoneBot 配置
### _method_ `bot_connect(bot)` {#Adapter-bot-connect}
- **说明**
告知 NoneBot 建立了一个新的 [Bot](#Bot) 连接。
当有新的 [Bot](#Bot) 实例连接建立成功时调用。
- **参数**
- `bot` ([Bot](#Bot)): [Bot](#Bot) 实例
- **返回**
- None
### _method_ `bot_disconnect(bot)` {#Adapter-bot-disconnect}
- **说明**
告知 NoneBot [Bot](#Bot) 连接已断开。
当有 [Bot](#Bot) 实例连接断开时调用。
- **参数**
- `bot` ([Bot](#Bot)): [Bot](#Bot) 实例
- **返回**
- None
### _method_ `setup_http_server(setup)` {#Adapter-setup-http-server}
- **说明:** 设置一个 HTTP 服务器路由配置
- **参数**
- `setup` ([HTTPServerSetup](../drivers/index.md#HTTPServerSetup))
- **返回**
- untyped
### _method_ `setup_websocket_server(setup)` {#Adapter-setup-websocket-server}
- **说明:** 设置一个 WebSocket 服务器路由配置
- **参数**
- `setup` ([WebSocketServerSetup](../drivers/index.md#WebSocketServerSetup))
- **返回**
- untyped
### _async method_ `request(setup)` {#Adapter-request}
- **说明:** 进行一个 HTTP 客户端请求
- **参数**
- `setup` ([Request](../drivers/index.md#Request))
- **返回**
- [Response](../drivers/index.md#Response)
### _method_ `websocket(setup)` {#Adapter-websocket}
- **说明:** 建立一个 WebSocket 客户端连接请求
- **参数**
- `setup` ([Request](../drivers/index.md#Request))
- **返回**
- AsyncGenerator[[WebSocket](../drivers/index.md#WebSocket), None]
### _method_ `on_ready(func)` {#Adapter-on-ready}
- **参数**
- `func` (LIFESPAN_FUNC)
- **返回**
- LIFESPAN_FUNC
## _abstract class_ `Message(<auto>)` {#Message}
- **说明:** 消息序列
- **参数**
- `message`: 消息内容
### _classmethod_ `template(format_string)` {#Message-template}
- **说明**
创建消息模板。
用法和 `str.format` 大致相同,支持以 `Message` 对象作为消息模板并输出消息对象。
并且提供了拓展的格式化控制符,
可以通过该消息类型的 `MessageSegment` 工厂方法创建消息。
- **参数**
- `format_string` (str | TM): 格式化模板
- **返回**
- [MessageTemplate](#MessageTemplate)[Self]: 消息格式化器
### _abstract classmethod_ `get_segment_class()` {#Message-get-segment-class}
- **说明:** 获取消息段类型
- **参数**
empty
- **返回**
- type[TMS]
### _abstract staticmethod_ `_construct(msg)` {#Message--construct}
- **说明:** 构造消息数组
- **参数**
- `msg` (str)
- **返回**
- Iterable[TMS]
### _method_ `__getitem__(args)` {#Message---getitem--}
- **重载**
**1.** `(args) -> Self`
- **参数**
- `args` (str): 消息段类型
- **返回**
- Self: 所有类型为 `args` 的消息段
**2.** `(args) -> TMS`
- **参数**
- `args` (tuple[str, int]): 消息段类型和索引
- **返回**
- TMS: 类型为 `args[0]` 的消息段第 `args[1]` 个
**3.** `(args) -> Self`
- **参数**
- `args` (tuple[str, slice]): 消息段类型和切片
- **返回**
- Self: 类型为 `args[0]` 的消息段切片 `args[1]`
**4.** `(args) -> TMS`
- **参数**
- `args` (int): 索引
- **返回**
- TMS: 第 `args` 个消息段
**5.** `(args) -> Self`
- **参数**
- `args` (slice): 切片
- **返回**
- Self: 消息切片 `args`
### _method_ `__contains__(value)` {#Message---contains--}
- **说明:** 检查消息段是否存在
- **参数**
- `value` (TMS | str): 消息段或消息段类型
- **返回**
- bool: 消息内是否存在给定消息段或给定类型的消息段
### _method_ `has(value)` {#Message-has}
- **说明:** 与 [`__contains__`](#Message---contains--) 相同
- **参数**
- `value` (TMS | str)
- **返回**
- bool
### _method_ `index(value, *args)` {#Message-index}
- **说明:** 索引消息段
- **参数**
- `value` (TMS | str): 消息段或者消息段类型
- `*args` (SupportsIndex)
- `arg`: start 与 end
- **返回**
- int: 索引 index
- **异常**
- ValueError: 消息段不存在
### _method_ `get(type_, count=None)` {#Message-get}
- **说明:** 获取指定类型的消息段
- **参数**
- `type_` (str): 消息段类型
- `count` (int | None): 获取个数
- **返回**
- Self: 构建的新消息
### _method_ `count(value)` {#Message-count}
- **说明:** 计算指定消息段的个数
- **参数**
- `value` (TMS | str): 消息段或消息段类型
- **返回**
- int: 个数
### _method_ `only(value)` {#Message-only}
- **说明:** 检查消息中是否仅包含指定消息段
- **参数**
- `value` (TMS | str): 指定消息段或消息段类型
- **返回**
- bool: 是否仅包含指定消息段
### _method_ `append(obj)` {#Message-append}
- **说明:** 添加一个消息段到消息数组末尾。
- **参数**
- `obj` (str | TMS): 要添加的消息段
- **返回**
- Self
### _method_ `extend(obj)` {#Message-extend}
- **说明:** 拼接一个消息数组或多个消息段到消息数组末尾。
- **参数**
- `obj` (Self | Iterable[TMS]): 要添加的消息数组
- **返回**
- Self
### _method_ `join(iterable)` {#Message-join}
- **说明:** 将多个消息连接并将自身作为分割
- **参数**
- `iterable` (Iterable[TMS | Self]): 要连接的消息
- **返回**
- Self: 连接后的消息
### _method_ `copy()` {#Message-copy}
- **说明:** 深拷贝消息
- **参数**
empty
- **返回**
- Self
### _method_ `include(*types)` {#Message-include}
- **说明:** 过滤消息
- **参数**
- `*types` (str): 包含的消息段类型
- **返回**
- Self: 新构造的消息
### _method_ `exclude(*types)` {#Message-exclude}
- **说明:** 过滤消息
- **参数**
- `*types` (str): 不包含的消息段类型
- **返回**
- Self: 新构造的消息
### _method_ `extract_plain_text()` {#Message-extract-plain-text}
- **说明:** 提取消息内纯文本消息
- **参数**
empty
- **返回**
- str
## _abstract class_ `MessageSegment(<auto>)` {#MessageSegment}
- **说明:** 消息段基类
- **参数**
auto
### _instance-var_ `type` {#MessageSegment-type}
- **类型:** str
- **说明:** 消息段类型
### _class-var_ `data` {#MessageSegment-data}
- **类型:** dict[str, Any]
- **说明:** 消息段数据
### _abstract classmethod_ `get_message_class()` {#MessageSegment-get-message-class}
- **说明:** 获取消息数组类型
- **参数**
empty
- **返回**
- type[TM]
### _abstract method_ `__str__()` {#MessageSegment---str--}
- **说明:** 该消息段所代表的 str在命令匹配部分使用
- **参数**
empty
- **返回**
- str
### _method_ `__add__(other)` {#MessageSegment---add--}
- **参数**
- `other` (str | TMS | Iterable[TMS])
- **返回**
- TM
### _method_ `get(key, default=None)` {#MessageSegment-get}
- **参数**
- `key` (str)
- `default` (Any)
- **返回**
- untyped
### _method_ `keys()` {#MessageSegment-keys}
- **参数**
empty
- **返回**
- untyped
### _method_ `values()` {#MessageSegment-values}
- **参数**
empty
- **返回**
- untyped
### _method_ `items()` {#MessageSegment-items}
- **参数**
empty
- **返回**
- untyped
### _method_ `join(iterable)` {#MessageSegment-join}
- **参数**
- `iterable` (Iterable[TMS | TM])
- **返回**
- TM
### _method_ `copy()` {#MessageSegment-copy}
- **参数**
empty
- **返回**
- Self
### _abstract method_ `is_text()` {#MessageSegment-is-text}
- **说明:** 当前消息段是否为纯文本
- **参数**
empty
- **返回**
- bool
## _class_ `MessageTemplate(template, factory=str)` {#MessageTemplate}
- **说明:** 消息模板格式化实现类。
- **参数**
- `template` (str | TM): 模板
- `factory` (type[str] | type[TM]): 消息类型工厂,默认为 `str`
### _method_ `add_format_spec(spec, name=None)` {#MessageTemplate-add-format-spec}
- **参数**
- `spec` (FormatSpecFunc_T)
- `name` (str | None)
- **返回**
- FormatSpecFunc_T
### _method_ `format(*args, **kwargs)` {#MessageTemplate-format}
- **说明:** 根据传入参数和模板生成消息对象
- **参数**
- `*args`
- `**kwargs`
- **返回**
- untyped
### _method_ `format_map(mapping)` {#MessageTemplate-format-map}
- **说明:** 根据传入字典和模板生成消息对象, 在传入字段名不是有效标识符时有用
- **参数**
- `mapping` (Mapping[str, Any])
- **返回**
- TF
### _method_ `vformat(format_string, args, kwargs)` {#MessageTemplate-vformat}
- **参数**
- `format_string` (str)
- `args` (Sequence[Any])
- `kwargs` (Mapping[str, Any])
- **返回**
- TF
### _method_ `format_field(value, format_spec)` {#MessageTemplate-format-field}
- **参数**
- `value` (Any)
- `format_spec` (str)
- **返回**
- Any

View File

@@ -0,0 +1,168 @@
---
sidebar_position: 1
description: nonebot.config 模块
---
# nonebot.config
本模块定义了 NoneBot 本身运行所需的配置项。
NoneBot 使用 [`pydantic`](https://pydantic-docs.helpmanual.io/) 以及
[`python-dotenv`](https://saurabh-kumar.com/python-dotenv/) 来读取配置。
配置项需符合特殊格式或 json 序列化格式
详情见 [`pydantic Field Type`](https://pydantic-docs.helpmanual.io/usage/types/) 文档。
## _class_ `Env(<auto>)` {#Env}
- **说明**
运行环境配置。大小写不敏感。
将会从 **环境变量** > **dotenv 配置文件** 的优先级读取环境信息。
- **参数**
auto
### _class-var_ `environment` {#Env-environment}
- **类型:** str
- **说明**
当前环境名。
NoneBot 将从 `.env.{environment}` 文件中加载配置。
## _class_ `Config(<auto>)` {#Config}
- **说明**
NoneBot 主要配置。大小写不敏感。
除了 NoneBot 的配置项外,还可以自行添加配置项到 `.env.{environment}` 文件中。
这些配置将会在 json 反序列化后一起带入 `Config` 类中。
配置方法参考: [配置](https://nonebot.dev/docs/appendices/config)
- **参数**
auto
### _class-var_ `driver` {#Config-driver}
- **类型:** str
- **说明**
NoneBot 运行所使用的 `Driver` 。继承自 [Driver](drivers/index.md#Driver) 。
配置格式为 `<module>[:<Driver>][+<module>[:<Mixin>]]*`
`~``nonebot.drivers.` 的缩写。
配置方法参考: [配置驱动器](https://nonebot.dev/docs/advanced/driver#%E9%85%8D%E7%BD%AE%E9%A9%B1%E5%8A%A8%E5%99%A8)
### _class-var_ `host` {#Config-host}
- **类型:** IPvAnyAddress
- **说明:** NoneBot [ReverseDriver](drivers/index.md#ReverseDriver) 服务端监听的 IP/主机名。
### _class-var_ `port` {#Config-port}
- **类型:** int
- **说明:** NoneBot [ReverseDriver](drivers/index.md#ReverseDriver) 服务端监听的端口。
### _class-var_ `log_level` {#Config-log-level}
- **类型:** int | str
- **说明**
NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称。
参考 [记录日志](https://nonebot.dev/docs/appendices/log)[loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
:::tip 提示
日志等级名称应为大写,如 `INFO`
:::
- **用法**
```conf
LOG_LEVEL=25
LOG_LEVEL=INFO
```
### _class-var_ `api_timeout` {#Config-api-timeout}
- **类型:** float | None
- **说明:** API 请求超时时间,单位: 秒。
### _class-var_ `superusers` {#Config-superusers}
- **类型:** set[str]
- **说明:** 机器人超级用户。
- **用法**
```conf
SUPERUSERS=["12345789"]
```
### _class-var_ `nickname` {#Config-nickname}
- **类型:** set[str]
- **说明:** 机器人昵称。
### _class-var_ `command_start` {#Config-command-start}
- **类型:** set[str]
- **说明**
命令的起始标记,用于判断一条消息是不是命令。
参考[命令响应规则](https://nonebot.dev/docs/advanced/matcher#command)。
- **用法**
```conf
COMMAND_START=["/", ""]
```
### _class-var_ `command_sep` {#Config-command-sep}
- **类型:** set[str]
- **说明**
命令的分隔标记,用于将文本形式的命令切分为元组(实际的命令名)。
参考[命令响应规则](https://nonebot.dev/docs/advanced/matcher#command)。
- **用法**
```conf
COMMAND_SEP=["."]
```
### _class-var_ `session_expire_timeout` {#Config-session-expire-timeout}
- **类型:** timedelta
- **说明:** 等待用户回复的超时时间。
- **用法**
```conf
SESSION_EXPIRE_TIMEOUT=120 # 单位: 秒
SESSION_EXPIRE_TIMEOUT=[DD ][HH:MM]SS[.ffffff]
SESSION_EXPIRE_TIMEOUT=P[DD]DT[HH]H[MM]M[SS]S # ISO 8601
```

View File

@@ -0,0 +1,116 @@
---
sidebar_position: 9
description: nonebot.consts 模块
---
# nonebot.consts
本模块包含了 NoneBot 事件处理过程中使用到的常量。
## _var_ `RECEIVE_KEY` {#RECEIVE-KEY}
- **类型:** Literal['\_receive\_{id}']
- **说明:** `receive` 存储 key
## _var_ `LAST_RECEIVE_KEY` {#LAST-RECEIVE-KEY}
- **类型:** Literal['\_last\_receive']
- **说明:** `last_receive` 存储 key
## _var_ `ARG_KEY` {#ARG-KEY}
- **类型:** Literal['{key}']
- **说明:** `arg` 存储 key
## _var_ `REJECT_TARGET` {#REJECT-TARGET}
- **类型:** Literal['\_current\_target']
- **说明:** 当前 `reject` 目标存储 key
## _var_ `REJECT_CACHE_TARGET` {#REJECT-CACHE-TARGET}
- **类型:** Literal['\_next\_target']
- **说明:** 下一个 `reject` 目标存储 key
## _var_ `PREFIX_KEY` {#PREFIX-KEY}
- **类型:** Literal['\_prefix']
- **说明:** 命令前缀存储 key
## _var_ `CMD_KEY` {#CMD-KEY}
- **类型:** Literal['command']
- **说明:** 命令元组存储 key
## _var_ `RAW_CMD_KEY` {#RAW-CMD-KEY}
- **类型:** Literal['raw\_command']
- **说明:** 命令文本存储 key
## _var_ `CMD_ARG_KEY` {#CMD-ARG-KEY}
- **类型:** Literal['command\_arg']
- **说明:** 命令参数存储 key
## _var_ `CMD_START_KEY` {#CMD-START-KEY}
- **类型:** Literal['command\_start']
- **说明:** 命令开头存储 key
## _var_ `CMD_WHITESPACE_KEY` {#CMD-WHITESPACE-KEY}
- **类型:** Literal['command\_whitespace']
- **说明:** 命令与参数间空白符存储 key
## _var_ `SHELL_ARGS` {#SHELL-ARGS}
- **类型:** Literal['\_args']
- **说明:** shell 命令 parse 后参数字典存储 key
## _var_ `SHELL_ARGV` {#SHELL-ARGV}
- **类型:** Literal['\_argv']
- **说明:** shell 命令原始参数列表存储 key
## _var_ `REGEX_MATCHED` {#REGEX-MATCHED}
- **类型:** Literal['\_matched']
- **说明:** 正则匹配结果存储 key
## _var_ `STARTSWITH_KEY` {#STARTSWITH-KEY}
- **类型:** Literal['\_startswith']
- **说明:** 响应触发前缀 key
## _var_ `ENDSWITH_KEY` {#ENDSWITH-KEY}
- **类型:** Literal['\_endswith']
- **说明:** 响应触发后缀 key
## _var_ `FULLMATCH_KEY` {#FULLMATCH-KEY}
- **类型:** Literal['\_fullmatch']
- **说明:** 响应触发完整消息 key
## _var_ `KEYWORD_KEY` {#KEYWORD-KEY}
- **类型:** Literal['\_keyword']
- **说明:** 响应触发关键字 key

View File

@@ -0,0 +1,3 @@
{
"position": 13
}

View File

@@ -0,0 +1,98 @@
---
sidebar_position: 0
description: nonebot.dependencies 模块
---
# nonebot.dependencies
本模块模块实现了依赖注入的定义与处理。
## _abstract class_ `Param(*args, validate=False, **kwargs)` {#Param}
- **说明**
依赖注入的基本单元 —— 参数。
继承自 `pydantic.fields.FieldInfo`,用于描述参数信息(不包括参数名)。
- **参数**
- `*args`
- `validate` (bool)
- `**kwargs` (Any)
## _class_ `Dependent(<auto>)` {#Dependent}
- **说明:** 依赖注入容器
- **参数**
- `call`: 依赖注入的可调用对象,可以是任何 Callable 对象
- `pre_checkers`: 依赖注入解析前的参数检查
- `params`: 具名参数列表
- `parameterless`: 匿名参数列表
- `allow_types`: 允许的参数类型
### _staticmethod_ `parse_params(call, allow_types)` {#Dependent-parse-params}
- **参数**
- `call` (\_DependentCallable[R])
- `allow_types` (tuple[type[Param], ...])
- **返回**
- tuple[ModelField, ...]
### _staticmethod_ `parse_parameterless(parameterless, allow_types)` {#Dependent-parse-parameterless}
- **参数**
- `parameterless` (tuple[Any, ...])
- `allow_types` (tuple[type[Param], ...])
- **返回**
- tuple[Param, ...]
### _classmethod_ `parse(*, call, parameterless=None, allow_types)` {#Dependent-parse}
- **参数**
- `call` (\_DependentCallable[R])
- `parameterless` (Iterable[Any] | None)
- `allow_types` (Iterable[type[Param]])
- **返回**
- Dependent[R]
### _async method_ `check(**params)` {#Dependent-check}
- **参数**
- `**params` (Any)
- **返回**
- None
### _async method_ `solve(**params)` {#Dependent-solve}
- **参数**
- `**params` (Any)
- **返回**
- dict[str, Any]

View File

@@ -0,0 +1,46 @@
---
sidebar_position: 1
description: nonebot.dependencies.utils 模块
---
# nonebot.dependencies.utils
## _def_ `get_typed_signature(call)` {#get-typed-signature}
- **说明:** 获取可调用对象签名
- **参数**
- `call` ((...) -> Any)
- **返回**
- inspect.Signature
## _def_ `get_typed_annotation(param, globalns)` {#get-typed-annotation}
- **说明:** 获取参数的类型注解
- **参数**
- `param` (inspect.Parameter)
- `globalns` (dict[str, Any])
- **返回**
- Any
## _def_ `check_field_type(field, value)` {#check-field-type}
- **说明:** 检查字段类型是否匹配
- **参数**
- `field` (ModelField)
- `value` (Any)
- **返回**
- Any

View File

@@ -0,0 +1,3 @@
{
"position": 14
}

View File

@@ -0,0 +1,136 @@
---
sidebar_position: 2
description: nonebot.drivers.aiohttp 模块
---
# nonebot.drivers.aiohttp
[AIOHTTP](https://aiohttp.readthedocs.io/en/stable/) 驱动适配器。
```bash
nb driver install aiohttp
# 或者
pip install nonebot2[aiohttp]
```
:::tip 提示
本驱动仅支持客户端连接
:::
## _class_ `Mixin(<auto>)` {#Mixin}
- **说明:** AIOHTTP Mixin
- **参数**
auto
### _async method_ `request(setup)` {#Mixin-request}
- **参数**
- `setup` ([Request](index.md#Request))
- **返回**
- [Response](index.md#Response)
### _method_ `websocket(setup)` {#Mixin-websocket}
- **参数**
- `setup` ([Request](index.md#Request))
- **返回**
- AsyncGenerator[[WebSocket](index.md#WebSocket), None]
## _class_ `WebSocket(*, request, session, websocket)` {#WebSocket}
- **说明:** AIOHTTP Websocket Wrapper
- **参数**
- `request` ([Request](index.md#Request))
- `session` (aiohttp.ClientSession)
- `websocket` (aiohttp.ClientWebSocketResponse)
### _async method_ `accept()` {#WebSocket-accept}
- **参数**
empty
- **返回**
- untyped
### _async method_ `close(code=1000)` {#WebSocket-close}
- **参数**
- `code` (int)
- **返回**
- untyped
### _async method_ `receive()` {#WebSocket-receive}
- **参数**
empty
- **返回**
- str
### _async method_ `receive_text()` {#WebSocket-receive-text}
- **参数**
empty
- **返回**
- str
### _async method_ `receive_bytes()` {#WebSocket-receive-bytes}
- **参数**
empty
- **返回**
- bytes
### _async method_ `send_text(data)` {#WebSocket-send-text}
- **参数**
- `data` (str)
- **返回**
- None
### _async method_ `send_bytes(data)` {#WebSocket-send-bytes}
- **参数**
- `data` (bytes)
- **返回**
- None
## _class_ `Driver(env, config)` {#Driver}
- **参数**
- `env` ([Env](../config.md#Env))
- `config` ([Config](../config.md#Config))

View File

@@ -0,0 +1,240 @@
---
sidebar_position: 1
description: nonebot.drivers.fastapi 模块
---
# nonebot.drivers.fastapi
[FastAPI](https://fastapi.tiangolo.com/) 驱动适配
```bash
nb driver install fastapi
# 或者
pip install nonebot2[fastapi]
```
:::tip 提示
本驱动仅支持服务端连接
:::
## _class_ `Config(<auto>)` {#Config}
- **说明:** FastAPI 驱动框架设置,详情参考 FastAPI 文档
- **参数**
auto
### _class-var_ `fastapi_openapi_url` {#Config-fastapi-openapi-url}
- **类型:** str | None
- **说明:** `openapi.json` 地址,默认为 `None` 即关闭
### _class-var_ `fastapi_docs_url` {#Config-fastapi-docs-url}
- **类型:** str | None
- **说明:** `swagger` 地址,默认为 `None` 即关闭
### _class-var_ `fastapi_redoc_url` {#Config-fastapi-redoc-url}
- **类型:** str | None
- **说明:** `redoc` 地址,默认为 `None` 即关闭
### _class-var_ `fastapi_include_adapter_schema` {#Config-fastapi-include-adapter-schema}
- **类型:** bool
- **说明:** 是否包含适配器路由的 schema默认为 `True`
### _class-var_ `fastapi_reload` {#Config-fastapi-reload}
- **类型:** bool
- **说明:** 开启/关闭冷重载
### _class-var_ `fastapi_reload_dirs` {#Config-fastapi-reload-dirs}
- **类型:** list[str] | None
- **说明:** 重载监控文件夹列表,默认为 uvicorn 默认值
### _class-var_ `fastapi_reload_delay` {#Config-fastapi-reload-delay}
- **类型:** float
- **说明:** 重载延迟,默认为 uvicorn 默认值
### _class-var_ `fastapi_reload_includes` {#Config-fastapi-reload-includes}
- **类型:** list[str] | None
- **说明:** 要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
### _class-var_ `fastapi_reload_excludes` {#Config-fastapi-reload-excludes}
- **类型:** list[str] | None
- **说明:** 不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
### _class-var_ `fastapi_extra` {#Config-fastapi-extra}
- **类型:** dict[str, Any]
- **说明:** 传递给 `FastAPI` 的其他参数。
## _class_ `Driver(env, config)` {#Driver}
- **说明:** FastAPI 驱动框架。
- **参数**
- `env` ([Env](../config.md#Env))
- `config` (NoneBotConfig)
### _property_ `type` {#Driver-type}
- **类型:** str
- **说明:** 驱动名称: `fastapi`
### _property_ `server_app` {#Driver-server-app}
- **类型:** FastAPI
- **说明:** `FastAPI APP` 对象
### _property_ `asgi` {#Driver-asgi}
- **类型:** FastAPI
- **说明:** `FastAPI APP` 对象
### _property_ `logger` {#Driver-logger}
- **类型:** logging.Logger
- **说明:** fastapi 使用的 logger
### _method_ `setup_http_server(setup)` {#Driver-setup-http-server}
- **参数**
- `setup` ([HTTPServerSetup](index.md#HTTPServerSetup))
- **返回**
- untyped
### _method_ `setup_websocket_server(setup)` {#Driver-setup-websocket-server}
- **参数**
- `setup` ([WebSocketServerSetup](index.md#WebSocketServerSetup))
- **返回**
- None
### _method_ `run(host=None, port=None, *, app=None, **kwargs)` {#Driver-run}
- **说明:** 使用 `uvicorn` 启动 FastAPI
- **参数**
- `host` (str | None)
- `port` (int | None)
- `app` (str | None)
- `**kwargs`
- **返回**
- untyped
## _class_ `FastAPIWebSocket(*, request, websocket)` {#FastAPIWebSocket}
- **说明:** FastAPI WebSocket Wrapper
- **参数**
- `request` (BaseRequest)
- `websocket` ([WebSocket](index.md#WebSocket))
### _async method_ `accept()` {#FastAPIWebSocket-accept}
- **参数**
empty
- **返回**
- None
### _async method_ `close(code=status.WS_1000_NORMAL_CLOSURE, reason="")` {#FastAPIWebSocket-close}
- **参数**
- `code` (int)
- `reason` (str)
- **返回**
- None
### _async method_ `receive()` {#FastAPIWebSocket-receive}
- **参数**
empty
- **返回**
- str | bytes
### _async method_ `receive_text()` {#FastAPIWebSocket-receive-text}
- **参数**
empty
- **返回**
- str
### _async method_ `receive_bytes()` {#FastAPIWebSocket-receive-bytes}
- **参数**
empty
- **返回**
- bytes
### _async method_ `send_text(data)` {#FastAPIWebSocket-send-text}
- **参数**
- `data` (str)
- **返回**
- None
### _async method_ `send_bytes(data)` {#FastAPIWebSocket-send-bytes}
- **参数**
- `data` (bytes)
- **返回**
- None

View File

@@ -0,0 +1,44 @@
---
sidebar_position: 3
description: nonebot.drivers.httpx 模块
---
# nonebot.drivers.httpx
[HTTPX](https://www.python-httpx.org/) 驱动适配
```bash
nb driver install httpx
# 或者
pip install nonebot2[httpx]
```
:::tip 提示
本驱动仅支持客户端 HTTP 连接
:::
## _class_ `Mixin(<auto>)` {#Mixin}
- **说明:** HTTPX Mixin
- **参数**
auto
### _async method_ `request(setup)` {#Mixin-request}
- **参数**
- `setup` ([Request](index.md#Request))
- **返回**
- [Response](index.md#Response)
## _class_ `Driver(env, config)` {#Driver}
- **参数**
- `env` ([Env](../config.md#Env))
- `config` ([Config](../config.md#Config))

View File

@@ -0,0 +1,572 @@
---
sidebar_position: 0
description: nonebot.drivers 模块
---
# nonebot.drivers
本模块定义了驱动适配器基类。
各驱动请继承以下基类。
## _abstract class_ `Mixin(<auto>)` {#Mixin}
- **说明:** 可与其他驱动器共用的混入基类。
- **参数**
auto
### _abstract property_ `type` {#Mixin-type}
- **类型:** str
- **说明:** 混入驱动类型名称
## _abstract class_ `Driver(env, config)` {#Driver}
- **说明**
驱动器基类。
驱动器控制框架的启动和停止,适配器的注册,以及机器人生命周期管理。
- **参数**
- `env` ([Env](../config.md#Env)): 包含环境信息的 Env 对象
- `config` ([Config](../config.md#Config)): 包含配置信息的 Config 对象
### _instance-var_ `env` {#Driver-env}
- **类型:** str
- **说明:** 环境名称
### _instance-var_ `config` {#Driver-config}
- **类型:** [Config](../config.md#Config)
- **说明:** 全局配置对象
### _property_ `bots` {#Driver-bots}
- **类型:** dict[str, [Bot](../adapters/index.md#Bot)]
- **说明:** 获取当前所有已连接的 Bot
### _method_ `register_adapter(adapter, **kwargs)` {#Driver-register-adapter}
- **说明:** 注册一个协议适配器
- **参数**
- `adapter` (type[[Adapter](../adapters/index.md#Adapter)]): 适配器类
- `**kwargs`: 其他传递给适配器的参数
- **返回**
- None
### _abstract property_ `type` {#Driver-type}
- **类型:** str
- **说明:** 驱动类型名称
### _abstract property_ `logger` {#Driver-logger}
- **类型:** untyped
- **说明:** 驱动专属 logger 日志记录器
### _abstract method_ `run(*args, **kwargs)` {#Driver-run}
- **说明:** 启动驱动框架
- **参数**
- `*args`
- `**kwargs`
- **返回**
- untyped
### _method_ `on_startup(func)` {#Driver-on-startup}
- **说明:** 注册一个启动时执行的函数
- **参数**
- `func` (LIFESPAN_FUNC)
- **返回**
- LIFESPAN_FUNC
### _method_ `on_shutdown(func)` {#Driver-on-shutdown}
- **说明:** 注册一个停止时执行的函数
- **参数**
- `func` (LIFESPAN_FUNC)
- **返回**
- LIFESPAN_FUNC
### _classmethod_ `on_bot_connect(func)` {#Driver-on-bot-connect}
- **说明**
装饰一个函数使他在 bot 连接成功时执行。
钩子函数参数:
- bot: 当前连接上的 Bot 对象
- **参数**
- `func` ([T_BotConnectionHook](../typing.md#T-BotConnectionHook))
- **返回**
- [T_BotConnectionHook](../typing.md#T-BotConnectionHook)
### _classmethod_ `on_bot_disconnect(func)` {#Driver-on-bot-disconnect}
- **说明**
装饰一个函数使他在 bot 连接断开时执行。
钩子函数参数:
- bot: 当前连接上的 Bot 对象
- **参数**
- `func` ([T_BotDisconnectionHook](../typing.md#T-BotDisconnectionHook))
- **返回**
- [T_BotDisconnectionHook](../typing.md#T-BotDisconnectionHook)
## _class_ `Cookies(cookies=None)` {#Cookies}
- **参数**
- `cookies` (CookieTypes)
### _method_ `set(name, value, domain="", path="/")` {#Cookies-set}
- **参数**
- `name` (str)
- `value` (str)
- `domain` (str)
- `path` (str)
- **返回**
- None
### _method_ `get(name, default=None, domain=None, path=None)` {#Cookies-get}
- **参数**
- `name` (str)
- `default` (str | None)
- `domain` (str | None)
- `path` (str | None)
- **返回**
- str | None
### _method_ `delete(name, domain=None, path=None)` {#Cookies-delete}
- **参数**
- `name` (str)
- `domain` (str | None)
- `path` (str | None)
- **返回**
- None
### _method_ `clear(domain=None, path=None)` {#Cookies-clear}
- **参数**
- `domain` (str | None)
- `path` (str | None)
- **返回**
- None
### _method_ `update(cookies=None)` {#Cookies-update}
- **参数**
- `cookies` (CookieTypes)
- **返回**
- None
### _method_ `as_header(request)` {#Cookies-as-header}
- **参数**
- `request` (Request)
- **返回**
- dict[str, str]
## _class_ `Request(method, url, *, params=None, headers=None, cookies=None, content=None, data=None, json=None, files=None, version=HTTPVersion.H11, timeout=None, proxy=None)` {#Request}
- **参数**
- `method` (str | bytes)
- `url` (URL | str | RawURL)
- `params` (QueryTypes)
- `headers` (HeaderTypes)
- `cookies` (CookieTypes)
- `content` (ContentTypes)
- `data` (DataTypes)
- `json` (Any)
- `files` (FilesTypes)
- `version` (str | HTTPVersion)
- `timeout` (float | None)
- `proxy` (str | None)
## _class_ `Response(status_code, *, headers=None, content=None, request=None)` {#Response}
- **参数**
- `status_code` (int)
- `headers` (HeaderTypes)
- `content` (ContentTypes)
- `request` (Request | None)
## _abstract class_ `ASGIMixin(<auto>)` {#ASGIMixin}
- **说明**
ASGI 服务端基类。
将后端框架封装,以满足适配器使用。
- **参数**
auto
### _abstract property_ `server_app` {#ASGIMixin-server-app}
- **类型:** Any
- **说明:** 驱动 APP 对象
### _abstract property_ `asgi` {#ASGIMixin-asgi}
- **类型:** Any
- **说明:** 驱动 ASGI 对象
### _abstract method_ `setup_http_server(setup)` {#ASGIMixin-setup-http-server}
- **说明:** 设置一个 HTTP 服务器路由配置
- **参数**
- `setup` ([HTTPServerSetup](#HTTPServerSetup))
- **返回**
- None
### _abstract method_ `setup_websocket_server(setup)` {#ASGIMixin-setup-websocket-server}
- **说明:** 设置一个 WebSocket 服务器路由配置
- **参数**
- `setup` ([WebSocketServerSetup](#WebSocketServerSetup))
- **返回**
- None
## _abstract class_ `WebSocket(*, request)` {#WebSocket}
- **参数**
- `request` (Request)
### _abstract property_ `closed` {#WebSocket-closed}
- **类型:** bool
- **说明:** 连接是否已经关闭
### _abstract async method_ `accept()` {#WebSocket-accept}
- **说明:** 接受 WebSocket 连接请求
- **参数**
empty
- **返回**
- None
### _abstract async method_ `close(code=1000, reason="")` {#WebSocket-close}
- **说明:** 关闭 WebSocket 连接请求
- **参数**
- `code` (int)
- `reason` (str)
- **返回**
- None
### _abstract async method_ `receive()` {#WebSocket-receive}
- **说明:** 接收一条 WebSocket text/bytes 信息
- **参数**
empty
- **返回**
- str | bytes
### _abstract async method_ `receive_text()` {#WebSocket-receive-text}
- **说明:** 接收一条 WebSocket text 信息
- **参数**
empty
- **返回**
- str
### _abstract async method_ `receive_bytes()` {#WebSocket-receive-bytes}
- **说明:** 接收一条 WebSocket binary 信息
- **参数**
empty
- **返回**
- bytes
### _async method_ `send(data)` {#WebSocket-send}
- **说明:** 发送一条 WebSocket text/bytes 信息
- **参数**
- `data` (str | bytes)
- **返回**
- None
### _abstract async method_ `send_text(data)` {#WebSocket-send-text}
- **说明:** 发送一条 WebSocket text 信息
- **参数**
- `data` (str)
- **返回**
- None
### _abstract async method_ `send_bytes(data)` {#WebSocket-send-bytes}
- **说明:** 发送一条 WebSocket binary 信息
- **参数**
- `data` (bytes)
- **返回**
- None
## _enum_ `HTTPVersion` {#HTTPVersion}
- **说明:** An enumeration.
- **参数**
auto
- `H10: '1.0'`
- `H11: '1.1'`
- `H2: '2'`
## _abstract class_ `ForwardMixin(<auto>)` {#ForwardMixin}
- **说明:** 客户端混入基类。
- **参数**
auto
## _abstract class_ `ReverseMixin(<auto>)` {#ReverseMixin}
- **说明:** 服务端混入基类。
- **参数**
auto
## _var_ `ForwardDriver` {#ForwardDriver}
- **类型:** ForwardMixin
- **说明**
支持客户端请求的驱动器。
**Deprecated**,请使用 [ForwardMixin](#ForwardMixin) 或其子类代替。
## _var_ `ReverseDriver` {#ReverseDriver}
- **类型:** ReverseMixin
- **说明**
支持服务端请求的驱动器。
**Deprecated**,请使用 [ReverseMixin](#ReverseMixin) 或其子类代替。
## _def_ `combine_driver(driver, *mixins)` {#combine-driver}
- **说明:** 将一个驱动器和多个混入类合并。
- **重载**
**1.** `(driver) -> type[D]`
- **参数**
- `driver` (type[D])
- **返回**
- type[D]
**2.** `(driver, *mixins) -> type[CombinedDriver]`
- **参数**
- `driver` (type[D])
- `*mixins` (type[[Mixin](#Mixin)])
- **返回**
- type[CombinedDriver]
## _abstract class_ `HTTPClientMixin(<auto>)` {#HTTPClientMixin}
- **说明:** HTTP 客户端混入基类。
- **参数**
auto
### _abstract async method_ `request(setup)` {#HTTPClientMixin-request}
- **说明:** 发送一个 HTTP 请求
- **参数**
- `setup` ([Request](#Request))
- **返回**
- [Response](#Response)
## _class_ `HTTPServerSetup(<auto>)` {#HTTPServerSetup}
- **说明:** HTTP 服务器路由配置。
- **参数**
auto
## _abstract class_ `WebSocketClientMixin(<auto>)` {#WebSocketClientMixin}
- **说明:** WebSocket 客户端混入基类。
- **参数**
auto
### _abstract method_ `websocket(setup)` {#WebSocketClientMixin-websocket}
- **说明:** 发起一个 WebSocket 连接
- **参数**
- `setup` ([Request](#Request))
- **返回**
- AsyncGenerator[[WebSocket](#WebSocket), None]
## _class_ `WebSocketServerSetup(<auto>)` {#WebSocketServerSetup}
- **说明:** WebSocket 服务器路由配置。
- **参数**
auto

View File

@@ -0,0 +1,60 @@
---
sidebar_position: 6
description: nonebot.drivers.none 模块
---
# nonebot.drivers.none
None 驱动适配
:::tip 提示
本驱动不支持任何服务器或客户端连接
:::
## _class_ `Driver(env, config)` {#Driver}
- **说明:** None 驱动框架
- **参数**
- `env` ([Env](../config.md#Env))
- `config` ([Config](../config.md#Config))
### _property_ `type` {#Driver-type}
- **类型:** str
- **说明:** 驱动名称: `none`
### _property_ `logger` {#Driver-logger}
- **类型:** untyped
- **说明:** none driver 使用的 logger
### _method_ `run(*args, **kwargs)` {#Driver-run}
- **说明:** 启动 none driver
- **参数**
- `*args`
- `**kwargs`
- **返回**
- untyped
### _method_ `exit(force=False)` {#Driver-exit}
- **说明:** 退出 none driver
- **参数**
- `force` (bool): 强制退出
- **返回**
- untyped

View File

@@ -0,0 +1,216 @@
---
sidebar_position: 5
description: nonebot.drivers.quart 模块
---
# nonebot.drivers.quart
[Quart](https://pgjones.gitlab.io/quart/index.html) 驱动适配
```bash
nb driver install quart
# 或者
pip install nonebot2[quart]
```
:::tip 提示
本驱动仅支持服务端连接
:::
## _class_ `Config(<auto>)` {#Config}
- **说明:** Quart 驱动框架设置
- **参数**
auto
### _class-var_ `quart_reload` {#Config-quart-reload}
- **类型:** bool
- **说明:** 开启/关闭冷重载
### _class-var_ `quart_reload_dirs` {#Config-quart-reload-dirs}
- **类型:** list[str] | None
- **说明:** 重载监控文件夹列表,默认为 uvicorn 默认值
### _class-var_ `quart_reload_delay` {#Config-quart-reload-delay}
- **类型:** float
- **说明:** 重载延迟,默认为 uvicorn 默认值
### _class-var_ `quart_reload_includes` {#Config-quart-reload-includes}
- **类型:** list[str] | None
- **说明:** 要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
### _class-var_ `quart_reload_excludes` {#Config-quart-reload-excludes}
- **类型:** list[str] | None
- **说明:** 不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
### _class-var_ `quart_extra` {#Config-quart-extra}
- **类型:** dict[str, Any]
- **说明:** 传递给 `Quart` 的其他参数。
## _class_ `Driver(env, config)` {#Driver}
- **说明:** Quart 驱动框架
- **参数**
- `env` ([Env](../config.md#Env))
- `config` (NoneBotConfig)
### _property_ `type` {#Driver-type}
- **类型:** str
- **说明:** 驱动名称: `quart`
### _property_ `server_app` {#Driver-server-app}
- **类型:** Quart
- **说明:** `Quart` 对象
### _property_ `asgi` {#Driver-asgi}
- **类型:** untyped
- **说明:** `Quart` 对象
### _property_ `logger` {#Driver-logger}
- **类型:** untyped
- **说明:** Quart 使用的 logger
### _method_ `setup_http_server(setup)` {#Driver-setup-http-server}
- **参数**
- `setup` ([HTTPServerSetup](index.md#HTTPServerSetup))
- **返回**
- untyped
### _method_ `setup_websocket_server(setup)` {#Driver-setup-websocket-server}
- **参数**
- `setup` ([WebSocketServerSetup](index.md#WebSocketServerSetup))
- **返回**
- None
### _method_ `run(host=None, port=None, *, app=None, **kwargs)` {#Driver-run}
- **说明:** 使用 `uvicorn` 启动 Quart
- **参数**
- `host` (str | None)
- `port` (int | None)
- `app` (str | None)
- `**kwargs`
- **返回**
- untyped
## _class_ `WebSocket(*, request, websocket_ctx)` {#WebSocket}
- **说明:** Quart WebSocket Wrapper
- **参数**
- `request` (BaseRequest)
- `websocket_ctx` (WebsocketContext)
### _async method_ `accept()` {#WebSocket-accept}
- **参数**
empty
- **返回**
- untyped
### _async method_ `close(code=1000, reason="")` {#WebSocket-close}
- **参数**
- `code` (int)
- `reason` (str)
- **返回**
- untyped
### _async method_ `receive()` {#WebSocket-receive}
- **参数**
empty
- **返回**
- str | bytes
### _async method_ `receive_text()` {#WebSocket-receive-text}
- **参数**
empty
- **返回**
- str
### _async method_ `receive_bytes()` {#WebSocket-receive-bytes}
- **参数**
empty
- **返回**
- bytes
### _async method_ `send_text(data)` {#WebSocket-send-text}
- **参数**
- `data` (str)
- **返回**
- untyped
### _async method_ `send_bytes(data)` {#WebSocket-send-bytes}
- **参数**
- `data` (bytes)
- **返回**
- untyped

View File

@@ -0,0 +1,136 @@
---
sidebar_position: 4
description: nonebot.drivers.websockets 模块
---
# nonebot.drivers.websockets
[websockets](https://websockets.readthedocs.io/) 驱动适配
```bash
nb driver install websockets
# 或者
pip install nonebot2[websockets]
```
:::tip 提示
本驱动仅支持客户端 WebSocket 连接
:::
## _def_ `catch_closed(func)` {#catch-closed}
- **参数**
- `func` ((P) -> Awaitable[T])
- **返回**
- (P) -> Awaitable[T]
## _class_ `Mixin(<auto>)` {#Mixin}
- **说明:** Websockets Mixin
- **参数**
auto
### _method_ `websocket(setup)` {#Mixin-websocket}
- **参数**
- `setup` ([Request](index.md#Request))
- **返回**
- AsyncGenerator[[WebSocket](index.md#WebSocket), None]
## _class_ `WebSocket(*, request, websocket)` {#WebSocket}
- **说明:** Websockets WebSocket Wrapper
- **参数**
- `request` ([Request](index.md#Request))
- `websocket` (WebSocketClientProtocol)
### _async method_ `accept()` {#WebSocket-accept}
- **参数**
empty
- **返回**
- untyped
### _async method_ `close(code=1000, reason="")` {#WebSocket-close}
- **参数**
- `code` (int)
- `reason` (str)
- **返回**
- untyped
### _async method_ `receive()` {#WebSocket-receive}
- **参数**
empty
- **返回**
- str | bytes
### _async method_ `receive_text()` {#WebSocket-receive-text}
- **参数**
empty
- **返回**
- str
### _async method_ `receive_bytes()` {#WebSocket-receive-bytes}
- **参数**
empty
- **返回**
- bytes
### _async method_ `send_text(data)` {#WebSocket-send-text}
- **参数**
- `data` (str)
- **返回**
- None
### _async method_ `send_bytes(data)` {#WebSocket-send-bytes}
- **参数**
- `data` (bytes)
- **返回**
- None
## _class_ `Driver(env, config)` {#Driver}
- **参数**
- `env` ([Env](../config.md#Env))
- `config` ([Config](../config.md#Config))

View File

@@ -0,0 +1,254 @@
---
sidebar_position: 10
description: nonebot.exception 模块
---
# nonebot.exception
本模块包含了所有 NoneBot 运行时可能会抛出的异常。
这些异常并非所有需要用户处理,在 NoneBot 内部运行时被捕获,并进行对应操作。
```bash
NoneBotException
├── ParserExit
├── ProcessException
| ├── IgnoredException
| ├── SkippedException
| | └── TypeMisMatch
| ├── MockApiException
| └── StopPropagation
├── MatcherException
| ├── PausedException
| ├── RejectedException
| └── FinishedException
├── AdapterException
| ├── NoLogException
| ├── ApiNotAvailable
| ├── NetworkError
| └── ActionFailed
└── DriverException
└── WebSocketClosed
```
## _class_ `NoneBotException(<auto>)` {#NoneBotException}
- **说明:** 所有 NoneBot 发生的异常基类。
- **参数**
auto
## _class_ `ParserExit(<auto>)` {#ParserExit}
- **说明:** 处理消息失败时返回的异常。
- **参数**
auto
## _class_ `ProcessException(<auto>)` {#ProcessException}
- **说明:** 事件处理过程中发生的异常基类。
- **参数**
auto
## _class_ `IgnoredException(<auto>)` {#IgnoredException}
- **说明:** 指示 NoneBot 应该忽略该事件。可由 PreProcessor 抛出。
- **参数**
- `reason`: 忽略事件的原因
## _class_ `SkippedException(<auto>)` {#SkippedException}
- **说明**
指示 NoneBot 立即结束当前 `Dependent` 的运行。
例如,可以在 `Handler` 中通过 [Matcher.skip](matcher.md#Matcher-skip) 抛出。
- **参数**
auto
- **用法**
```python
def always_skip():
Matcher.skip()
@matcher.handle()
async def handler(dependency = Depends(always_skip)):
# never run
```
## _class_ `TypeMisMatch(<auto>)` {#TypeMisMatch}
- **说明:** 当前 `Handler` 的参数类型不匹配。
- **参数**
auto
## _class_ `MockApiException(<auto>)` {#MockApiException}
- **说明:** 指示 NoneBot 阻止本次 API 调用或修改本次调用返回值,并返回自定义内容。 可由 api hook 抛出。
- **参数**
- `result`: 返回的内容
## _class_ `StopPropagation(<auto>)` {#StopPropagation}
- **说明**
指示 NoneBot 终止事件向下层传播。
在 [Matcher.block](matcher.md#Matcher-block) 为 `True`
或使用 [Matcher.stop_propagation](matcher.md#Matcher-stop-propagation) 方法时抛出。
- **参数**
auto
- **用法**
```python
matcher = on_notice(block=True)
# 或者
@matcher.handle()
async def handler(matcher: Matcher):
matcher.stop_propagation()
```
## _class_ `MatcherException(<auto>)` {#MatcherException}
- **说明:** 所有 Matcher 发生的异常基类。
- **参数**
auto
## _class_ `PausedException(<auto>)` {#PausedException}
- **说明**
指示 NoneBot 结束当前 `Handler` 并等待下一条消息后继续下一个 `Handler`。 可用于用户输入新信息。
可以在 `Handler` 中通过 [Matcher.pause](matcher.md#Matcher-pause) 抛出。
- **参数**
auto
- **用法**
```python
@matcher.handle()
async def handler():
await matcher.pause("some message")
```
## _class_ `RejectedException(<auto>)` {#RejectedException}
- **说明**
指示 NoneBot 结束当前 `Handler` 并等待下一条消息后重新运行当前 `Handler`。 可用于用户重新输入。
可以在 `Handler` 中通过 [Matcher.reject](matcher.md#Matcher-reject) 抛出。
- **参数**
auto
- **用法**
```python
@matcher.handle()
async def handler():
await matcher.reject("some message")
```
## _class_ `FinishedException(<auto>)` {#FinishedException}
- **说明**
指示 NoneBot 结束当前 `Handler` 且后续 `Handler` 不再被运行。可用于结束用户会话。
可以在 `Handler` 中通过 [Matcher.finish](matcher.md#Matcher-finish) 抛出。
- **参数**
auto
- **用法**
```python
@matcher.handle()
async def handler():
await matcher.finish("some message")
```
## _class_ `AdapterException(<auto>)` {#AdapterException}
- **说明:** 代表 `Adapter` 抛出的异常,所有的 `Adapter` 都要在内部继承自这个 `Exception`。
- **参数**
- `adapter_name`: 标识 adapter
## _class_ `NoLogException(<auto>)` {#NoLogException}
- **说明**
指示 NoneBot 对当前 `Event` 进行处理但不显示 Log 信息。
可在 [Event.get_log_string](adapters/index.md#Event-get-log-string) 时抛出
- **参数**
auto
## _class_ `ApiNotAvailable(<auto>)` {#ApiNotAvailable}
- **说明:** 在 API 连接不可用时抛出。
- **参数**
auto
## _class_ `NetworkError(<auto>)` {#NetworkError}
- **说明:** 在网络出现问题时抛出, 如: API 请求地址不正确, API 请求无返回或返回状态非正常等。
- **参数**
auto
## _class_ `ActionFailed(<auto>)` {#ActionFailed}
- **说明:** API 请求成功返回数据,但 API 操作失败。
- **参数**
auto
## _class_ `DriverException(<auto>)` {#DriverException}
- **说明:** `Driver` 抛出的异常基类。
- **参数**
auto
## _class_ `WebSocketClosed(<auto>)` {#WebSocketClosed}
- **说明:** WebSocket 连接已关闭。
- **参数**
auto

View File

@@ -0,0 +1,283 @@
---
sidebar_position: 0
description: nonebot 模块
---
# nonebot
本模块主要定义了 NoneBot 启动所需函数,供 bot 入口文件调用。
## 快捷导入
为方便使用,本模块从子模块导入了部分内容,以下内容可以直接通过本模块导入:
- `on` => [`on`](plugin/on.md#on)
- `on_metaevent` => [`on_metaevent`](plugin/on.md#on-metaevent)
- `on_message` => [`on_message`](plugin/on.md#on-message)
- `on_notice` => [`on_notice`](plugin/on.md#on-notice)
- `on_request` => [`on_request`](plugin/on.md#on-request)
- `on_startswith` => [`on_startswith`](plugin/on.md#on-startswith)
- `on_endswith` => [`on_endswith`](plugin/on.md#on-endswith)
- `on_fullmatch` => [`on_fullmatch`](plugin/on.md#on-fullmatch)
- `on_keyword` => [`on_keyword`](plugin/on.md#on-keyword)
- `on_command` => [`on_command`](plugin/on.md#on-command)
- `on_shell_command` => [`on_shell_command`](plugin/on.md#on-shell-command)
- `on_regex` => [`on_regex`](plugin/on.md#on-regex)
- `on_type` => [`on_type`](plugin/on.md#on-type)
- `CommandGroup` => [`CommandGroup`](plugin/on.md#CommandGroup)
- `Matchergroup` => [`MatcherGroup`](plugin/on.md#MatcherGroup)
- `load_plugin` => [`load_plugin`](plugin/load.md#load-plugin)
- `load_plugins` => [`load_plugins`](plugin/load.md#load-plugins)
- `load_all_plugins` => [`load_all_plugins`](plugin/load.md#load-all-plugins)
- `load_from_json` => [`load_from_json`](plugin/load.md#load-from-json)
- `load_from_toml` => [`load_from_toml`](plugin/load.md#load-from-toml)
- `load_builtin_plugin` =>
[`load_builtin_plugin`](plugin/load.md#load-builtin-plugin)
- `load_builtin_plugins` =>
[`load_builtin_plugins`](plugin/load.md#load-builtin-plugins)
- `get_plugin` => [`get_plugin`](plugin/index.md#get-plugin)
- `get_plugin_by_module_name` =>
[`get_plugin_by_module_name`](plugin/index.md#get-plugin-by-module-name)
- `get_loaded_plugins` =>
[`get_loaded_plugins`](plugin/index.md#get-loaded-plugins)
- `get_available_plugin_names` =>
[`get_available_plugin_names`](plugin/index.md#get-available-plugin-names)
- `require` => [`require`](plugin/load.md#require)
## _def_ `get_driver()` {#get-driver}
- **说明**
获取全局 [Driver](drivers/index.md#Driver) 实例。
可用于在计划任务的回调等情形中获取当前 [Driver](drivers/index.md#Driver) 实例。
- **参数**
empty
- **返回**
- [Driver](drivers/index.md#Driver): 全局 [Driver](drivers/index.md#Driver) 对象
- **异常**
- ValueError: 全局 [Driver](drivers/index.md#Driver) 对象尚未初始化 ([nonebot.init](#init) 尚未调用)
- **用法**
```python
driver = nonebot.get_driver()
```
## _def_ `get_adapter(name)` {#get-adapter}
- **说明:** 获取已注册的 [Adapter](adapters/index.md#Adapter) 实例。
- **重载**
**1.** `(name) -> Adapter`
- **参数**
- `name` (str): 适配器名称
- **返回**
- [Adapter](adapters/index.md#Adapter): 指定名称的 [Adapter](adapters/index.md#Adapter) 对象
**2.** `(name) -> A`
- **参数**
- `name` (type[A]): 适配器类型
- **返回**
- A: 指定类型的 [Adapter](adapters/index.md#Adapter) 对象
- **异常**
- ValueError: 指定的 [Adapter](adapters/index.md#Adapter) 未注册
- ValueError: 全局 [Driver](drivers/index.md#Driver) 对象尚未初始化 ([nonebot.init](#init) 尚未调用)
- **用法**
```python
from nonebot.adapters.console import Adapter
adapter = nonebot.get_adapter(Adapter)
```
## _def_ `get_adapters()` {#get-adapters}
- **说明:** 获取所有已注册的 [Adapter](adapters/index.md#Adapter) 实例。
- **参数**
empty
- **返回**
- dict[str, [Adapter](adapters/index.md#Adapter)]: 所有 [Adapter](adapters/index.md#Adapter) 实例字典
- **异常**
- ValueError: 全局 [Driver](drivers/index.md#Driver) 对象尚未初始化 ([nonebot.init](#init) 尚未调用)
- **用法**
```python
adapters = nonebot.get_adapters()
```
## _def_ `get_app()` {#get-app}
- **说明:** 获取全局 [ASGIMixin](drivers/index.md#ASGIMixin) 对应的 Server App 对象。
- **参数**
empty
- **返回**
- Any: Server App 对象
- **异常**
- AssertionError: 全局 Driver 对象不是 [ASGIMixin](drivers/index.md#ASGIMixin) 类型
- ValueError: 全局 [Driver](drivers/index.md#Driver) 对象尚未初始化 ([nonebot.init](#init) 尚未调用)
- **用法**
```python
app = nonebot.get_app()
```
## _def_ `get_asgi()` {#get-asgi}
- **说明:** 获取全局 [ASGIMixin](drivers/index.md#ASGIMixin) 对应的 [ASGI](https://asgi.readthedocs.io/) 对象。
- **参数**
empty
- **返回**
- Any: ASGI 对象
- **异常**
- AssertionError: 全局 Driver 对象不是 [ASGIMixin](drivers/index.md#ASGIMixin) 类型
- ValueError: 全局 [Driver](drivers/index.md#Driver) 对象尚未初始化 ([nonebot.init](#init) 尚未调用)
- **用法**
```python
asgi = nonebot.get_asgi()
```
## _def_ `get_bot(self_id=None)` {#get-bot}
- **说明**
获取一个连接到 NoneBot 的 [Bot](adapters/index.md#Bot) 对象。
当提供 `self_id` 时,此函数是 `get_bots()[self_id]` 的简写;
当不提供时,返回一个 [Bot](adapters/index.md#Bot)。
- **参数**
- `self_id` (str | None): 用来识别 [Bot](adapters/index.md#Bot) 的 [Bot.self_id](adapters/index.md#Bot-self-id) 属性
- **返回**
- [Bot](adapters/index.md#Bot): [Bot](adapters/index.md#Bot) 对象
- **异常**
- KeyError: 对应 self_id 的 Bot 不存在
- ValueError: 没有传入 self_id 且没有 Bot 可用
- ValueError: 全局 [Driver](drivers/index.md#Driver) 对象尚未初始化 ([nonebot.init](#init) 尚未调用)
- **用法**
```python
assert nonebot.get_bot("12345") == nonebot.get_bots()["12345"]
another_unspecified_bot = nonebot.get_bot()
```
## _def_ `get_bots()` {#get-bots}
- **说明:** 获取所有连接到 NoneBot 的 [Bot](adapters/index.md#Bot) 对象。
- **参数**
empty
- **返回**
- dict[str, [Bot](adapters/index.md#Bot)]: 一个以 [Bot.self_id](adapters/index.md#Bot-self-id) 为键
[Bot](adapters/index.md#Bot) 对象为值的字典
- **异常**
- ValueError: 全局 [Driver](drivers/index.md#Driver) 对象尚未初始化 ([nonebot.init](#init) 尚未调用)
- **用法**
```python
bots = nonebot.get_bots()
```
## _def_ `init(*, _env_file=None, **kwargs)` {#init}
- **说明**
初始化 NoneBot 以及 全局 [Driver](drivers/index.md#Driver) 对象。
NoneBot 将会从 .env 文件中读取环境信息,并使用相应的 env 文件配置。
也可以传入自定义的 `_env_file` 来指定 NoneBot 从该文件读取配置。
- **参数**
- `_env_file` (DotenvType | None): 配置文件名,默认从 `.env.{env_name}` 中读取配置
- `**kwargs` (Any): 任意变量,将会存储到 [Driver.config](drivers/index.md#Driver-config) 对象里
- **返回**
- None
- **用法**
```python
nonebot.init(database=Database(...))
```
## _def_ `run(*args, **kwargs)` {#run}
- **说明:** 启动 NoneBot即运行全局 [Driver](drivers/index.md#Driver) 对象。
- **参数**
- `*args` (Any): 传入 [Driver.run](drivers/index.md#Driver-run) 的位置参数
- `**kwargs` (Any): 传入 [Driver.run](drivers/index.md#Driver-run) 的命名参数
- **返回**
- None
- **用法**
```python
nonebot.run(host="127.0.0.1", port=8080)
```

View File

@@ -0,0 +1,71 @@
---
sidebar_position: 7
description: nonebot.log 模块
---
# nonebot.log
本模块定义了 NoneBot 的日志记录 Logger。
NoneBot 使用 [`loguru`][loguru] 来记录日志信息。
自定义 logger 请参考 [自定义日志](https://nonebot.dev/docs/appendices/log)
以及 [`loguru`][loguru] 文档。
[loguru]: https://github.com/Delgan/loguru
## _var_ `logger` {#logger}
- **类型:** Logger
- **说明**
NoneBot 日志记录器对象。
默认信息:
- 格式: `[%(asctime)s %(name)s] %(levelname)s: %(message)s`
- 等级: `INFO` ,根据 `config.log_level` 配置改变
- 输出: 输出至 stdout
- **用法**
```python
from nonebot.log import logger
```
## _class_ `LoguruHandler(<auto>)` {#LoguruHandler}
- **说明:** logging 与 loguru 之间的桥梁,将 logging 的日志转发到 loguru。
- **参数**
auto
### _method_ `emit(record)` {#LoguruHandler-emit}
- **参数**
- `record` (logging.LogRecord)
- **返回**
- untyped
## _def_ `default_filter(record)` {#default-filter}
- **说明:** 默认的日志过滤器,根据 `config.log_level` 配置改变日志等级。
- **参数**
- `record` (Record)
- **返回**
- untyped
## _var_ `default_format` {#default-format}
- **类型:** str
- **说明:** 默认日志格式

View File

@@ -0,0 +1,749 @@
---
sidebar_position: 3
description: nonebot.matcher 模块
---
# nonebot.matcher
本模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行对话。
## _class_ `Matcher()` {#Matcher}
- **说明:** 事件响应器类
- **参数**
empty
### _instance-var_ `handlers` {#Matcher-handlers}
- **类型:** list[[Dependent](dependencies/index.md#Dependent)[Any]]
- **说明:** 事件响应器拥有的事件处理函数列表
### _class-var_ `type` {#Matcher-type}
- **类型:** ClassVar[str]
- **说明:** 事件响应器类型
### _class-var_ `rule` {#Matcher-rule}
- **类型:** ClassVar[[Rule](rule.md#Rule)]
- **说明:** 事件响应器匹配规则
### _class-var_ `permission` {#Matcher-permission}
- **类型:** ClassVar[[Permission](permission.md#Permission)]
- **说明:** 事件响应器触发权限
### _class-var_ `priority` {#Matcher-priority}
- **类型:** ClassVar[int]
- **说明:** 事件响应器优先级
### _class-var_ `block` {#Matcher-block}
- **类型:** bool
- **说明:** 事件响应器是否阻止事件传播
### _class-var_ `temp` {#Matcher-temp}
- **类型:** ClassVar[bool]
- **说明:** 事件响应器是否为临时
### _class-var_ `expire_time` {#Matcher-expire-time}
- **类型:** ClassVar[datetime | None]
- **说明:** 事件响应器过期时间点
### _classmethod_ `new(type_="", rule=None, permission=None, handlers=None, temp=False, priority=1, block=False, *, plugin=None, module=None, source=None, expire_time=None, default_state=None, default_type_updater=None, default_permission_updater=None)` {#Matcher-new}
- **说明:** 创建一个新的事件响应器,并存储至 `matchers <#matchers>`\_
- **参数**
- `type_` (str): 事件响应器类型,与 `event.get_type()` 一致时触发,空字符串表示任意
- `rule` ([Rule](rule.md#Rule) | None): 匹配规则
- `permission` ([Permission](permission.md#Permission) | None): 权限
- `handlers` (list[[T\_Handler](typing.md#T-Handler) | [Dependent](dependencies/index.md#Dependent)[Any]] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器,即触发一次后删除
- `priority` (int): 响应优先级
- `block` (bool): 是否阻止事件向更低优先级的响应器传播
- `plugin` ([Plugin](plugin/model.md#Plugin) | None): **Deprecated.** 事件响应器所在插件
- `module` (ModuleType | None): **Deprecated.** 事件响应器所在模块
- `source` (MatcherSource | None): 事件响应器源代码上下文信息
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `default_state` ([T_State](typing.md#T-State) | None): 默认状态 `state`
- `default_type_updater` ([T_TypeUpdater](typing.md#T-TypeUpdater) | [Dependent](dependencies/index.md#Dependent)[str] | None): 默认事件类型更新函数
- `default_permission_updater` ([T_PermissionUpdater](typing.md#T-PermissionUpdater) | [Dependent](dependencies/index.md#Dependent)[[Permission](permission.md#Permission)] | None): 默认会话权限更新函数
- **返回**
- type[Matcher]: 新的事件响应器类
### _classmethod_ `destroy()` {#Matcher-destroy}
- **说明:** 销毁当前的事件响应器
- **参数**
empty
- **返回**
- None
### _classmethod_ `check_perm(bot, event, stack=None, dependency_cache=None)` {#Matcher-check-perm}
- **说明:** 检查是否满足触发权限
- **参数**
- `bot` ([Bot](adapters/index.md#Bot)): Bot 对象
- `event` ([Event](adapters/index.md#Event)): 上报事件
- `stack` (AsyncExitStack | None): 异步上下文栈
- `dependency_cache` ([T_DependencyCache](typing.md#T-DependencyCache) | None): 依赖缓存
- **返回**
- bool: 是否满足权限
### _classmethod_ `check_rule(bot, event, state, stack=None, dependency_cache=None)` {#Matcher-check-rule}
- **说明:** 检查是否满足匹配规则
- **参数**
- `bot` ([Bot](adapters/index.md#Bot)): Bot 对象
- `event` ([Event](adapters/index.md#Event)): 上报事件
- `state` ([T_State](typing.md#T-State)): 当前状态
- `stack` (AsyncExitStack | None): 异步上下文栈
- `dependency_cache` ([T_DependencyCache](typing.md#T-DependencyCache) | None): 依赖缓存
- **返回**
- bool: 是否满足匹配规则
### _classmethod_ `type_updater(func)` {#Matcher-type-updater}
- **说明:** 装饰一个函数来更改当前事件响应器的默认响应事件类型更新函数
- **参数**
- `func` ([T_TypeUpdater](typing.md#T-TypeUpdater)): 响应事件类型更新函数
- **返回**
- [T_TypeUpdater](typing.md#T-TypeUpdater)
### _classmethod_ `permission_updater(func)` {#Matcher-permission-updater}
- **说明:** 装饰一个函数来更改当前事件响应器的默认会话权限更新函数
- **参数**
- `func` ([T_PermissionUpdater](typing.md#T-PermissionUpdater)): 会话权限更新函数
- **返回**
- [T_PermissionUpdater](typing.md#T-PermissionUpdater)
### _classmethod_ `append_handler(handler, parameterless=None)` {#Matcher-append-handler}
- **参数**
- `handler` ([T_Handler](typing.md#T-Handler))
- `parameterless` (Iterable[Any] | None)
- **返回**
- [Dependent](dependencies/index.md#Dependent)[Any]
### _classmethod_ `handle(parameterless=None)` {#Matcher-handle}
- **说明:** 装饰一个函数来向事件响应器直接添加一个处理函数
- **参数**
- `parameterless` (Iterable[Any] | None): 非参数类型依赖列表
- **返回**
- ([T_Handler](typing.md#T-Handler)) -> [T_Handler](typing.md#T-Handler)
### _classmethod_ `receive(id="", parameterless=None)` {#Matcher-receive}
- **说明:** 装饰一个函数来指示 NoneBot 在接收用户新的一条消息后继续运行该函数
- **参数**
- `id` (str): 消息 ID
- `parameterless` (Iterable[Any] | None): 非参数类型依赖列表
- **返回**
- ([T_Handler](typing.md#T-Handler)) -> [T_Handler](typing.md#T-Handler)
### _classmethod_ `got(key, prompt=None, parameterless=None)` {#Matcher-got}
- **说明**
装饰一个函数来指示 NoneBot 获取一个参数 `key`
当要获取的 `key` 不存在时接收用户新的一条消息再运行该函数,
如果 `key` 已存在则直接继续运行
- **参数**
- `key` (str): 参数名
- `prompt` (str | [Message](adapters/index.md#Message) | [MessageSegment](adapters/index.md#MessageSegment) | [MessageTemplate](adapters/index.md#MessageTemplate) | None): 在参数不存在时向用户发送的消息
- `parameterless` (Iterable[Any] | None): 非参数类型依赖列表
- **返回**
- ([T_Handler](typing.md#T-Handler)) -> [T_Handler](typing.md#T-Handler)
### _classmethod_ `send(message, **kwargs)` {#Matcher-send}
- **说明:** 发送一条消息给当前交互用户
- **参数**
- `message` (str | [Message](adapters/index.md#Message) | [MessageSegment](adapters/index.md#MessageSegment) | [MessageTemplate](adapters/index.md#MessageTemplate)): 消息内容
- `**kwargs` (Any): [Bot.send](adapters/index.md#Bot-send) 的参数, 请参考对应 adapter 的 bot 对象 api
- **返回**
- Any
### _classmethod_ `finish(message=None, **kwargs)` {#Matcher-finish}
- **说明:** 发送一条消息给当前交互用户并结束当前事件响应器
- **参数**
- `message` (str | [Message](adapters/index.md#Message) | [MessageSegment](adapters/index.md#MessageSegment) | [MessageTemplate](adapters/index.md#MessageTemplate) | None): 消息内容
- `**kwargs`: [Bot.send](adapters/index.md#Bot-send) 的参数, 请参考对应 adapter 的 bot 对象 api
- **返回**
- NoReturn
### _classmethod_ `pause(prompt=None, **kwargs)` {#Matcher-pause}
- **说明:** 发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后继续下一个处理函数
- **参数**
- `prompt` (str | [Message](adapters/index.md#Message) | [MessageSegment](adapters/index.md#MessageSegment) | [MessageTemplate](adapters/index.md#MessageTemplate) | None): 消息内容
- `**kwargs`: [Bot.send](adapters/index.md#Bot-send) 的参数, 请参考对应 adapter 的 bot 对象 api
- **返回**
- NoReturn
### _classmethod_ `reject(prompt=None, **kwargs)` {#Matcher-reject}
- **说明:** 最近使用 `got` / `receive` 接收的消息不符合预期, 发送一条消息给当前交互用户并将当前事件处理流程中断在当前位置,在接收用户新的一个事件后从头开始执行当前处理函数
- **参数**
- `prompt` (str | [Message](adapters/index.md#Message) | [MessageSegment](adapters/index.md#MessageSegment) | [MessageTemplate](adapters/index.md#MessageTemplate) | None): 消息内容
- `**kwargs`: [Bot.send](adapters/index.md#Bot-send) 的参数, 请参考对应 adapter 的 bot 对象 api
- **返回**
- NoReturn
### _classmethod_ `reject_arg(key, prompt=None, **kwargs)` {#Matcher-reject-arg}
- **说明:** 最近使用 `got` 接收的消息不符合预期, 发送一条消息给当前交互用户并将当前事件处理流程中断在当前位置,在接收用户新的一条消息后从头开始执行当前处理函数
- **参数**
- `key` (str): 参数名
- `prompt` (str | [Message](adapters/index.md#Message) | [MessageSegment](adapters/index.md#MessageSegment) | [MessageTemplate](adapters/index.md#MessageTemplate) | None): 消息内容
- `**kwargs`: [Bot.send](adapters/index.md#Bot-send) 的参数, 请参考对应 adapter 的 bot 对象 api
- **返回**
- NoReturn
### _classmethod_ `reject_receive(id="", prompt=None, **kwargs)` {#Matcher-reject-receive}
- **说明:** 最近使用 `receive` 接收的消息不符合预期, 发送一条消息给当前交互用户并将当前事件处理流程中断在当前位置,在接收用户新的一个事件后从头开始执行当前处理函数
- **参数**
- `id` (str): 消息 id
- `prompt` (str | [Message](adapters/index.md#Message) | [MessageSegment](adapters/index.md#MessageSegment) | [MessageTemplate](adapters/index.md#MessageTemplate) | None): 消息内容
- `**kwargs`: [Bot.send](adapters/index.md#Bot-send) 的参数, 请参考对应 adapter 的 bot 对象 api
- **返回**
- NoReturn
### _classmethod_ `skip()` {#Matcher-skip}
- **说明**
跳过当前事件处理函数,继续下一个处理函数
通常在事件处理函数的依赖中使用。
- **参数**
empty
- **返回**
- NoReturn
### _method_ `get_receive(id, default=None)` {#Matcher-get-receive}
- **说明**
获取一个 `receive` 事件
如果没有找到对应的事件,返回 `default`
- **重载**
**1.** `(id) -> Event | None`
- **参数**
- `id` (str)
- **返回**
- [Event](adapters/index.md#Event) | None
**2.** `(id, default) -> Event | T`
- **参数**
- `id` (str)
- `default` (T)
- **返回**
- [Event](adapters/index.md#Event) | T
### _method_ `set_receive(id, event)` {#Matcher-set-receive}
- **说明:** 设置一个 `receive` 事件
- **参数**
- `id` (str)
- `event` ([Event](adapters/index.md#Event))
- **返回**
- None
### _method_ `get_last_receive(default=None)` {#Matcher-get-last-receive}
- **说明**
获取最近一次 `receive` 事件
如果没有事件,返回 `default`
- **重载**
**1.** `() -> Event | None`
- **参数**
empty
- **返回**
- [Event](adapters/index.md#Event) | None
**2.** `(default) -> Event | T`
- **参数**
- `default` (T)
- **返回**
- [Event](adapters/index.md#Event) | T
### _method_ `get_arg(key, default=None)` {#Matcher-get-arg}
- **说明**
获取一个 `got` 消息
如果没有找到对应的消息,返回 `default`
- **重载**
**1.** `(key) -> Message | None`
- **参数**
- `key` (str)
- **返回**
- [Message](adapters/index.md#Message) | None
**2.** `(key, default) -> Message | T`
- **参数**
- `key` (str)
- `default` (T)
- **返回**
- [Message](adapters/index.md#Message) | T
### _method_ `set_arg(key, message)` {#Matcher-set-arg}
- **说明:** 设置一个 `got` 消息
- **参数**
- `key` (str)
- `message` ([Message](adapters/index.md#Message))
- **返回**
- None
### _method_ `set_target(target, cache=True)` {#Matcher-set-target}
- **参数**
- `target` (str)
- `cache` (bool)
- **返回**
- None
### _method_ `get_target(default=None)` {#Matcher-get-target}
- **重载**
**1.** `() -> str | None`
- **参数**
empty
- **返回**
- str | None
**2.** `(default) -> str | T`
- **参数**
- `default` (T)
- **返回**
- str | T
### _method_ `stop_propagation()` {#Matcher-stop-propagation}
- **说明:** 阻止事件传播
- **参数**
empty
- **返回**
- untyped
### _async method_ `update_type(bot, event, stack=None, dependency_cache=None)` {#Matcher-update-type}
- **参数**
- `bot` ([Bot](adapters/index.md#Bot))
- `event` ([Event](adapters/index.md#Event))
- `stack` (AsyncExitStack | None)
- `dependency_cache` ([T_DependencyCache](typing.md#T-DependencyCache) | None)
- **返回**
- str
### _async method_ `update_permission(bot, event, stack=None, dependency_cache=None)` {#Matcher-update-permission}
- **参数**
- `bot` ([Bot](adapters/index.md#Bot))
- `event` ([Event](adapters/index.md#Event))
- `stack` (AsyncExitStack | None)
- `dependency_cache` ([T_DependencyCache](typing.md#T-DependencyCache) | None)
- **返回**
- [Permission](permission.md#Permission)
### _async method_ `resolve_reject()` {#Matcher-resolve-reject}
- **参数**
empty
- **返回**
- untyped
### _method_ `ensure_context(bot, event)` {#Matcher-ensure-context}
- **参数**
- `bot` ([Bot](adapters/index.md#Bot))
- `event` ([Event](adapters/index.md#Event))
- **返回**
- untyped
### _async method_ `simple_run(bot, event, state, stack=None, dependency_cache=None)` {#Matcher-simple-run}
- **参数**
- `bot` ([Bot](adapters/index.md#Bot))
- `event` ([Event](adapters/index.md#Event))
- `state` ([T_State](typing.md#T-State))
- `stack` (AsyncExitStack | None)
- `dependency_cache` ([T_DependencyCache](typing.md#T-DependencyCache) | None)
- **返回**
- untyped
### _async method_ `run(bot, event, state, stack=None, dependency_cache=None)` {#Matcher-run}
- **参数**
- `bot` ([Bot](adapters/index.md#Bot))
- `event` ([Event](adapters/index.md#Event))
- `state` ([T_State](typing.md#T-State))
- `stack` (AsyncExitStack | None)
- `dependency_cache` ([T_DependencyCache](typing.md#T-DependencyCache) | None)
- **返回**
- untyped
## _var_ `matchers` {#matchers}
- **类型:** untyped
## _class_ `MatcherManager()` {#MatcherManager}
- **说明**
事件响应器管理器
实现了常用字典操作,用于管理事件响应器。
- **参数**
empty
### _method_ `keys()` {#MatcherManager-keys}
- **参数**
empty
- **返回**
- KeysView[int]
### _method_ `values()` {#MatcherManager-values}
- **参数**
empty
- **返回**
- ValuesView[list[type[[Matcher](#Matcher)]]]
### _method_ `items()` {#MatcherManager-items}
- **参数**
empty
- **返回**
- ItemsView[int, list[type[[Matcher](#Matcher)]]]
### _method_ `get(key, default=None)` {#MatcherManager-get}
- **重载**
**1.** `(key) -> list[type[Matcher]] | None`
- **参数**
- `key` (int)
- **返回**
- list[type[[Matcher](#Matcher)]] | None
**2.** `(key, default) -> list[type[Matcher]] | T`
- **参数**
- `key` (int)
- `default` (T)
- **返回**
- list[type[[Matcher](#Matcher)]] | T
### _method_ `pop(key)` {#MatcherManager-pop}
- **参数**
- `key` (int)
- **返回**
- list[type[[Matcher](#Matcher)]]
### _method_ `popitem()` {#MatcherManager-popitem}
- **参数**
empty
- **返回**
- tuple[int, list[type[[Matcher](#Matcher)]]]
### _method_ `clear()` {#MatcherManager-clear}
- **参数**
empty
- **返回**
- None
### _method_ `update(__m)` {#MatcherManager-update}
- **参数**
- `__m` (MutableMapping[int, list[type[[Matcher](#Matcher)]]])
- **返回**
- None
### _method_ `setdefault(key, default)` {#MatcherManager-setdefault}
- **参数**
- `key` (int)
- `default` (list[type[[Matcher](#Matcher)]])
- **返回**
- list[type[[Matcher](#Matcher)]]
### _method_ `set_provider(provider_class)` {#MatcherManager-set-provider}
- **说明:** 设置事件响应器存储器
- **参数**
- `provider_class` (type[[MatcherProvider](#MatcherProvider)]): 事件响应器存储器类
- **返回**
- None
## _abstract class_ `MatcherProvider(matchers)` {#MatcherProvider}
- **说明:** 事件响应器存储器基类
- **参数**
- `matchers` (Mapping[int, list[type[[Matcher](#Matcher)]]]): 当前存储器中已有的事件响应器
## _var_ `DEFAULT_PROVIDER_CLASS` {#DEFAULT-PROVIDER-CLASS}
- **类型:** untyped
- **说明:** 默认存储器类型

View File

@@ -0,0 +1,117 @@
---
sidebar_position: 2
description: nonebot.message 模块
---
# nonebot.message
本模块定义了事件处理主要流程。
NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供了多个插槽以进行事件的预处理等。
## _def_ `event_preprocessor(func)` {#event-preprocessor}
- **说明**
事件预处理。
装饰一个函数,使它在每次接收到事件并分发给各响应器之前执行。
- **参数**
- `func` ([T_EventPreProcessor](typing.md#T-EventPreProcessor))
- **返回**
- [T_EventPreProcessor](typing.md#T-EventPreProcessor)
## _def_ `event_postprocessor(func)` {#event-postprocessor}
- **说明**
事件后处理。
装饰一个函数,使它在每次接收到事件并分发给各响应器之后执行。
- **参数**
- `func` ([T_EventPostProcessor](typing.md#T-EventPostProcessor))
- **返回**
- [T_EventPostProcessor](typing.md#T-EventPostProcessor)
## _def_ `run_preprocessor(func)` {#run-preprocessor}
- **说明**
运行预处理。
装饰一个函数,使它在每次事件响应器运行前执行。
- **参数**
- `func` ([T_RunPreProcessor](typing.md#T-RunPreProcessor))
- **返回**
- [T_RunPreProcessor](typing.md#T-RunPreProcessor)
## _def_ `run_postprocessor(func)` {#run-postprocessor}
- **说明**
运行后处理。
装饰一个函数,使它在每次事件响应器运行后执行。
- **参数**
- `func` ([T_RunPostProcessor](typing.md#T-RunPostProcessor))
- **返回**
- [T_RunPostProcessor](typing.md#T-RunPostProcessor)
## _async def_ `check_and_run_matcher(Matcher, bot, event, state, stack=None, dependency_cache=None)` {#check-and-run-matcher}
- **说明:** 检查并运行事件响应器。
- **参数**
- `Matcher` (type[[Matcher](matcher.md#Matcher)]): 事件响应器
- `bot` ([Bot](adapters/index.md#Bot)): Bot 对象
- `event` ([Event](adapters/index.md#Event)): Event 对象
- `state` ([T_State](typing.md#T-State)): 会话状态
- `stack` (AsyncExitStack | None): 异步上下文栈
- `dependency_cache` ([T_DependencyCache](typing.md#T-DependencyCache) | None): 依赖缓存
- **返回**
- None
## _async def_ `handle_event(bot, event)` {#handle-event}
- **说明:** 处理一个事件。调用该函数以实现分发事件。
- **参数**
- `bot` ([Bot](adapters/index.md#Bot)): Bot 对象
- `event` ([Event](adapters/index.md#Event)): Event 对象
- **返回**
- None
- **用法**
```python
import asyncio
asyncio.create_task(handle_event(bot, event))
```

View File

@@ -0,0 +1,478 @@
---
sidebar_position: 4
description: nonebot.params 模块
---
# nonebot.params
本模块定义了依赖注入的各类参数。
## _def_ `Arg(key=None)` {#Arg}
- **说明:** Arg 参数消息
- **参数**
- `key` (str | None)
- **返回**
- Any
## _def_ `ArgStr(key=None)` {#ArgStr}
- **说明:** Arg 参数消息文本
- **参数**
- `key` (str | None)
- **返回**
- str
## _def_ `Depends(dependency=None, *, use_cache=True, validate=False)` {#Depends}
- **说明:** 子依赖装饰器
- **参数**
- `dependency` ([T_Handler](typing.md#T-Handler) | None): 依赖函数。默认为参数的类型注释。
- `use_cache` (bool): 是否使用缓存。默认为 `True`
- `validate` (bool | FieldInfo): 是否使用 Pydantic 类型校验。默认为 `False`
- **返回**
- Any
- **用法**
```python
def depend_func() -> Any:
return ...
def depend_gen_func():
try:
yield ...
finally:
...
async def handler(
param_name: Any = Depends(depend_func),
gen: Any = Depends(depend_gen_func),
):
...
```
## _class_ `ArgParam(*args, validate=False, **kwargs)` {#ArgParam}
- **说明**
Arg 注入参数
本注入解析事件响应器操作 `got` 所获取的参数。
可以通过 `Arg`、`ArgStr`、`ArgPlainText` 等函数参数 `key` 指定获取的参数,
留空则会根据参数名称获取。
- **参数**
- `*args`
- `validate` (bool)
- `**kwargs` (Any)
## _class_ `BotParam(*args, validate=False, **kwargs)` {#BotParam}
- **说明**
注入参数。
本注入解析所有类型为且仅为 [Bot](adapters/index.md#Bot) 及其子类或 `None` 的参数。
为保证兼容性,本注入还会解析名为 `bot` 且没有类型注解的参数。
- **参数**
- `*args`
- `validate` (bool)
- `**kwargs` (Any)
## _class_ `EventParam(*args, validate=False, **kwargs)` {#EventParam}
- **说明**
注入参数
本注入解析所有类型为且仅为 [Event](adapters/index.md#Event) 及其子类或 `None` 的参数。
为保证兼容性,本注入还会解析名为 `event` 且没有类型注解的参数。
- **参数**
- `*args`
- `validate` (bool)
- `**kwargs` (Any)
## _class_ `StateParam(*args, validate=False, **kwargs)` {#StateParam}
- **说明**
事件处理状态注入参数
本注入解析所有类型为 `T_State` 的参数。
为保证兼容性,本注入还会解析名为 `state` 且没有类型注解的参数。
- **参数**
- `*args`
- `validate` (bool)
- `**kwargs` (Any)
## _class_ `DependParam(*args, validate=False, **kwargs)` {#DependParam}
- **说明**
子依赖注入参数。
本注入解析所有子依赖注入,然后将它们的返回值作为参数值传递给父依赖。
本注入应该具有最高优先级,因此应该在其他参数之前检查。
- **参数**
- `*args`
- `validate` (bool)
- `**kwargs` (Any)
## _def_ `ArgPlainText(key=None)` {#ArgPlainText}
- **说明:** Arg 参数消息纯文本
- **参数**
- `key` (str | None)
- **返回**
- str
## _class_ `DefaultParam(*args, validate=False, **kwargs)` {#DefaultParam}
- **说明**
默认值注入参数
本注入解析所有剩余未能解析且具有默认值的参数。
本注入参数应该具有最低优先级,因此应该在所有其他注入参数之后使用。
- **参数**
- `*args`
- `validate` (bool)
- `**kwargs` (Any)
## _class_ `MatcherParam(*args, validate=False, **kwargs)` {#MatcherParam}
- **说明**
事件响应器实例注入参数
本注入解析所有类型为且仅为 [Matcher](matcher.md#Matcher) 及其子类或 `None` 的参数。
为保证兼容性,本注入还会解析名为 `matcher` 且没有类型注解的参数。
- **参数**
- `*args`
- `validate` (bool)
- `**kwargs` (Any)
## _class_ `ExceptionParam(*args, validate=False, **kwargs)` {#ExceptionParam}
- **说明**
的异常注入参数
本注入解析所有类型为 `Exception` 或 `None` 的参数。
为保证兼容性,本注入还会解析名为 `exception` 且没有类型注解的参数。
- **参数**
- `*args`
- `validate` (bool)
- `**kwargs` (Any)
## _def_ `EventType()` {#EventType}
- **说明:** 类型参数
- **参数**
empty
- **返回**
- str
## _def_ `EventMessage()` {#EventMessage}
- **说明:** 消息参数
- **参数**
empty
- **返回**
- Any
## _def_ `EventPlainText()` {#EventPlainText}
- **说明:** 纯文本消息参数
- **参数**
empty
- **返回**
- str
## _def_ `EventToMe()` {#EventToMe}
- **说明:** `to_me` 参数
- **参数**
empty
- **返回**
- bool
## _def_ `Command()` {#Command}
- **说明:** 消息命令元组
- **参数**
empty
- **返回**
- tuple[str, ...]
## _def_ `RawCommand()` {#RawCommand}
- **说明:** 消息命令文本
- **参数**
empty
- **返回**
- str
## _def_ `CommandArg()` {#CommandArg}
- **说明:** 消息命令参数
- **参数**
empty
- **返回**
- Any
## _def_ `CommandStart()` {#CommandStart}
- **说明:** 消息命令开头
- **参数**
empty
- **返回**
- str
## _def_ `CommandWhitespace()` {#CommandWhitespace}
- **说明:** 消息命令与参数之间的空白
- **参数**
empty
- **返回**
- str
## _def_ `ShellCommandArgs()` {#ShellCommandArgs}
- **说明:** shell 命令解析后的参数字典
- **参数**
empty
- **返回**
- Any
## _def_ `ShellCommandArgv()` {#ShellCommandArgv}
- **说明:** shell 命令原始参数列表
- **参数**
empty
- **返回**
- Any
## _def_ `RegexMatched()` {#RegexMatched}
- **说明:** 正则匹配结果
- **参数**
empty
- **返回**
- Match[str]
## _def_ `RegexStr()` {#RegexStr}
- **说明:** 正则匹配结果文本
- **参数**
empty
- **返回**
- str
## _def_ `RegexGroup()` {#RegexGroup}
- **说明:** 正则匹配结果 group 元组
- **参数**
empty
- **返回**
- tuple[Any, ...]
## _def_ `RegexDict()` {#RegexDict}
- **说明:** 正则匹配结果 group 字典
- **参数**
empty
- **返回**
- dict[str, Any]
## _def_ `Startswith()` {#Startswith}
- **说明:** 响应触发前缀
- **参数**
empty
- **返回**
- str
## _def_ `Endswith()` {#Endswith}
- **说明:** 响应触发后缀
- **参数**
empty
- **返回**
- str
## _def_ `Fullmatch()` {#Fullmatch}
- **说明:** 响应触发完整消息
- **参数**
empty
- **返回**
- str
## _def_ `Keyword()` {#Keyword}
- **说明:** 响应触发关键字
- **参数**
empty
- **返回**
- str
## _def_ `Received(id=None, default=None)` {#Received}
- **说明:** `receive` 事件参数
- **参数**
- `id` (str | None)
- `default` (Any)
- **返回**
- Any
## _def_ `LastReceived(default=None)` {#LastReceived}
- **说明:** `last_receive` 事件参数
- **参数**
- `default` (Any)
- **返回**
- Any

View File

@@ -0,0 +1,208 @@
---
sidebar_position: 6
description: nonebot.permission 模块
---
# nonebot.permission
本模块是 [Matcher.permission](matcher.md#Matcher-permission) 的类型定义。
每个[事件响应器](matcher.md#Matcher)
拥有一个 [Permission](#Permission),其中是 `PermissionChecker` 的集合。
只要有一个 `PermissionChecker` 检查结果为 `True` 时就会继续运行。
## _def_ `USER(*users, perm=None)` {#USER}
- **说明**
匹配当前事件属于指定会话。
如果 `perm` 中仅有 `User` 类型的权限检查函数,则会去除原有检查函数的会话 ID 限制。
- **参数**
- `*users` (str)
- `perm` (Permission | None): 需要同时满足的权限
- `user`: 会话白名单
- **返回**
- untyped
## _class_ `User(users, perm=None)` {#User}
- **说明:** 检查当前事件是否属于指定会话。
- **参数**
- `users` (tuple[str, ...]): 会话 ID 元组
- `perm` (Permission | None): 需同时满足的权限
### _classmethod_ `from_event(event, perm=None)` {#User-from-event}
- **说明**
从事件中获取会话 ID。
如果 `perm` 中仅有 `User` 类型的权限检查函数,则会去除原有的会话 ID 限制。
- **参数**
- `event` ([Event](adapters/index.md#Event)): Event 对象
- `perm` (Permission | None): 需同时满足的权限
- **返回**
- Self
### _classmethod_ `from_permission(*users, perm=None)` {#User-from-permission}
- **说明**
指定会话与权限。
如果 `perm` 中仅有 `User` 类型的权限检查函数,则会去除原有的会话 ID 限制。
- **参数**
- `*users` (str): 会话白名单
- `perm` (Permission | None): 需同时满足的权限
- **返回**
- Self
## _class_ `Permission(*checkers)` {#Permission}
- **说明**
权限类。
当事件传递时,在 [Matcher](matcher.md#Matcher) 运行前进行检查。
- **参数**
- `*checkers` ([T_PermissionChecker](typing.md#T-PermissionChecker) | [Dependent](dependencies/index.md#Dependent)[bool]): PermissionChecker
- **用法**
```python
Permission(async_function) | sync_function
# 等价于
Permission(async_function, sync_function)
```
### _instance-var_ `checkers` {#Permission-checkers}
- **类型:** set[[Dependent](dependencies/index.md#Dependent)[bool]]
- **说明:** 存储 `PermissionChecker`
### _async method_ `__call__(bot, event, stack=None, dependency_cache=None)` {#Permission---call--}
- **说明:** 检查是否满足某个权限。
- **参数**
- `bot` ([Bot](adapters/index.md#Bot)): Bot 对象
- `event` ([Event](adapters/index.md#Event)): Event 对象
- `stack` (AsyncExitStack | None): 异步上下文栈
- `dependency_cache` ([T_DependencyCache](typing.md#T-DependencyCache) | None): 依赖缓存
- **返回**
- bool
## _class_ `Message(<auto>)` {#Message}
- **说明:** 检查是否为消息事件
- **参数**
auto
## _class_ `Notice(<auto>)` {#Notice}
- **说明:** 检查是否为通知事件
- **参数**
auto
## _class_ `Request(<auto>)` {#Request}
- **说明:** 检查是否为请求事件
- **参数**
auto
## _class_ `MetaEvent(<auto>)` {#MetaEvent}
- **说明:** 检查是否为元事件
- **参数**
auto
## _var_ `MESSAGE` {#MESSAGE}
- **类型:** [Permission](#Permission)
- **说明**
匹配任意 `message` 类型事件
仅在需要同时捕获不同类型事件时使用,优先使用 message type 的 Matcher。
## _var_ `NOTICE` {#NOTICE}
- **类型:** [Permission](#Permission)
- **说明**
匹配任意 `notice` 类型事件
仅在需要同时捕获不同类型事件时使用,优先使用 notice type 的 Matcher。
## _var_ `REQUEST` {#REQUEST}
- **类型:** [Permission](#Permission)
- **说明**
匹配任意 `request` 类型事件
仅在需要同时捕获不同类型事件时使用,优先使用 request type 的 Matcher。
## _var_ `METAEVENT` {#METAEVENT}
- **类型:** [Permission](#Permission)
- **说明**
匹配任意 `meta_event` 类型事件
仅在需要同时捕获不同类型事件时使用,优先使用 meta_event type 的 Matcher。
## _class_ `SuperUser(<auto>)` {#SuperUser}
- **说明:** 检查当前事件是否是消息事件且属于超级管理员
- **参数**
auto
## _var_ `SUPERUSER` {#SUPERUSER}
- **类型:** [Permission](#Permission)
- **说明:** 匹配任意超级用户事件

View File

@@ -0,0 +1,3 @@
{
"position": 12
}

View File

@@ -0,0 +1,95 @@
---
sidebar_position: 0
description: nonebot.plugin 模块
---
# nonebot.plugin
本模块为 NoneBot 插件开发提供便携的定义函数。
## 快捷导入
为方便使用,本模块从子模块导入了部分内容,以下内容可以直接通过本模块导入:
- `on` => [`on`](on.md#on)
- `on_metaevent` => [`on_metaevent`](on.md#on-metaevent)
- `on_message` => [`on_message`](on.md#on-message)
- `on_notice` => [`on_notice`](on.md#on-notice)
- `on_request` => [`on_request`](on.md#on-request)
- `on_startswith` => [`on_startswith`](on.md#on-startswith)
- `on_endswith` => [`on_endswith`](on.md#on-endswith)
- `on_fullmatch` => [`on_fullmatch`](on.md#on-fullmatch)
- `on_keyword` => [`on_keyword`](on.md#on-keyword)
- `on_command` => [`on_command`](on.md#on-command)
- `on_shell_command` => [`on_shell_command`](on.md#on-shell-command)
- `on_regex` => [`on_regex`](on.md#on-regex)
- `on_type` => [`on_type`](on.md#on-type)
- `CommandGroup` => [`CommandGroup`](on.md#CommandGroup)
- `Matchergroup` => [`MatcherGroup`](on.md#MatcherGroup)
- `load_plugin` => [`load_plugin`](load.md#load-plugin)
- `load_plugins` => [`load_plugins`](load.md#load-plugins)
- `load_all_plugins` => [`load_all_plugins`](load.md#load-all-plugins)
- `load_from_json` => [`load_from_json`](load.md#load-from-json)
- `load_from_toml` => [`load_from_toml`](load.md#load-from-toml)
- `load_builtin_plugin` =>
[`load_builtin_plugin`](load.md#load-builtin-plugin)
- `load_builtin_plugins` =>
[`load_builtin_plugins`](load.md#load-builtin-plugins)
- `require` => [`require`](load.md#require)
- `PluginMetadata` => [`PluginMetadata`](model.md#PluginMetadata)
## _def_ `get_plugin(name)` {#get-plugin}
- **说明**
获取已经导入的某个插件。
如果为 `load_plugins` 文件夹导入的插件,则为文件(夹)名。
- **参数**
- `name` (str): 插件名,即 [Plugin.name](model.md#Plugin-name)。
- **返回**
- [Plugin](model.md#Plugin) | None
## _def_ `get_plugin_by_module_name(module_name)` {#get-plugin-by-module-name}
- **说明**
通过模块名获取已经导入的某个插件。
如果提供的模块名为某个插件的子模块,同样会返回该插件。
- **参数**
- `module_name` (str): 模块名,即 [Plugin.module_name](model.md#Plugin-module-name)。
- **返回**
- [Plugin](model.md#Plugin) | None
## _def_ `get_loaded_plugins()` {#get-loaded-plugins}
- **说明:** 获取当前已导入的所有插件。
- **参数**
empty
- **返回**
- set[[Plugin](model.md#Plugin)]
## _def_ `get_available_plugin_names()` {#get-available-plugin-names}
- **说明:** 获取当前所有可用的插件名(包含尚未加载的插件)。
- **参数**
empty
- **返回**
- set[str]

View File

@@ -0,0 +1,165 @@
---
sidebar_position: 1
description: nonebot.plugin.load 模块
---
# nonebot.plugin.load
本模块定义插件加载接口。
## _def_ `load_plugin(module_path)` {#load-plugin}
- **说明:** 加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
- **参数**
- `module_path` (str | Path): 插件名称 `path.to.your.plugin` 或插件路径 `pathlib.Path(path/to/your/plugin)`
- **返回**
- [Plugin](model.md#Plugin) | None
## _def_ `load_plugins(*plugin_dir)` {#load-plugins}
- **说明:** 导入文件夹下多个插件,以 `_` 开头的插件不会被导入!
- **参数**
- `*plugin_dir` (str): 文件夹路径
- **返回**
- set[[Plugin](model.md#Plugin)]
## _def_ `load_all_plugins(module_path, plugin_dir)` {#load-all-plugins}
- **说明:** 导入指定列表中的插件以及指定目录下多个插件,以 `_` 开头的插件不会被导入!
- **参数**
- `module_path` (Iterable[str]): 指定插件集合
- `plugin_dir` (Iterable[str]): 指定文件夹路径集合
- **返回**
- set[[Plugin](model.md#Plugin)]
## _def_ `load_from_json(file_path, encoding="utf-8")` {#load-from-json}
- **说明:** 导入指定 json 文件中的 `plugins` 以及 `plugin_dirs` 下多个插件。 以 `_` 开头的插件不会被导入!
- **参数**
- `file_path` (str): 指定 json 文件路径
- `encoding` (str): 指定 json 文件编码
- **返回**
- set[[Plugin](model.md#Plugin)]
- **用法**
```json title=plugins.json
{
"plugins": ["some_plugin"],
"plugin_dirs": ["some_dir"]
}
```
```python
nonebot.load_from_json("plugins.json")
```
## _def_ `load_from_toml(file_path, encoding="utf-8")` {#load-from-toml}
- **说明:** 导入指定 toml 文件 `[tool.nonebot]` 中的 `plugins` 以及 `plugin_dirs` 下多个插件。 以 `_` 开头的插件不会被导入!
- **参数**
- `file_path` (str): 指定 toml 文件路径
- `encoding` (str): 指定 toml 文件编码
- **返回**
- set[[Plugin](model.md#Plugin)]
- **用法**
```toml title=pyproject.toml
[tool.nonebot]
plugins = ["some_plugin"]
plugin_dirs = ["some_dir"]
```
```python
nonebot.load_from_toml("pyproject.toml")
```
## _def_ `load_builtin_plugin(name)` {#load-builtin-plugin}
- **说明:** 导入 NoneBot 内置插件。
- **参数**
- `name` (str): 插件名称
- **返回**
- [Plugin](model.md#Plugin) | None
## _def_ `load_builtin_plugins(*plugins)` {#load-builtin-plugins}
- **说明:** 导入多个 NoneBot 内置插件。
- **参数**
- `*plugins` (str): 插件名称列表
- **返回**
- set[[Plugin](model.md#Plugin)]
## _def_ `require(name)` {#require}
- **说明**
获取一个插件的导出内容。
如果为 `load_plugins` 文件夹导入的插件,则为文件(夹)名。
- **参数**
- `name` (str): 插件名,即 [Plugin.name](model.md#Plugin-name)。
- **返回**
- ModuleType
- **异常**
- RuntimeError: 插件无法加载
## _def_ `inherit_supported_adapters(*names)` {#inherit-supported-adapters}
- **说明**
获取已加载插件的适配器支持状态集合。
如果传入了多个插件名称,返回值会自动取交集。
- **参数**
- `*names` (str): 插件名称列表。
- **返回**
- set[str] | None
- **异常**
- RuntimeError: 插件未加载
- ValueError: 插件缺少元数据

View File

@@ -0,0 +1,128 @@
---
sidebar_position: 5
description: nonebot.plugin.manager 模块
---
# nonebot.plugin.manager
本模块实现插件加载流程。
参考: [import hooks](https://docs.python.org/3/reference/import.html#import-hooks), [PEP302](https://www.python.org/dev/peps/pep-0302/)
## _class_ `PluginManager(plugins=None, search_path=None)` {#PluginManager}
- **说明:** 插件管理器。
- **参数**
- `plugins` (Iterable[str] | None): 独立插件模块名集合。
- `search_path` (Iterable[str] | None): 插件搜索路径(文件夹)。
### _property_ `third_party_plugins` {#PluginManager-third-party-plugins}
- **类型:** set[str]
- **说明:** 返回所有独立插件名称。
### _property_ `searched_plugins` {#PluginManager-searched-plugins}
- **类型:** set[str]
- **说明:** 返回已搜索到的插件名称。
### _property_ `available_plugins` {#PluginManager-available-plugins}
- **类型:** set[str]
- **说明:** 返回当前插件管理器中可用的插件名称。
### _method_ `prepare_plugins()` {#PluginManager-prepare-plugins}
- **说明:** 搜索插件并缓存插件名称。
- **参数**
empty
- **返回**
- set[str]
### _method_ `load_plugin(name)` {#PluginManager-load-plugin}
- **说明**
加载指定插件。
对于独立插件,可以使用完整插件模块名或者插件名称。
- **参数**
- `name` (str): 插件名称。
- **返回**
- [Plugin](model.md#Plugin) | None
### _method_ `load_all_plugins()` {#PluginManager-load-all-plugins}
- **说明:** 加载所有可用插件。
- **参数**
empty
- **返回**
- set[[Plugin](model.md#Plugin)]
## _class_ `PluginFinder(<auto>)` {#PluginFinder}
- **参数**
auto
### _method_ `find_spec(fullname, path, target=None)` {#PluginFinder-find-spec}
- **参数**
- `fullname` (str)
- `path` (Sequence[str] | None)
- `target` (ModuleType | None)
- **返回**
- untyped
## _class_ `PluginLoader(manager, fullname, path)` {#PluginLoader}
- **参数**
- `manager` (PluginManager)
- `fullname` (str)
- `path`
### _method_ `create_module(spec)` {#PluginLoader-create-module}
- **参数**
- `spec`
- **返回**
- ModuleType | None
### _method_ `exec_module(module)` {#PluginLoader-exec-module}
- **参数**
- `module` (ModuleType)
- **返回**
- None

View File

@@ -0,0 +1,132 @@
---
sidebar_position: 3
description: nonebot.plugin.model 模块
---
# nonebot.plugin.model
本模块定义插件相关信息。
## _class_ `PluginMetadata(<auto>)` {#PluginMetadata}
- **说明:** 插件元信息,由插件编写者提供
- **参数**
auto
### _instance-var_ `name` {#PluginMetadata-name}
- **类型:** str
- **说明:** 插件名称
### _instance-var_ `description` {#PluginMetadata-description}
- **类型:** str
- **说明:** 插件功能介绍
### _instance-var_ `usage` {#PluginMetadata-usage}
- **类型:** str
- **说明:** 插件使用方法
### _class-var_ `type` {#PluginMetadata-type}
- **类型:** str | None
- **说明:** 插件类型,用于商店分类
### _class-var_ `homepage` {#PluginMetadata-homepage}
- **类型:** str | None
- **说明:** 插件主页
### _class-var_ `config` {#PluginMetadata-config}
- **类型:** type[BaseModel] | None
- **说明:** 插件配置项
### _class-var_ `supported_adapters` {#PluginMetadata-supported-adapters}
- **类型:** set[str] | None
- **说明**
插件支持的适配器模块路径
格式为 `<module>[:<Adapter>]``~``nonebot.adapters.` 的缩写。
`None` 表示支持**所有适配器**。
### _class-var_ `extra` {#PluginMetadata-extra}
- **类型:** dict[Any, Any]
- **说明:** 插件额外信息,可由插件编写者自由扩展定义
### _method_ `get_supported_adapters()` {#PluginMetadata-get-supported-adapters}
- **说明:** 获取当前已安装的插件支持适配器类列表
- **参数**
empty
- **返回**
- set[type[[Adapter](../adapters/index.md#Adapter)]] | None
## _class_ `Plugin(<auto>)` {#Plugin}
- **说明:** 存储插件信息
- **参数**
auto
### _instance-var_ `name` {#Plugin-name}
- **类型:** str
- **说明:** 插件索引标识NoneBot 使用 文件/文件夹 名称作为标识符
### _instance-var_ `module` {#Plugin-module}
- **类型:** ModuleType
- **说明:** 插件模块对象
### _instance-var_ `module_name` {#Plugin-module-name}
- **类型:** str
- **说明:** 点分割模块路径
### _instance-var_ `manager` {#Plugin-manager}
- **类型:** [PluginManager](manager.md#PluginManager)
- **说明:** 导入该插件的插件管理器
### _class-var_ `matcher` {#Plugin-matcher}
- **类型:** set[type[[Matcher](../matcher.md#Matcher)]]
- **说明:** 插件加载时定义的 `Matcher`
### _class-var_ `parent_plugin` {#Plugin-parent-plugin}
- **类型:** Plugin | None
- **说明:** 父插件
### _class-var_ `sub_plugins` {#Plugin-sub-plugins}
- **类型:** set[Plugin]
- **说明:** 子插件集合

View File

@@ -0,0 +1,948 @@
---
sidebar_position: 2
description: nonebot.plugin.on 模块
---
# nonebot.plugin.on
本模块定义事件响应器便携定义函数。
## _def_ `store_matcher(matcher)` {#store-matcher}
- **说明:** 存储一个事件响应器到插件。
- **参数**
- `matcher` (type[[Matcher](../matcher.md#Matcher)]): 事件响应器
- **返回**
- None
## _def_ `get_matcher_plugin(depth=...)` {#get-matcher-plugin}
- **说明**
获取事件响应器定义所在插件。
**Deprecated**, 请使用 [get_matcher_source](#get-matcher-source) 获取信息。
- **参数**
- `depth` (int): 调用栈深度
- **返回**
- [Plugin](model.md#Plugin) | None
## _def_ `get_matcher_module(depth=...)` {#get-matcher-module}
- **说明**
获取事件响应器定义所在模块。
**Deprecated**, 请使用 [get_matcher_source](#get-matcher-source) 获取信息。
- **参数**
- `depth` (int): 调用栈深度
- **返回**
- ModuleType | None
## _def_ `get_matcher_source(depth=...)` {#get-matcher-source}
- **说明:** 获取事件响应器定义所在源码信息。
- **参数**
- `depth` (int): 调用栈深度
- **返回**
- MatcherSource | None
## _def_ `on(type="", rule=..., permission=..., *, handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on}
- **说明:** 注册一个基础事件响应器,可自定义类型。
- **参数**
- `type` (str): 事件响应器类型
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_metaevent(rule=..., permission=..., *, handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-metaevent}
- **说明:** 注册一个元事件响应器。
- **参数**
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_message(rule=..., permission=..., *, handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-message}
- **说明:** 注册一个消息事件响应器。
- **参数**
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_notice(rule=..., permission=..., *, handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-notice}
- **说明:** 注册一个通知事件响应器。
- **参数**
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_request(rule=..., permission=..., *, handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-request}
- **说明:** 注册一个请求事件响应器。
- **参数**
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_startswith(msg, rule=..., ignorecase=..., *, permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-startswith}
- **说明:** 注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。
- **参数**
- `msg` (str | tuple[str, ...]): 指定消息开头内容
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `ignorecase` (bool): 是否忽略大小写
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_endswith(msg, rule=..., ignorecase=..., *, permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-endswith}
- **说明:** 注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。
- **参数**
- `msg` (str | tuple[str, ...]): 指定消息结尾内容
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `ignorecase` (bool): 是否忽略大小写
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_fullmatch(msg, rule=..., ignorecase=..., *, permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-fullmatch}
- **说明:** 注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。
- **参数**
- `msg` (str | tuple[str, ...]): 指定消息全匹配内容
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `ignorecase` (bool): 是否忽略大小写
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_keyword(keywords, rule=..., *, permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-keyword}
- **说明:** 注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。
- **参数**
- `keywords` (set[str]): 关键词列表
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_command(cmd, rule=..., aliases=..., force_whitespace=..., *, permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-command}
- **说明**
注册一个消息事件响应器,并且当消息以指定命令开头时响应。
命令匹配规则参考: `命令形式匹配 <rule.md#command-command>`\_
- **参数**
- `cmd` (str | tuple[str, ...]): 指定命令内容
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `aliases` (set[str | tuple[str, ...]] | None): 命令别名
- `force_whitespace` (str | bool | None): 是否强制命令后必须有指定空白符
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_shell_command(cmd, rule=..., aliases=..., parser=..., *, permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-shell-command}
- **说明**
注册一个支持 `shell_like` 解析参数的命令消息事件响应器。
与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。
可以通过 [ShellCommandArgv](../params.md#ShellCommandArgv) 获取原始参数列表,
通过 [ShellCommandArgs](../params.md#ShellCommandArgs) 获取解析后的参数字典。
- **参数**
- `cmd` (str | tuple[str, ...]): 指定命令内容
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `aliases` (set[str | tuple[str, ...]] | None): 命令别名
- `parser` ([ArgumentParser](../rule.md#ArgumentParser) | None): `nonebot.rule.ArgumentParser` 对象
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_regex(pattern, flags=..., rule=..., *, permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-regex}
- **说明**
注册一个消息事件响应器,并且当消息匹配正则表达式时响应。
命令匹配规则参考: `正则匹配 <rule.md#regex-regex-flags-0>`\_
- **参数**
- `pattern` (str): 正则表达式
- `flags` (int | re.RegexFlag): 正则匹配标志
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _def_ `on_type(types, rule=..., *, permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#on-type}
- **说明:** 注册一个事件响应器,并且当事件为指定类型时响应。
- **参数**
- `types` (type[[Event](../adapters/index.md#Event)] | tuple[type[[Event](../adapters/index.md#Event)], ...]): 事件类型
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _class_ `CommandGroup(cmd, prefix_aliases=..., *, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#CommandGroup}
- **参数**
- `cmd` (str | tuple[str, ...])
- `prefix_aliases` (bool)
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None)
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None)
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None)
- `temp` (bool)
- `expire_time` (datetime | timedelta | None)
- `priority` (int)
- `block` (bool)
- `state` ([T_State](../typing.md#T-State) | None)
### _method_ `command(cmd, *, rule=..., aliases=..., force_whitespace=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#CommandGroup-command}
- **说明:** 注册一个新的命令。新参数将会覆盖命令组默认值
- **参数**
- `cmd` (str | tuple[str, ...]): 指定命令内容
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `aliases` (set[str | tuple[str, ...]] | None): 命令别名
- `force_whitespace` (str | bool | None): 是否强制命令后必须有指定空白符
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `shell_command(cmd, *, rule=..., aliases=..., parser=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#CommandGroup-shell-command}
- **说明:** 注册一个新的 `shell_like` 命令。新参数将会覆盖命令组默认值
- **参数**
- `cmd` (str | tuple[str, ...]): 指定命令内容
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `aliases` (set[str | tuple[str, ...]] | None): 命令别名
- `parser` ([ArgumentParser](../rule.md#ArgumentParser) | None): `nonebot.rule.ArgumentParser` 对象
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
## _class_ `MatcherGroup(*, type=..., rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup}
- **参数**
- `type` (str)
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None)
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None)
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None)
- `temp` (bool)
- `expire_time` (datetime | timedelta | None)
- `priority` (int)
- `block` (bool)
- `state` ([T_State](../typing.md#T-State) | None)
### _method_ `on(*, type=..., rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on}
- **说明:** 注册一个基础事件响应器,可自定义类型。
- **参数**
- `type` (str): 事件响应器类型
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_metaevent(*, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-metaevent}
- **说明:** 注册一个元事件响应器。
- **参数**
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_message(*, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-message}
- **说明:** 注册一个消息事件响应器。
- **参数**
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_notice(*, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-notice}
- **说明:** 注册一个通知事件响应器。
- **参数**
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_request(*, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-request}
- **说明:** 注册一个请求事件响应器。
- **参数**
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_startswith(msg, *, ignorecase=..., rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-startswith}
- **说明:** 注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。
- **参数**
- `msg` (str | tuple[str, ...]): 指定消息开头内容
- `ignorecase` (bool): 是否忽略大小写
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_endswith(msg, *, ignorecase=..., rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-endswith}
- **说明:** 注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。
- **参数**
- `msg` (str | tuple[str, ...]): 指定消息结尾内容
- `ignorecase` (bool): 是否忽略大小写
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_fullmatch(msg, *, ignorecase=..., rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-fullmatch}
- **说明:** 注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。
- **参数**
- `msg` (str | tuple[str, ...]): 指定消息全匹配内容
- `ignorecase` (bool): 是否忽略大小写
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_keyword(keywords, *, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-keyword}
- **说明:** 注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。
- **参数**
- `keywords` (set[str]): 关键词列表
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_command(cmd, aliases=..., force_whitespace=..., *, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-command}
- **说明**
注册一个消息事件响应器,并且当消息以指定命令开头时响应。
命令匹配规则参考: `命令形式匹配 <rule.md#command-command>`\_
- **参数**
- `cmd` (str | tuple[str, ...]): 指定命令内容
- `aliases` (set[str | tuple[str, ...]] | None): 命令别名
- `force_whitespace` (str | bool | None): 是否强制命令后必须有指定空白符
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_shell_command(cmd, aliases=..., parser=..., *, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-shell-command}
- **说明**
注册一个支持 `shell_like` 解析参数的命令消息事件响应器。
与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。
可以通过 [ShellCommandArgv](../params.md#ShellCommandArgv) 获取原始参数列表,
通过 [ShellCommandArgs](../params.md#ShellCommandArgs) 获取解析后的参数字典。
- **参数**
- `cmd` (str | tuple[str, ...]): 指定命令内容
- `aliases` (set[str | tuple[str, ...]] | None): 命令别名
- `parser` ([ArgumentParser](../rule.md#ArgumentParser) | None): `nonebot.rule.ArgumentParser` 对象
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_regex(pattern, flags=..., *, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-regex}
- **说明**
注册一个消息事件响应器,并且当消息匹配正则表达式时响应。
命令匹配规则参考: `正则匹配 <rule.md#regex-regex-flags-0>`\_
- **参数**
- `pattern` (str): 正则表达式
- `flags` (int | re.RegexFlag): 正则匹配标志
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]
### _method_ `on_type(types, *, rule=..., permission=..., handlers=..., temp=..., expire_time=..., priority=..., block=..., state=...)` {#MatcherGroup-on-type}
- **说明:** 注册一个事件响应器,并且当事件为指定类型时响应。
- **参数**
- `types` (type[[Event](../adapters/index.md#Event)] | tuple[type[[Event](../adapters/index.md#Event)]]): 事件类型
- `rule` ([Rule](../rule.md#Rule) | [T_RuleChecker](../typing.md#T-RuleChecker) | None): 事件响应规则
- `permission` ([Permission](../permission.md#Permission) | [T_PermissionChecker](../typing.md#T-PermissionChecker) | None): 事件响应权限
- `handlers` (list[[T_Handler](../typing.md#T-Handler) | [Dependent](../dependencies/index.md#Dependent)] | None): 事件处理函数列表
- `temp` (bool): 是否为临时事件响应器(仅执行一次)
- `expire_time` (datetime | timedelta | None): 事件响应器最终有效时间点,过时即被删除
- `priority` (int): 事件响应器优先级
- `block` (bool): 是否阻止事件向更低优先级传递
- `state` ([T_State](../typing.md#T-State) | None): 默认 state
- **返回**
- type[[Matcher](../matcher.md#Matcher)]

View File

@@ -0,0 +1,402 @@
---
sidebar_position: 5
description: nonebot.rule 模块
---
# nonebot.rule
本模块是 [Matcher.rule](matcher.md#Matcher-rule) 的类型定义。
每个[事件响应器](matcher.md#Matcher)拥有一个
[Rule](#Rule),其中是 `RuleChecker` 的集合。
只有当所有 `RuleChecker` 检查结果为 `True` 时继续运行。
## _class_ `Rule(*checkers)` {#Rule}
- **说明**
规则类。
当事件传递时,在 [Matcher](matcher.md#Matcher) 运行前进行检查。
- **参数**
- `*checkers` ([T_RuleChecker](typing.md#T-RuleChecker) | [Dependent](dependencies/index.md#Dependent)[bool]): RuleChecker
- **用法**
```python
Rule(async_function) & sync_function
# 等价于
Rule(async_function, sync_function)
```
### _instance-var_ `checkers` {#Rule-checkers}
- **类型:** set[[Dependent](dependencies/index.md#Dependent)[bool]]
- **说明:** 存储 `RuleChecker`
### _async method_ `__call__(bot, event, state, stack=None, dependency_cache=None)` {#Rule---call--}
- **说明:** 检查是否符合所有规则
- **参数**
- `bot` ([Bot](adapters/index.md#Bot)): Bot 对象
- `event` ([Event](adapters/index.md#Event)): Event 对象
- `state` ([T_State](typing.md#T-State)): 当前 State
- `stack` (AsyncExitStack | None): 异步上下文栈
- `dependency_cache` ([T_DependencyCache](typing.md#T-DependencyCache) | None): 依赖缓存
- **返回**
- bool
## _class_ `CMD_RESULT(<auto>)` {#CMD-RESULT}
- **参数**
auto
## _class_ `TRIE_VALUE(<auto>)` {#TRIE-VALUE}
- **说明:** TRIE_VALUE(command_start, command)
- **参数**
auto
## _class_ `StartswithRule(msg, ignorecase=False)` {#StartswithRule}
- **说明:** 检查消息纯文本是否以指定字符串开头。
- **参数**
- `msg` (tuple[str, ...]): 指定消息开头字符串元组
- `ignorecase` (bool): 是否忽略大小写
## _def_ `startswith(msg, ignorecase=False)` {#startswith}
- **说明:** 匹配消息纯文本开头。
- **参数**
- `msg` (str | tuple[str, ...]): 指定消息开头字符串元组
- `ignorecase` (bool): 是否忽略大小写
- **返回**
- [Rule](#Rule)
## _class_ `EndswithRule(msg, ignorecase=False)` {#EndswithRule}
- **说明:** 检查消息纯文本是否以指定字符串结尾。
- **参数**
- `msg` (tuple[str, ...]): 指定消息结尾字符串元组
- `ignorecase` (bool): 是否忽略大小写
## _def_ `endswith(msg, ignorecase=False)` {#endswith}
- **说明:** 匹配消息纯文本结尾。
- **参数**
- `msg` (str | tuple[str, ...]): 指定消息开头字符串元组
- `ignorecase` (bool): 是否忽略大小写
- **返回**
- [Rule](#Rule)
## _class_ `FullmatchRule(msg, ignorecase=False)` {#FullmatchRule}
- **说明:** 检查消息纯文本是否与指定字符串全匹配。
- **参数**
- `msg` (tuple[str, ...]): 指定消息全匹配字符串元组
- `ignorecase` (bool): 是否忽略大小写
## _def_ `fullmatch(msg, ignorecase=False)` {#fullmatch}
- **说明:** 完全匹配消息。
- **参数**
- `msg` (str | tuple[str, ...]): 指定消息全匹配字符串元组
- `ignorecase` (bool): 是否忽略大小写
- **返回**
- [Rule](#Rule)
## _class_ `KeywordsRule(*keywords)` {#KeywordsRule}
- **说明:** 检查消息纯文本是否包含指定关键字。
- **参数**
- `*keywords` (str): 指定关键字元组
## _def_ `keyword(*keywords)` {#keyword}
- **说明:** 匹配消息纯文本关键词。
- **参数**
- `*keywords` (str): 指定关键字元组
- **返回**
- [Rule](#Rule)
## _class_ `CommandRule(cmds, force_whitespace=None)` {#CommandRule}
- **说明:** 检查消息是否为指定命令。
- **参数**
- `cmds` (list[tuple[str, ...]]): 指定命令元组列表
- `force_whitespace` (str | bool | None): 是否强制命令后必须有指定空白符
## _def_ `command(*cmds, force_whitespace=None)` {#command}
- **说明**
匹配消息命令。
根据配置里提供的 [`command_start`](config.md#Config-command-start),
[`command_sep`](config.md#Config-command-sep) 判断消息是否为命令。
可以通过 [Command](params.md#Command) 获取匹配成功的命令(例: `("test",)`
通过 [RawCommand](params.md#RawCommand) 获取匹配成功的原始命令文本(例: `"/test"`
通过 [CommandArg](params.md#CommandArg) 获取匹配成功的命令参数。
- **参数**
- `*cmds` (str | tuple[str, ...]): 命令文本或命令元组
- `force_whitespace` (str | bool | None): 是否强制命令后必须有指定空白符
- **返回**
- [Rule](#Rule)
- **用法**
使用默认 `command_start`, `command_sep` 配置情况下:
命令 `("test",)` 可以匹配: `/test` 开头的消息
命令 `("test", "sub")` 可以匹配: `/test.sub` 开头的消息
:::tip 提示
命令内容与后续消息间无需空格!
:::
## _class_ `ArgumentParser(<auto>)` {#ArgumentParser}
- **说明**
`shell_like` 命令参数解析器,解析出错时不会退出程序。
支持 [Message](adapters/index.md#Message) 富文本解析。
- **参数**
auto
- **用法**
用法与 `argparse.ArgumentParser` 相同,
参考文档: [argparse](https://docs.python.org/3/library/argparse.html)
### _method_ `parse_known_args(args=None, namespace=None)` {#ArgumentParser-parse-known-args}
- **重载**
**1.** `(args=None, namespace=None) -> tuple[Namespace, list[str | MessageSegment]]`
- **参数**
- `args` (Sequence[str | [MessageSegment](adapters/index.md#MessageSegment)] | None)
- `namespace` (None)
- **返回**
- tuple[Namespace, list[str | [MessageSegment](adapters/index.md#MessageSegment)]]
**2.** `(args, namespace) -> tuple[T, list[str | MessageSegment]]`
- **参数**
- `args` (Sequence[str | [MessageSegment](adapters/index.md#MessageSegment)] | None)
- `namespace` (T)
- **返回**
- tuple[T, list[str | [MessageSegment](adapters/index.md#MessageSegment)]]
**3.** `(*, namespace) -> tuple[T, list[str | MessageSegment]]`
- **参数**
- `namespace` (T)
- **返回**
- tuple[T, list[str | [MessageSegment](adapters/index.md#MessageSegment)]]
## _class_ `ShellCommandRule(cmds, parser)` {#ShellCommandRule}
- **说明:** 检查消息是否为指定 shell 命令。
- **参数**
- `cmds` (list[tuple[str, ...]]): 指定命令元组列表
- `parser` (ArgumentParser | None): 可选参数解析器
## _def_ `shell_command(*cmds, parser=None)` {#shell-command}
- **说明**
匹配 `shell_like` 形式的消息命令。
根据配置里提供的 [`command_start`](config.md#Config-command-start),
[`command_sep`](config.md#Config-command-sep) 判断消息是否为命令。
可以通过 [Command](params.md#Command) 获取匹配成功的命令
(例: `("test",)`
通过 [RawCommand](params.md#RawCommand) 获取匹配成功的原始命令文本
(例: `"/test"`
通过 [ShellCommandArgv](params.md#ShellCommandArgv) 获取解析前的参数列表
(例: `["arg", "-h"]`
通过 [ShellCommandArgs](params.md#ShellCommandArgs) 获取解析后的参数字典
(例: `{"arg": "arg", "h": True}`)。
:::caution 警告
如果参数解析失败,则通过 [ShellCommandArgs](params.md#ShellCommandArgs)
获取的将是 [ParserExit](exception.md#ParserExit) 异常。
:::
- **参数**
- `*cmds` (str | tuple[str, ...]): 命令文本或命令元组
- `parser` (ArgumentParser | None): [ArgumentParser](#ArgumentParser) 对象
- **返回**
- [Rule](#Rule)
- **用法**
使用默认 `command_start`, `command_sep` 配置,更多示例参考
[argparse](https://docs.python.org/3/library/argparse.html) 标准库文档。
```python
from nonebot.rule import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-a", action="store_true")
rule = shell_command("ls", parser=parser)
```
:::tip 提示
命令内容与后续消息间无需空格!
:::
## _class_ `RegexRule(regex, flags=0)` {#RegexRule}
- **说明:** 检查消息字符串是否符合指定正则表达式。
- **参数**
- `regex` (str): 正则表达式
- `flags` (int): 正则表达式标记
## _def_ `regex(regex, flags=0)` {#regex}
- **说明**
匹配符合正则表达式的消息字符串。
可以通过 [RegexStr](params.md#RegexStr) 获取匹配成功的字符串,
通过 [RegexGroup](params.md#RegexGroup) 获取匹配成功的 group 元组,
通过 [RegexDict](params.md#RegexDict) 获取匹配成功的 group 字典。
- **参数**
- `regex` (str): 正则表达式
- `flags` (int | re.RegexFlag): 正则表达式标记
- **返回**
- [Rule](#Rule)
:::tip 提示
正则表达式匹配使用 search 而非 match如需从头匹配请使用 `r"^xxx"` 来确保匹配开头
:::
:::tip 提示
正则表达式匹配使用 `EventMessage` 的 `str` 字符串,
而非 `EventMessage` 的 `PlainText` 纯文本字符串
:::
## _class_ `ToMeRule(<auto>)` {#ToMeRule}
- **说明:** 检查事件是否与机器人有关。
- **参数**
auto
## _def_ `to_me()` {#to-me}
- **说明:** 匹配与机器人有关的事件。
- **参数**
empty
- **返回**
- [Rule](#Rule)
## _class_ `IsTypeRule(*types)` {#IsTypeRule}
- **说明:** 检查事件类型是否为指定类型。
- **参数**
- `*types` (type[[Event](adapters/index.md#Event)])
## _def_ `is_type(*types)` {#is-type}
- **说明:** 匹配事件类型。
- **参数**
- `*types` (type[[Event](adapters/index.md#Event)]): 事件类型
- **返回**
- [Rule](#Rule)

View File

@@ -0,0 +1,215 @@
---
sidebar_position: 11
description: nonebot.typing 模块
---
# nonebot.typing
本模块定义了 NoneBot 模块中共享的一些类型。
使用 Python 的 Type Hint 语法,
参考 [`PEP 484`](https://www.python.org/dev/peps/pep-0484/),
[`PEP 526`](https://www.python.org/dev/peps/pep-0526/) 和
[`typing`](https://docs.python.org/3/library/typing.html)。
## _def_ `overrides(InterfaceClass)` {#overrides}
- **说明:** 标记一个方法为父类 interface 的 implement
- **参数**
- `InterfaceClass` (object)
- **返回**
- untyped
## _var_ `T_State` {#T-State}
- **类型:** dict[Any, Any]
- **说明:** 事件处理状态 State 类型
## _var_ `T_BotConnectionHook` {#T-BotConnectionHook}
- **类型:** \_DependentCallable[Any]
- **说明**
Bot 连接建立时钩子函数
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- DefaultParam: 带有默认值的参数
## _var_ `T_BotDisconnectionHook` {#T-BotDisconnectionHook}
- **类型:** \_DependentCallable[Any]
- **说明**
Bot 连接断开时钩子函数
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- DefaultParam: 带有默认值的参数
## _var_ `T_CallingAPIHook` {#T-CallingAPIHook}
- **类型:** ([Bot](adapters/index.md#Bot), str, dict[str, Any]) -> Awaitable[Any]
- **说明:** `bot.call_api` 钩子函数
## _var_ `T_CalledAPIHook` {#T-CalledAPIHook}
- **类型:** ([Bot](adapters/index.md#Bot), Exception | None, str, dict[str, Any], Any) -> Awaitable[Any]
- **说明:** `bot.call_api` 后执行的函数,参数分别为 bot, exception, api, data, result
## _var_ `T_EventPreProcessor` {#T-EventPreProcessor}
- **类型:** \_DependentCallable[Any]
- **说明**
事件预处理函数 EventPreProcessor 类型
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- EventParam: Event 对象
- StateParam: State 对象
- DefaultParam: 带有默认值的参数
## _var_ `T_EventPostProcessor` {#T-EventPostProcessor}
- **类型:** \_DependentCallable[Any]
- **说明**
事件后处理函数 EventPostProcessor 类型
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- EventParam: Event 对象
- StateParam: State 对象
- DefaultParam: 带有默认值的参数
## _var_ `T_RunPreProcessor` {#T-RunPreProcessor}
- **类型:** \_DependentCallable[Any]
- **说明**
事件响应器运行前预处理函数 RunPreProcessor 类型
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- EventParam: Event 对象
- StateParam: State 对象
- MatcherParam: Matcher 对象
- DefaultParam: 带有默认值的参数
## _var_ `T_RunPostProcessor` {#T-RunPostProcessor}
- **类型:** \_DependentCallable[Any]
- **说明**
事件响应器运行后后处理函数 RunPostProcessor 类型
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- EventParam: Event 对象
- StateParam: State 对象
- MatcherParam: Matcher 对象
- ExceptionParam: 异常对象(可能为 None
- DefaultParam: 带有默认值的参数
## _var_ `T_RuleChecker` {#T-RuleChecker}
- **类型:** \_DependentCallable[bool]
- **说明**
RuleChecker 即判断是否响应事件的处理函数。
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- EventParam: Event 对象
- StateParam: State 对象
- DefaultParam: 带有默认值的参数
## _var_ `T_PermissionChecker` {#T-PermissionChecker}
- **类型:** \_DependentCallable[bool]
- **说明**
PermissionChecker 即判断事件是否满足权限的处理函数。
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- EventParam: Event 对象
- DefaultParam: 带有默认值的参数
## _var_ `T_Handler` {#T-Handler}
- **类型:** \_DependentCallable[Any]
- **说明:** Handler 处理函数。
## _var_ `T_TypeUpdater` {#T-TypeUpdater}
- **类型:** \_DependentCallable[str]
- **说明**
TypeUpdater 在 Matcher.pause, Matcher.reject 时被运行,用于更新响应的事件类型。 默认会更新为 `message`
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- EventParam: Event 对象
- StateParam: State 对象
- MatcherParam: Matcher 对象
- DefaultParam: 带有默认值的参数
## _var_ `T_PermissionUpdater` {#T-PermissionUpdater}
- **类型:** \_DependentCallable[[Permission](permission.md#Permission)]
- **说明**
PermissionUpdater 在 Matcher.pause, Matcher.reject 时被运行,用于更新会话对象权限。 默认会更新为当前事件的触发对象。
依赖参数:
- DependParam: 子依赖参数
- BotParam: Bot 对象
- EventParam: Event 对象
- StateParam: State 对象
- MatcherParam: Matcher 对象
- DefaultParam: 带有默认值的参数
## _var_ `T_DependencyCache` {#T-DependencyCache}
- **类型:** dict[\_DependentCallable[Any], Task[Any]]
- **说明:** 依赖缓存, 用于存储依赖函数的返回值

View File

@@ -0,0 +1,238 @@
---
sidebar_position: 8
description: nonebot.utils 模块
---
# nonebot.utils
本模块包含了 NoneBot 的一些工具函数
## _def_ `escape_tag(s)` {#escape-tag}
- **说明**
用于记录带颜色日志时转义 `<tag>` 类型特殊标签
参考: [loguru color 标签](https://loguru.readthedocs.io/en/stable/api/logger.html#color)
- **参数**
- `s` (str): 需要转义的字符串
- **返回**
- str
## _def_ `generic_check_issubclass(cls, class_or_tuple)` {#generic-check-issubclass}
- **说明**
检查 cls 是否是 class_or_tuple 中的一个类型子类。
特别的:
- 如果 cls 是 `typing.Union``types.UnionType` 类型,
则会检查其中的所有类型是否是 class_or_tuple 中一个类型的子类或 None。
- 如果 cls 是 `typing.TypeVar` 类型,
则会检查其 `__bound__``__constraints__`
是否是 class_or_tuple 中一个类型的子类或 None。
- **参数**
- `cls` (Any)
- `class_or_tuple` (type[Any] | tuple[type[Any], ...])
- **返回**
- bool
## _def_ `is_coroutine_callable(call)` {#is-coroutine-callable}
- **说明:** 检查 call 是否是一个 callable 协程函数
- **参数**
- `call` ((...) -> Any)
- **返回**
- bool
## _def_ `is_gen_callable(call)` {#is-gen-callable}
- **说明:** 检查 call 是否是一个生成器函数
- **参数**
- `call` ((...) -> Any)
- **返回**
- bool
## _def_ `is_async_gen_callable(call)` {#is-async-gen-callable}
- **说明:** 检查 call 是否是一个异步生成器函数
- **参数**
- `call` ((...) -> Any)
- **返回**
- bool
## _def_ `run_sync(call)` {#run-sync}
- **说明:** 一个用于包装 sync function 为 async function 的装饰器
- **参数**
- `call` ((P) -> R): 被装饰的同步函数
- **返回**
- (P) -> Coroutine[None, None, R]
## _def_ `run_sync_ctx_manager(cm)` {#run-sync-ctx-manager}
- **说明:** 一个用于包装 sync context manager 为 async context manager 的执行函数
- **参数**
- `cm` (ContextManager[T])
- **返回**
- AsyncGenerator[T, None]
## _async def_ `run_coro_with_catch(coro, exc, return_on_err=None)` {#run-coro-with-catch}
- **说明:** 运行协程并当遇到指定异常时返回指定值。
- **重载**
**1.** `(coro, exc, return_on_err=None) -> T | None`
- **参数**
- `coro` (Coroutine[Any, Any, T])
- `exc` (tuple[type[Exception], ...])
- `return_on_err` (None)
- **返回**
- T | None
**2.** `(coro, exc, return_on_err) -> T | R`
- **参数**
- `coro` (Coroutine[Any, Any, T])
- `exc` (tuple[type[Exception], ...])
- `return_on_err` (R)
- **返回**
- T | R
- **参数**
- `coro`: 要运行的协程
- `exc`: 要捕获的异常
- `return_on_err`: 当发生异常时返回的值
- **返回**
协程的返回值或发生异常时的指定值
## _def_ `get_name(obj)` {#get-name}
- **说明:** 获取对象的名称
- **参数**
- `obj` (Any)
- **返回**
- str
## _def_ `path_to_module_name(path)` {#path-to-module-name}
- **说明:** 转换路径为模块名
- **参数**
- `path` (Path)
- **返回**
- str
## _def_ `resolve_dot_notation(obj_str, default_attr, default_prefix=None)` {#resolve-dot-notation}
- **说明:** 解析并导入点分表示法的对象
- **参数**
- `obj_str` (str)
- `default_attr` (str)
- `default_prefix` (str | None)
- **返回**
- Any
## _class_ `classproperty(func)` {#classproperty}
- **说明:** 类属性装饰器
- **参数**
- `func` ((Any) -> T)
## _class_ `DataclassEncoder(<auto>)` {#DataclassEncoder}
- **说明:** 可以序列化 [Message](adapters/index.md#Message)(List[Dataclass]) 的 `JSONEncoder`
- **参数**
auto
### _method_ `default(o)` {#DataclassEncoder-default}
- **参数**
- `o`
- **返回**
- untyped
## _def_ `logger_wrapper(logger_name)` {#logger-wrapper}
- **说明:** 用于打印 adapter 的日志。
- **参数**
- `logger_name` (str): adapter 的名称
- **返回**
- untyped: 日志记录函数
日志记录函数的参数:
- level: 日志等级
- message: 日志信息
- exception: 异常信息

View File

@@ -0,0 +1,131 @@
---
sidebar_position: 4
description: 使用平台接口,完成更多功能
options:
menu:
- category: appendices
weight: 50
---
# 使用平台接口
import Messenger from "@/components/Messenger";
在 NoneBot 中,除了使用事件响应器操作发送文本消息外,我们还可以直接通过使用协议适配器提供的方法来使用平台特定的接口,完成发送特殊消息、获取信息等其他平台提供的功能。同时,在部分无法使用事件响应器的情况中,例如[定时任务](../best-practice/scheduler.md),我们也可以使用平台接口来完成需要的功能。
## 发送平台特殊消息
在之前的章节中,我们介绍了如何向用户发送文本消息以及[如何处理平台消息](../tutorial/message.md),现在我们来向用户发送平台特殊消息。
:::caution 注意
在以下的示例中,我们将使用 `Console` 协议适配器来演示如何发送平台消息。在实际使用中,你需要确保你使用的**消息序列类型**与你所要发送的**平台类型**一致。
:::
```python {4,7-17} title=weather/__init__.py
import inspect
from nonebot.adapters.console import MessageSegment
@weather.got("location", prompt=MessageSegment.emoji("question") + "请输入地名")
async def got_location(location: str = ArgPlainText()):
result = await weather.send(
MessageSegment.markdown(
inspect.cleandoc(
f"""
# {location}
- 今天
⛅ 多云 20℃~24℃
"""
)
)
)
```
<Messenger
msgs={[
{ position: "right", msg: "/天气" },
{ position: "left", msg: "❓请输入地名" },
{ position: "right", msg: "北京" },
{
position: "left",
monospace: true,
msg: "┏━━━━━━━━━━━━━━━━┓\n┃ 北京 ┃\n┗━━━━━━━━━━━━━━━━┛\n• 今天\n⛅ 多云 20℃~24℃",
},
]}
/>
在上面的示例中,我们使用了 `Console` 协议适配器提供的 `MessageSegment` 类来发送平台特定的消息 `emoji` 和 `markdown`。这两种消息可以显示在终端中,但是无法在其他平台上使用。在事件响应器操作中,我们可以使用 `str`、消息序列、消息段、消息模板四种类型来发送消息,但其中只有 `str` 和[纯文本形式的消息模板类型](../tutorial/message.md#使用消息模板)消息可以在所有平台上使用。
`send` 事件响应器操作实际上是由协议适配器通过调用平台 API 来实现的,通常会将 API 调用的结果作为返回值返回。
## 调用平台 API
在 NoneBot 中,我们可以通过 `Bot` 对象来调用协议适配器支持的平台 API来完成更多的功能。
### 获取 Bot
在调用平台 API 之前,我们首先要获得 Bot 对象。有两种方式可以获得 Bot 对象。
在事件处理流程的上下文中,我们可以直接使用依赖注入 Bot 来获取:
```python {1,4} title=weather/__init__.py
from nonebot.adapters import Bot
@weather.got("location", prompt="请输入地名")
async def got_location(bot: Bot, location: str = ArgPlainText()):
...
```
依赖注入会确保你获得的 Bot 对象与类型注解的 Bot 类型一致。也就是说,如果你使用的是 Bot 基类,将会允许任何平台的 Bot 对象;如果你使用的是平台特定的 Bot 类型,将会只允许该平台的 Bot 对象,其他类型的 Bot 将会跳过这个事件处理函数。更多详情请参考[事件处理重载](./overload.md)。
在其他情况下,我们可以通过 NoneBot 提供的方法来获取 Bot 对象,这些方法将会在[使用适配器](../advanced/adapter.md#获取-bot-对象)中详细介绍:
```python {4,6}
from nonebot import get_bot
# 获取当前所有 Bot 中的第一个
bot = get_bot()
# 获取指定 ID 的 Bot
bot = get_bot("bot_id")
```
### 调用 API
在获得 Bot 对象后,我们可以通过 Bot 的实例方法来调用平台 API
```python {2,5}
# 通过 bot.api_name(**kwargs) 的方法调用 API
result = await bot.get_user_info(user_id=12345678)
# 通过 bot.call_api(api_name, **kwargs) 的方法调用 API
result = await bot.call_api("get_user_info", user_id=12345678)
```
:::caution 注意
实际可以使用的 API 以及参数取决于平台提供的接口以及协议适配器的实现,请参考协议适配器以及平台文档。
:::
在了解了如何调用 API 后,我们可以来改进 `weather` 插件,使得消息发送后,调用 `Console` 接口响铃提醒机器人用户:
```python {4,18} title=weather/__init__.py
from nonebot.adapters.console import Bot, MessageSegment
@weather.got("location", prompt=MessageSegment.emoji("question") + "请输入地名")
async def got_location(bot: Bot, location: str = ArgPlainText()):
await weather.send(
MessageSegment.markdown(
inspect.cleandoc(
f"""
# {location}
- 今天
⛅ 多云 20℃~24℃
"""
)
)
)
await bot.bell()
```

View File

@@ -0,0 +1,591 @@
---
sidebar_position: 0
description: 读取用户配置来控制插件行为
options:
menu:
- category: appendices
weight: 10
---
# 配置
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
配置是项目中非常重要的一部分为了方便我们控制机器人的行为NoneBot 提供了一套配置系统。下面我们将会补充[指南](../quick-start.mdx)中的天气插件,使其能够读取用户配置。在这之前,我们需要先了解一下配置系统,如果你已经了解了 NoneBot 中的配置方法,可以跳转到[编写插件配置](#插件配置)。
NoneBot 使用 [`pydantic`](https://docs.pydantic.dev/) 以及 [`python-dotenv`](https://saurabh-kumar.com/python-dotenv/) 来读取 dotenv 配置文件以及环境变量,从而控制机器人行为。配置文件需要符合 dotenv 格式,复杂数据类型需使用 JSON 格式或 [pydantic 支持格式](https://docs.pydantic.dev/usage/types/)填写。
NoneBot 内置的配置项列表及含义可以在[内置配置项](#内置配置项)中查看。
## 配置项的加载
在 NoneBot 中,我们可以把配置途径分为 **直接传入**、**系统环境变量**、**dotenv 配置文件** 三种,其加载优先级依次由高到低。
### 直接传入
在 NoneBot 初始化的过程中,可以通过 `nonebot.init()` 传入任意合法的 Python 变量,也可以在初始化完成后直接赋值。
通常,在初始化前的传参会在机器人的入口文件(如 `bot.py`)中进行,而初始化后的赋值可以在任何地方进行。
```python {4,8,9} title=bot.py
import nonebot
# 初始化时
nonebot.init(custom_config1="config on init")
# 初始化后
config = nonebot.get_driver().config
config.custom_config1 = "changed after init"
config.custom_config2 = "new config after init"
```
### 系统环境变量
在 dotenv 配置文件中定义的配置项,也会在环境变量中进行寻找。如果在环境变量中发现同名配置项(大小写不敏感),将会覆盖 dotenv 中所填值。
例如,在 dotenv 配置文件中存在配置项 `custom_config`
```dotenv
CUSTOM_CONFIG=config in dotenv
```
同时,设置环境变量:
```bash
# windows
set CUSTOM_CONFIG "config in environment variables"
# linux/macOS
export CUSTOM_CONFIG="config in environment variables"
```
那最终 NoneBot 所读取的内容为环境变量中的内容,即 `config in environment variables`。
:::caution 注意
NoneBot 不会自发读取未被定义的配置项的环境变量,如果需要读取某一环境变量需要在 dotenv 配置文件中进行声明。
:::
### dotenv 配置文件
dotenv 是一种便捷的跨平台配置通用模式,也是我们推荐的配置方式。
NoneBot 在启动时将会从系统环境变量或者 `.env` 文件中寻找配置项 `ENVIRONMENT` (大小写不敏感),默认值为 `prod`。这将决定 NoneBot 后续进一步加载环境配置的文件路径 `.env.{ENVIRONMENT}`。
#### 配置项解析
dotenv 文件中的配置值使用 JSON 进行解析。如果配置项值无法被解析,将作为**字符串**处理。例如:
```dotenv
STRING_CONFIG=some string
LIST_CONFIG=[1, 2, 3]
DICT_CONFIG={"key": "value"}
MULTILINE_CONFIG='
[
{
"item_key": "item_value"
}
]
'
EMPTY_CONFIG=
NULL_CONFIG
```
将被解析为:
```python
dotenv_config = {
"string_config": "some string",
"list_config": [1, 2, 3],
"dict_config": {"key": "value"},
"multiline_config": [{"item_key": "item_value"}],
"empty_config": "",
"null_config": None
}
```
特别的NoneBot 支持使用 `env_nested_delimiter` 配置嵌套字典,在层与层之间使用 `__` 分隔即可:
```dotenv
DICT={"k1": "v1", "k2": null}
DICT__K2=v2
DICT__K3=v3
DICT__INNER__K4=v4
```
将被解析为:
```python
dotenv_config = {
"dict": {
"k1": "v1",
"k2": "v2",
"k3": "v3",
"inner": {
"k4": "v4"
}
}
}
```
#### .env 文件
`.env` 文件是基础配置文件,该文件中的配置项在不同环境下都会被加载,但会被 `.env.{ENVIRONMENT}` 文件中的配置所**覆盖**。
我们可以在 `.env` 文件中写入当前的环境信息:
```dotenv
ENVIRONMENT=dev
COMMON_CONFIG=common config # 这个配置项在任何环境中都会被加载
```
这样,我们在启动 NoneBot 时就会从 `.env.dev` 文件中加载剩余配置项。
:::tip 提示
在生产环境中,可以通过设置环境变量 `ENVIRONMENT=prod` 来确保 NoneBot 读取正确的环境配置。
:::
#### .env.{ENVIRONMENT} 文件
`.env.{ENVIRONMENT}` 文件类似于预设,可以让我们在多套不同的配置方案中灵活切换,默认 NoneBot 会读取 `.env.prod` 配置。如果你使用了 `nb-cli` 创建 `simple` 项目,那么将含有两套预设配置:`.env.dev` 和 `.env.prod`。
在 NoneBot 初始化时,可以指定加载某个环境配置文件:
```python
nonebot.init(_env_file=".env.dev")
```
这将忽略在 `.env` 文件或环境变量中指定的 `ENVIRONMENT` 配置项。
## 读取配置项
NoneBot 的全局配置对象可以通过 `driver` 获取,如:
```python
import nonebot
config = nonebot.get_driver().config
```
如果我们需要获取某个配置项,可以直接通过 `config` 对象的属性访问:
```python
superusers = config.superusers
```
如果配置项不存在,将会抛出异常。
## 插件配置
在一个涉及大量配置项的项目中,通过直接读取配置项的方式显然并不高效。同时,由于额外的全局配置项没有预先定义,开发时编辑器将无法提示字段与类型,并且运行时没有对配置项直接进行合法性检查。那么就需要一种方式来规范定义插件配置项。
在 NoneBot 中,我们使用强大高效的 `pydantic` 来定义配置模型,这个模型可以被用于配置的读取和类型检查等。例如在 `weather` 插件目录中新建 `config.py` 来定义一个模型:
```python title=weather/config.py
from pydantic import BaseModel, validator
class Config(BaseModel):
weather_api_key: str
weather_command_priority: int = 10
weather_plugin_enabled: bool = True
@validator("weather_command_priority")
def check_priority(cls, v):
if isinstance(v, int) and v >= 1:
return v
raise ValueError("weather command priority must be an integer and greater than 1")
```
在 `config.py` 中,我们定义了一个 `Config` 类,它继承自 `pydantic.BaseModel`,并定义了一些配置项。在 `Config` 类中,我们还定义了一个 `check_priority` 方法,它用于检查 `weather_command_priority` 配置项的合法性。更多关于 `pydantic` 的编写方式,可以参考 [pydantic 官方文档](https://docs.pydantic.dev/)。
在定义好配置模型后,我们可以在插件加载时获取全局配置,导入插件自身的配置模型并使用:
```python {5,11} title=weather/__init__.py
from nonebot import get_driver
from .config import Config
plugin_config = Config.parse_obj(get_driver().config)
weather = on_command(
"天气",
rule=to_me(),
aliases={"weather", "查天气"},
priority=plugin_config.weather_command_priority,
block=True,
)
```
然后,我们便可以从 `plugin_config` 中读取配置了,例如 `plugin_config.weather_api_key`。
这种方式可以简洁、高效地读取配置项,同时也可以设置默认值或者在运行时对配置项进行合法性检查,防止由于配置项导致的插件出错等情况出现。
:::tip 提示
发布插件应该为自身的事件响应器提供可配置的优先级,以便插件使用者可以自定义多个插件间的响应顺序。
:::
由于插件配置项是从全局配置中读取的,通常我们需要在配置项名称前面添加前缀名,以防止配置项冲突。例如在上方的示例中,我们就添加了配置项前缀 `weather_`。但是这样会导致在使用配置项时过长的变量名,因此我们可以使用 `pydantic` 的 `alias` 或者通过配置 scope 来简化配置项名称。这里我们以 scope 配置为例:
```python title=weather/config.py
from pydantic import BaseModel
class ScopedConfig(BaseModel):
api_key: str
command_priority: int = 10
plugin_enabled: bool = True
class Config(BaseModel):
weather: ScopedConfig
```
```python title=weather/__init__.py
from nonebot import get_driver
from .config import Config
plugin_config = Config.parse_obj(get_driver().config).weather
```
这样我们就可以省略插件配置项名称中的前缀 `weather_` 了。但需要注意的是,如果我们使用了 scope 配置,那么在配置文件中也需要使用 [`env_nested_delimiter` 格式](#配置项解析),例如:
```dotenv
WEATHER__API_KEY=123456
WEATHER__COMMAND_PRIORITY=10
```
## 内置配置项
配置项 API 文档可以前往 [Config 类](../api/config.md#Config)查看。
### Driver
- **类型**: `str`
- **默认值**: `"~fastapi"`
NoneBot 运行所使用的驱动器。具体配置方法可以参考[安装驱动器](../tutorial/store.mdx#安装驱动器)和[选择驱动器](../advanced/driver.md)。
<Tabs groupId="configMethod">
<TabItem value="dotenv" label="dotenv" default>
```dotenv
DRIVER=~fastapi+~httpx+~websockets
```
</TabItem>
<TabItem value="env" label="系统环境变量">
```bash
# windows
set DRIVER '~fastapi+~httpx+~websockets'
# linux/macOS
export DRIVER='~fastapi+~httpx+~websockets'
```
</TabItem>
<TabItem value="init" label="直接传入">
```python title=bot.py
import nonebot
nonebot.init(driver="~fastapi+~httpx+~websockets")
```
</TabItem>
</Tabs>
### Host
- **类型**: `IPvAnyAddress`
- **默认值**: `127.0.0.1`
当 NoneBot 作为服务端时,监听的 IP / 主机名。
<Tabs groupId="configMethod">
<TabItem value="dotenv" label="dotenv" default>
```dotenv
HOST=127.0.0.1
```
</TabItem>
<TabItem value="env" label="系统环境变量">
```bash
# windows
set HOST '127.0.0.1'
# linux/macOS
export HOST='127.0.0.1'
```
</TabItem>
<TabItem value="init" label="直接传入">
```python title=bot.py
import nonebot
nonebot.init(host="127.0.0.1")
```
</TabItem>
</Tabs>
### Port
- **类型**: `int` (1 ~ 65535)
- **默认值**: `8080`
当 NoneBot 作为服务端时,监听的端口。
<Tabs groupId="configMethod">
<TabItem value="dotenv" label="dotenv" default>
```dotenv
PORT=8080
```
</TabItem>
<TabItem value="env" label="系统环境变量">
```bash
# windows
set PORT '8080'
# linux/macOS
export PORT='8080'
```
</TabItem>
<TabItem value="init" label="直接传入">
```python title=bot.py
import nonebot
nonebot.init(port=8080)
```
</TabItem>
</Tabs>
### Log Level
- **类型**: `int | str`
- **默认值**: `INFO`
NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称。具体等级对照表参考 [loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
:::tip 提示
日志等级名称应为大写,如 `INFO`。
:::
<Tabs groupId="configMethod">
<TabItem value="dotenv" label="dotenv" default>
```dotenv
LOG_LEVEL=DEBUG
```
</TabItem>
<TabItem value="env" label="系统环境变量">
```bash
# windows
set LOG_LEVEL 'DEBUG'
# linux/macOS
export LOG_LEVEL='DEBUG'
```
</TabItem>
<TabItem value="init" label="直接传入">
```python title=bot.py
import nonebot
nonebot.init(log_level="DEBUG")
```
</TabItem>
</Tabs>
### API Timeout
- **类型**: `float | None`
- **默认值**: `30.0`
调用平台接口的超时时间,单位为秒。`None` 表示不设置超时时间。
<Tabs groupId="configMethod">
<TabItem value="dotenv" label="dotenv" default>
```dotenv
API_TIMEOUT=10.0
```
</TabItem>
<TabItem value="env" label="系统环境变量">
```bash
# windows
set API_TIMEOUT '10.0'
# linux/macOS
export API_TIMEOUT='10.0'
```
</TabItem>
<TabItem value="init" label="直接传入">
```python title=bot.py
import nonebot
nonebot.init(api_timeout=10.0)
```
</TabItem>
</Tabs>
### SuperUsers
- **类型**: `set[str]`
- **默认值**: `set()`
机器人超级用户,可以使用权限 [`SUPERUSER`](../api/permission.md#SUPERUSER)。
<Tabs groupId="configMethod">
<TabItem value="dotenv" label="dotenv" default>
```dotenv
SUPERUSERS=["123123123"]
```
</TabItem>
<TabItem value="env" label="系统环境变量">
```bash
# windows
set SUPERUSERS '["123123123"]'
# linux/macOS
export SUPERUSERS='["123123123"]'
```
</TabItem>
<TabItem value="init" label="直接传入">
```python title=bot.py
import nonebot
nonebot.init(superusers={"123123123"})
```
</TabItem>
</Tabs>
### Nickname
- **类型**: `set[str]`
- **默认值**: `set()`
机器人昵称,通常协议适配器会根据用户是否 @bot 或者是否以机器人昵称开头来判断是否是向机器人发送的消息。
<Tabs groupId="configMethod">
<TabItem value="dotenv" label="dotenv" default>
```dotenv
NICKNAME=["bot"]
```
</TabItem>
<TabItem value="env" label="系统环境变量">
```bash
# windows
set NICKNAME '["bot"]'
# linux/macOS
export NICKNAME='["bot"]'
```
</TabItem>
<TabItem value="init" label="直接传入">
```python title=bot.py
import nonebot
nonebot.init(nickname={"bot"})
```
</TabItem>
</Tabs>
### Command Start 和 Command Separator
- **类型**: `set[str]`
- **默认值**:
- Command Start: `{"/"}`
- Command Separator: `{"."}`
命令消息的起始符和分隔符。用于 [`command`](../advanced/matcher.md#command) 规则。
<Tabs groupId="configMethod">
<TabItem value="dotenv" label="dotenv" default>
```dotenv
COMMAND_START=["/", ""]
COMMAND_SEP=[".", " "]
```
</TabItem>
<TabItem value="env" label="系统环境变量">
```bash
# windows
set COMMAND_START '["/", ""]'
set COMMAND_SEP '[".", " "]'
# linux/macOS
export COMMAND_START='["/", ""]'
export COMMAND_SEP='[".", " "]'
```
</TabItem>
<TabItem value="init" label="直接传入">
```python title=bot.py
import nonebot
nonebot.init(command_start={"/", ""}, command_sep={".", " "})
```
</TabItem>
</Tabs>
### Session Expire Timeout
- **类型**: `timedelta`
- **默认值**: `timedelta(minutes=2)`
用户会话超时时间,配置格式参考 [Datetime Types](https://docs.pydantic.dev/usage/types/#datetime-types),可以为单位为秒的 `int | float` 等。
<Tabs groupId="configMethod">
<TabItem value="dotenv" label="dotenv" default>
```dotenv
SESSION_EXPIRE_TIMEOUT=120
```
</TabItem>
<TabItem value="env" label="系统环境变量">
```bash
# windows
set SESSION_EXPIRE_TIMEOUT '120'
# linux/macOS
export SESSION_EXPIRE_TIMEOUT='120'
```
</TabItem>
<TabItem value="init" label="直接传入">
```python title=bot.py
import nonebot
nonebot.init(session_expire_timeout=120)
```
</TabItem>
</Tabs>

View File

@@ -0,0 +1,102 @@
---
sidebar_position: 6
description: 记录与控制日志
options:
menu:
- category: appendices
weight: 70
---
# 日志
无论是在开发还是在生产环境中,日志都是一个重要的功能,可以帮助我们了解运行状况、排查问题等。虽然我们可以使用 `print` 来将需要的信息输出到控制台但是这种方式难以控制而且不利于日志的归档、分析等。NoneBot 使用优秀的 [Loguru](https://loguru.readthedocs.io/) 库来进行日志记录。
## 记录日志
我们可以从 NoneBot 中导入 `logger` 对象,然后使用 `logger` 对象的方法来记录日志。
```python
from nonebot import logger
logger.trace("This is a trace message")
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.success("This is a success message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")
```
我们仅需一行代码即可记录对应级别的日志。日志可以通过配置 [`LOG_LEVEL` 配置项](./config.mdx#log-level)来过滤输出等级,控制台中仅会输出大于等于 `LOG_LEVEL` 的日志。默认的 `LOG_LEVEL``INFO`,即只会输出 `INFO``SUCCESS``WARNING``ERROR``CRITICAL` 级别的日志。
如果需要记录 `Exception traceback` 日志,可以向 `logger` 添加 `exception` 选项:
```python {4}
try:
1 / 0
except ZeroDivisionError:
logger.opt(exception=True).error("ZeroDivisionError")
```
如果需要输出彩色日志,可以向 `logger` 添加 `colors` 选项:
```python
logger.opt(colors=True).warning("We got a <red>BIG</red> problem")
```
更多日志记录方法请参考 [Loguru 文档](https://loguru.readthedocs.io/)。
## 自定义日志输出
NoneBot 在启动时会添加一个默认的日志处理器,该处理器会将日志输出到**stdout**,并且根据 `LOG_LEVEL` 配置项过滤日志等级。
默认的日志格式为:
```text
<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}
```
我们可以从 `nonebot.log` 模块导入以使用 NoneBot 的默认格式和过滤器:
```python
from nonebot.log import default_format, default_filter
```
如果需要自定义日志格式,我们需要移除 NoneBot 默认的日志处理器并添加新的日志处理器。例如,在机器人入口文件中 `nonebot.init` 之前添加以下内容:
```python title=bot.py
from nonebot.log import logger_id
# 移除 NoneBot 默认的日志处理器
logger.remove(logger_id)
# 添加新的日志处理器
logger.add(
sys.stdout,
level=0,
diagnose=True,
format="<g>{time:MM-DD HH:mm:ss}</g> [<lvl>{level}</lvl>] <c><u>{name}</u></c> | {message}",
filter=default_filter
)
```
如果想要输出日志到文件,我们可以使用 `logger.add` 方法添加文件处理器:
```python title=bot.py
logger.add("error.log", level="ERROR", format=default_format, rotation="1 week")
```
更多日志处理器的使用方法请参考 [Loguru 文档](https://loguru.readthedocs.io/)。
## 重定向 logging 日志
`logging` 是 Python 标准库中的日志模块NoneBot 提供了一个 logging handler 用于将 `logging` 日志重定向到 `loguru` 处理。
```python
from nonebot.log import LoguruHandler
# root logger 添加 LoguruHandler
logging.basicConfig(handlers=[LoguruHandler()])
# 或者为其他 logging.Logger 添加 LoguruHandler
logger.addHandler(LoguruHandler())
```

View File

@@ -0,0 +1,74 @@
---
sidebar_position: 7
description: 根据事件类型进行不同的处理
options:
menu:
- category: appendices
weight: 80
---
# 事件类型与重载
在之前的示例中,我们已经了解了如何[获取事件信息](../tutorial/event-data.mdx)以及[使用平台接口](./api-calling.mdx)。但是,事件信息通常不仅仅包含消息这一个内容,还有其他平台提供的信息,例如消息发送时间、消息发送者等等。同时,在使用平台接口时,我们需要确保使用的**平台接口**与所要发送的**平台类型**一致,对不同类型的事件需要做出不同的处理。在本章节中,我们将介绍如何获取事件更多的信息以及根据事件类型进行不同的处理。
## 事件类型
在 NoneBot 中,事件均是 `nonebot.adapters.Event` 基类的子类型,基类对一些必要的属性进行了抽象,子类型则根据不同的平台进行了实现。在[自定义权限](./permission.mdx#自定义权限)一节中,我们就使用了 `Event` 的抽象方法 `get_user_id` 来获取事件发送者 ID这个方法由协议适配器进行了实现返回机器人用户对应的平台 ID。更多的基类抽象方法可以在[使用适配器](../advanced/adapter.md#获取事件通用信息)中查看。
既然事件是基类的子类型,我们实际可以获得的信息通常多于基类抽象方法所提供的。如果我们不满足于基类能获得的信息,我们可以小小的修改一下事件处理函数的事件参数类型注解,使其变为子类型,这样我们就可以通过协议适配器定义的子类型来获取更多的信息。我们以 `Console` 协议适配器为例:
```python {4} title=weather/__init__.py
from nonebot.adapters.console import MessageEvent
@weather.got("location", prompt="请输入地名")
async def got_location(event: MessageEvent, location: str = ArgPlainText()):
await weather.finish(f"{event.time.strftime('%Y-%m-%d')} {location} 的天气是...")
```
在上面的代码中,我们获取了 `Console` 协议适配器的消息事件提供的发送时间 `time` 属性。
:::caution 注意
如果**基类**就能满足你的需求,那么就**不要修改**事件参数类型注解,这样可以使你的代码更加**通用**,可以在更多平台上运行。如何根据不同平台事件类型进行不同的处理,我们将在[重载](#重载)一节中介绍。
:::
## 重载
我们在编写机器人时常常会遇到这样一个问题如何对私聊和群聊消息进行不同的处理如何对不同平台的事件进行不同的处理针对这些问题NoneBot 提供了一个便捷而高效的解决方案 ── 重载。简单来说,依赖函数会根据其参数的类型注解来决定是否执行,忽略不符合其参数类型注解的情况。这样,我们就可以通过修改事件参数类型注解来实现对不同事件的处理,或者修改 `Bot` 参数类型注解来实现使用不同平台的接口。我们以 `OneBot` 协议适配器为例:
```python {4,8}
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
@matcher.handle()
async def handle_private(event: PrivateMessageEvent):
await matcher.finish("私聊消息")
@matcher.handle()
async def handle_group(event: GroupMessageEvent):
await matcher.finish("群聊消息")
```
这样,机器人用户就会在私聊和群聊中分别收到不同的回复。同样的,我们也可以通过修改 `Bot` 参数类型注解来实现使用不同平台的接口:
```python
from nonebot.adapters.console import Bot as ConsoleBot
from nonebot.adapters.onebot.v11 import Bot as OneBot
@matcher.handle()
async def handle_console(bot: ConsoleBot):
await bot.bell()
@matcher.handle()
async def handle_onebot(bot: OneBot):
await bot.send_group_message(group_id=123123, message="OneBot")
```
:::caution 注意
重载机制对所有的参数类型注解都有效,因此,依赖注入也可以使用这个特性来对不同的返回值进行处理。
但 Bot、Event 和 Matcher 三者的参数类型注解具有最高检查优先级,如果三者任一类型注解不匹配,那么其他依赖注入将不会执行(如:`Depends`)。
:::
:::tip 提示
如何更好地编写一个跨平台的插件,我们将在[最佳实践](../best-practice/multi-adapter.mdx)中介绍。
:::

View File

@@ -0,0 +1,116 @@
---
sidebar_position: 5
description: 控制事件响应器的权限
options:
menu:
- category: appendices
weight: 60
---
# 权限控制
import Messenger from "@site/src/components/Messenger";
**权限控制**是机器人在实际应用中需要解决的重点问题之一NoneBot 提供了灵活的权限控制机制 —— `Permission`。
类似于响应规则 `Rule``Permission` 是由非负整数个 `PermissionChecker` 所共同组成的**用于筛选事件**的对象。但需要特别说明的是,权限和响应规则有如下区别:
1. 权限检查**先于**响应规则检查
2. `Permission` 只需**其中一个** `PermissionChecker` 返回 `True` 时就会检查通过
3. 权限检查进行时,上下文中并不存在会话状态 `state`
4. `Rule` 仅在**初次触发**事件响应器时进行检查,在余下的会话中并不会限制事件;而 `Permission` 会**持续生效**,在连续对话中一直对事件主体加以限制。
## 基础使用
通常情况下,`Permission` 更侧重于对于**触发事件的机器人用户**的筛选,例如由 NoneBot 自身提供的 `SUPERUSER` 权限,便是筛选出会话发起者是否为超级用户。它可以对输入的用户进行鉴别,如果符合要求则会被认为通过并返回 `True`,反之则返回 `False`。
简单来说,`Permission` 是一个用于筛选出符合要求的用户的机制,可以通过 `Permission` 精确的控制响应对象的覆盖范围,从而拒绝掉我们所不希望的事件。
例如,我们可以在 `weather` 插件中添加一个超级用户可用的指令:
```python {2,8} title=weather/__init__.py
from typing import Tuple
from nonebot.permission import SUPERUSER
manage = on_command(
("天气", "启用"),
rule=to_me(),
aliases={("天气", "禁用")},
permission=SUPERUSER,
)
@manage.handle()
async def control(cmd: Tuple[str, str] = Command()):
_, action = cmd
if action == "启用":
plugin_config.weather_plugin_enabled = True
elif action == "禁用":
plugin_config.weather_plugin_enabled = False
await manage.finish(f"天气插件已{action}")
```
如上方示例所示,在注册事件响应器时,我们设置了 `permission` 参数,那么这个事件处理器在触发事件前的检查阶段会对用户身份进行验证,如果不符合我们设置的条件(此处即为**超级用户**)则不会响应。此时,我们向机器人发送 `/天气.禁用` 指令,机器人不会有任何响应,因为我们还不是机器人的超级管理员。我们在 dotenv 文件中设置了 `SUPERUSERS` 配置项之后,机器人就会响应我们的指令了。
```dotenv title=.env
SUPERUSERS=["console_user"]
```
<Messenger
msgs={[
{ position: "right", msg: "/天气.禁用" },
{ position: "left", msg: "天气插件已禁用" },
{ position: "right", msg: "/天气.启用" },
{ position: "left", msg: "天气插件已启用" },
]}
/>
## 自定义权限
与事件响应规则类似,`PermissionChecker` 也是一个返回值为 `bool` 类型的依赖函数,即 `PermissionChecker` 支持依赖注入。例如,我们可以限制用户的指令调用次数:
```python title=weather/__init__.py
from nonebot.adapters import Event
fake_db: Dict[str, int] = {}
async def limit_permission(event: Event):
count = fake_db.setdefault(event.get_user_id(), 100)
if count > 0:
fake_db[event.get_user_id()] -= 1
return True
return False
weather = on_command("天气", permission=limit_permission)
```
## 权限组合
权限之间可以通过 `|` 运算符进行组合,使得任意一个权限检查返回 `True` 时通过。例如:
```python {4-6}
perm1 = Permission(foo_checker)
perm2 = Permission(bar_checker)
perm = perm1 | perm2
perm = perm1 | bar_checker
perm = foo_checker | perm2
```
同样的,我们也无需担心组合了一个 `None` 值,`Permission` 会自动忽略 `None` 值。
```python
assert (perm | None) is perm
```
## 主动使用权限
除了在事件响应器中使用权限外,我们也可以主动使用权限来判断事件是否符合条件。例如:
```python {3}
perm = Permission(some_checker)
result: bool = await perm(bot, event)
```
我们只需要传入 `Bot` 实例、事件,`Permission` 会并发调用所有 `PermissionChecker` 进行检查,并返回结果。

View File

@@ -0,0 +1,107 @@
---
sidebar_position: 1
description: 自定义响应规则
options:
menu:
- category: appendices
weight: 20
---
# 响应规则
机器人在实际应用中往往会接收到多种多样的事件类型NoneBot 通过响应规则来控制事件的处理。
在[指南](../tutorial/matcher.md#为事件响应器添加参数)中,我们为 `weather` 命令添加了一个 `rule=to_me()` 参数,这个参数就是一个响应规则,确保只有在私聊或者 `@bot` 时才会响应。
响应规则是一个 `Rule` 对象,它由一系列的 `RuleChecker` 函数组成,每个 `RuleChecker` 函数都会检查事件是否符合条件,如果所有的检查都通过,则事件会被处理。
## RuleChecker
`RuleChecker` 是一个返回值为 `bool` 类型的依赖函数,即 `RuleChecker` 支持依赖注入。我们可以根据上一节中添加的[配置项](./config.mdx#插件配置),在 `weather` 插件目录中编写一个响应规则:
```python {3,4} title=weather/__init__.py
plugin_config = Config.parse_obj(get_driver().config)
async def is_enable() -> bool:
return plugin_config.weather_plugin_enabled
weather = on_command("天气", rule=is_enable)
```
在上面的代码中,我们定义了一个函数 `is_enable`,它会检查配置项 `weather_plugin_enabled` 是否为 `True`。这个函数 `is_enable` 即为一个 `RuleChecker`。
## Rule
`Rule` 是若干个 `RuleChecker` 的集合,它会并发调用每个 `RuleChecker`,只有当所有 `RuleChecker` 检查通过时匹配成功。例如:我们可以组合两个 `RuleChecker`,一个用于检查插件是否启用,一个用于检查用户是否在黑名单中:
```python {10}
from nonebot.rule import Rule
from nonebot.adapters import Event
async def is_enable() -> bool:
return plugin_config.weather_plugin_enabled
async def is_blacklisted(event: Event) -> bool:
return event.get_user_id() not in BLACKLIST
rule = Rule(is_enable, is_blacklisted)
weather = on_command("天气", rule=rule)
```
## 合并响应规则
在定义响应规则时,我们可以将规则进行细分,来更好地复用规则。而在使用时,我们需要合并多个规则。除了使用 `Rule` 对象来组合多个 `RuleChecker` 外,我们还可以对 `Rule` 对象进行合并。在原 `weather` 插件中,我们可以将 `rule=to_me()` 与 `rule=is_enable` 使用 `&` 运算符合并:
```python {10} title=weather/__init__.py
from nonebot.rule import to_me
plugin_config = Config.parse_obj(get_driver().config)
async def is_enable() -> bool:
return plugin_config.weather_plugin_enabled
weather = on_command(
"天气",
rule=to_me() & is_enable,
aliases={"weather", "查天气"},
priority=plugin_config.weather_command_priority
block=True,
)
```
这样,`weather` 命令就只会在插件启用且在私聊或者 `@bot` 时才会响应。
合并响应规则可以有多种形式,例如:
```python {4-6}
rule1 = Rule(foo_checker)
rule2 = Rule(bar_checker)
rule = rule1 & rule2
rule = rule1 & bar_checker
rule = foo_checker & rule2
```
同时,我们也无需担心合并了一个 `None` 值,`Rule` 会忽略 `None` 值。
```python
assert (rule & None) is rule
```
## 主动使用响应规则
除了在事件响应器中使用响应规则外,我们也可以主动使用响应规则来判断事件是否符合条件。例如:
```python {3}
rule = Rule(some_checker)
result: bool = await rule(bot, event, state)
```
我们只需要传入 `Bot` 对象、事件和会话状态,`Rule` 会并发调用所有 `RuleChecker` 进行检查,并返回结果。
## 内置响应规则
NoneBot 内置了一些常用的响应规则,可以直接通过事件响应器辅助函数或者自行合并其他规则使用。内置响应规则列表可以参考[事件响应器进阶](../advanced/matcher.md)

Some files were not shown because too many files have changed in this diff Show More