mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-10-07 03:07:07 +00:00
Compare commits
107 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4d070f5b48 | ||
|
82138454bc | ||
|
d98fe53d56 | ||
|
278b9e92c2 | ||
|
45418ccfae | ||
|
2ad2922565 | ||
|
84ebcb4ce6 | ||
|
6a0caacfd6 | ||
|
a8f3940cbc | ||
|
15d3910462 | ||
|
edfd0eb887 | ||
|
fe63717848 | ||
|
63424bc3ac | ||
|
99b1d0ed96 | ||
|
90c7fd4747 | ||
|
c1a9758a18 | ||
|
17e7a0c029 | ||
|
df6a948c08 | ||
|
9f19eb7a96 | ||
|
2b68428526 | ||
|
d62c6561c2 | ||
|
fc3bb5ff1f | ||
|
76b1bbb443 | ||
|
7b724925ba | ||
|
62dc2574c7 | ||
|
ea40ae3a18 | ||
|
f94e7d9b5b | ||
|
c8ba973280 | ||
|
35e062c588 | ||
|
53724487d3 | ||
|
a3003b0ff6 | ||
|
96ecd415cd | ||
|
e8ef4735ea | ||
|
b78b08ed81 | ||
|
e11ea52276 | ||
|
819e7334b2 | ||
|
1ebafaa9a5 | ||
|
3554292d5f | ||
|
ec9ef9a760 | ||
|
74663c7c5e | ||
|
cbc99be031 | ||
|
81e9bdd7ec | ||
|
323038ecc6 | ||
|
7091beb809 | ||
|
010c48d30f | ||
|
a5b2dd38d5 | ||
|
fa5f295fe7 | ||
|
7f7b23bd2f | ||
|
0434e12b8a | ||
|
425d140161 | ||
|
64d8f7843a | ||
|
a0a6427540 | ||
|
31fe8e6582 | ||
|
38e42919b7 | ||
|
c769f95688 | ||
|
d642897a5b | ||
|
d7931f8ec2 | ||
|
8a0b989718 | ||
|
4fbbb646c3 | ||
|
75856e63f6 | ||
|
98213f50db | ||
|
5bce1db24e | ||
|
380ace5780 | ||
|
6e5b01a3d4 | ||
|
622e8e8af3 | ||
|
2bbb83d3f2 | ||
|
54756134d4 | ||
|
932b212e04 | ||
|
3b40e5b20c | ||
|
f594db207d | ||
|
70e23427e8 | ||
|
c1a303fd3d | ||
|
a62b9a5e1a | ||
|
36eece311a | ||
|
29ea5f5787 | ||
|
c00e3aacfc | ||
|
cf9f78528c | ||
|
68d4795de6 | ||
|
e689d7f7d2 | ||
|
a607f868c2 | ||
|
84ac1c4bad | ||
|
e11ff528e2 | ||
|
047f4d1878 | ||
|
0294c33baf | ||
|
11a8b6e40b | ||
|
cade86b62a | ||
|
df836ec1c6 | ||
|
12cc00a3d3 | ||
|
24aa81f0be | ||
|
339706a3a6 | ||
|
b43c9adb7a | ||
|
c2783039d4 | ||
|
c4706e4123 | ||
|
8a997540b3 | ||
|
045022b22a | ||
|
723fa4b3d8 | ||
|
41b59cff06 | ||
|
bed1b46527 | ||
|
ad695ca6e8 | ||
|
33e997708c | ||
|
56b6ee1d38 | ||
|
27b2cf52a5 | ||
|
b532130f6e | ||
|
d16b8594ad | ||
|
ad8442c6de | ||
|
4edf7e2c2c | ||
|
ea49318809 |
@@ -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.4.2
|
||||
rev: v0.5.0
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
|
@@ -123,6 +123,7 @@ NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架
|
||||
| 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 协议 |
|
||||
| Kritor([仓库](https://github.com/nonebot/adapter-kritor),[协议](https://github.com/KarinJS/kritor)) | ✅ | Kritor (OnebotX) 协议,QQ 机器人接口标准 |
|
||||
| Mirai([仓库](https://github.com/nonebot/adapter-mirai),[协议](https://docs.mirai.mamoe.net/mirai-api-http/)) | ✅ | QQ 协议 |
|
||||
| 钉钉([仓库](https://github.com/nonebot/adapter-ding),[协议](https://open.dingtalk.com/document/)) | 🤗 | 寻找 Maintainer(暂不可用) |
|
||||
| 开黑啦([仓库](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 协议,由社区贡献 |
|
||||
@@ -132,6 +133,7 @@ NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架
|
||||
| Walle-Q([仓库](https://github.com/onebot-walle/nonebot_adapter_walleq)) | ↗️ | QQ 协议,由社区贡献 |
|
||||
| Villa([仓库](https://github.com/CMHopeSunshine/nonebot-adapter-villa)) | ❌ | 米游社大别野 Bot 协议,官方已下线 |
|
||||
| Rocket.Chat([仓库](https://github.com/IUnlimit/nonebot-adapter-rocketchat),[协议](https://developer.rocket.chat/)) | ↗️ | Rocket.Chat Bot 协议,由社区贡献 |
|
||||
| Tailchat([仓库](https://github.com/eya46/nonebot-adapter-tailchat),[协议](https://tailchat.msgbyte.com/)) | ↗️ | Tailchat 开放平台 Bot 协议,由社区贡献 |
|
||||
|
||||
- 坚实后盾:支持多种 web 框架,可自定义替换、组合
|
||||
|
||||
|
@@ -59,6 +59,16 @@
|
||||
"tags": [],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot.adapters.mirai",
|
||||
"project_link": "nonebot-adapter-mirai",
|
||||
"name": "Mirai",
|
||||
"desc": "mirai-api-http v2 协议适配",
|
||||
"author": "RF-Tar-Railt",
|
||||
"homepage": "https://github.com/nonebot/adapter-mirai",
|
||||
"tags": [],
|
||||
"is_official": true
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot.adapters.mirai2",
|
||||
"project_link": "nonebot_adapter_mirai2",
|
||||
@@ -238,5 +248,15 @@
|
||||
}
|
||||
],
|
||||
"is_official": true
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_adapter_tailchat",
|
||||
"project_link": "nonebot-adapter-tailchat",
|
||||
"name": "Tailchat",
|
||||
"desc": "Tailchat 适配器",
|
||||
"author": "eya46",
|
||||
"homepage": "https://github.com/eya46/nonebot-adapter-tailchat",
|
||||
"tags": [],
|
||||
"is_official": false
|
||||
}
|
||||
]
|
||||
|
@@ -866,13 +866,6 @@
|
||||
"tags": [],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_covid",
|
||||
"project_link": "nonebot-plugin-covid",
|
||||
"author": "nicklly",
|
||||
"tags": [],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_yulu",
|
||||
"project_link": "nonebot-plugin-yulu",
|
||||
@@ -2543,13 +2536,6 @@
|
||||
"tags": [],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_molar_mass",
|
||||
"project_link": "nonebot-plugin-molar-mass",
|
||||
"author": "kifuan",
|
||||
"tags": [],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_report_manager",
|
||||
"project_link": "nonebot-plugin-report-manager",
|
||||
@@ -5903,5 +5889,534 @@
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_anime_downloader",
|
||||
"project_link": "nonebot-plugin-anime-downloader",
|
||||
"author": "zhaomaoniu",
|
||||
"tags": [
|
||||
{
|
||||
"label": "Anime",
|
||||
"color": "#ff7474"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_with_ai_agents",
|
||||
"project_link": "nonebot-plugin-with-ai-agents",
|
||||
"author": "yejue",
|
||||
"tags": [
|
||||
{
|
||||
"label": "AI 智能体",
|
||||
"color": "#5dac81"
|
||||
},
|
||||
{
|
||||
"label": "多平台模型",
|
||||
"color": "#5dac81"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_RanFurryPic",
|
||||
"project_link": "nonebot-plugin-RanFurryPic",
|
||||
"author": "Ekac00",
|
||||
"tags": [
|
||||
{
|
||||
"label": "furry",
|
||||
"color": "#8cb9e3"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_furryfusion",
|
||||
"project_link": "nonebot-plugin-furryfusion",
|
||||
"author": "Ekac00",
|
||||
"tags": [
|
||||
{
|
||||
"label": "furry",
|
||||
"color": "#8cb9e3"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_mysticism",
|
||||
"project_link": "nonebot-plugin-mysticism",
|
||||
"author": "Yan-Zero",
|
||||
"tags": [
|
||||
{
|
||||
"label": "占卜",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "tarot",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "神秘学",
|
||||
"color": "#2d4168"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_tsugu_frontend",
|
||||
"project_link": "nonebot-plugin-tsugu-frontend",
|
||||
"author": "zhaomaoniu",
|
||||
"tags": [
|
||||
{
|
||||
"label": "BanGDream",
|
||||
"color": "#e70050"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_kurogames",
|
||||
"project_link": "nonebot-plugin-kurogames",
|
||||
"author": "ConcyWee",
|
||||
"tags": [
|
||||
{
|
||||
"label": "战双帕弥什",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "鸣潮",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "库洛",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_valve_server_query",
|
||||
"project_link": "nonebot-plugin-valve-server-query",
|
||||
"author": "LiLuo-B",
|
||||
"tags": [
|
||||
{
|
||||
"label": "valve",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "query",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "a2s",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_sparkapi",
|
||||
"project_link": "nonebot-plugin-sparkapi",
|
||||
"author": "CCLMSY",
|
||||
"tags": [
|
||||
{
|
||||
"label": "AI",
|
||||
"color": "#00ff00"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_tsugu_bangdream_bot",
|
||||
"project_link": "nonebot-plugin-tsugu-bangdream-bot",
|
||||
"author": "WindowsSov8forUs",
|
||||
"tags": [
|
||||
{
|
||||
"label": "tsugu",
|
||||
"color": "#ffee88"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_calc24",
|
||||
"project_link": "nonebot-plugin-calc24",
|
||||
"author": "ajdgg",
|
||||
"tags": [],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_nai3_bot",
|
||||
"project_link": "nonebot-plugin-nai3-bot",
|
||||
"author": "Alpaca4610",
|
||||
"tags": [
|
||||
{
|
||||
"label": "NovelAI",
|
||||
"color": "#52ea52"
|
||||
},
|
||||
{
|
||||
"label": "NAI3",
|
||||
"color": "#52ea52"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_dg_lab_play",
|
||||
"project_link": "nonebot-plugin-dg-lab-play",
|
||||
"author": "Ljzd-PRO",
|
||||
"tags": [
|
||||
{
|
||||
"label": "dg-lab",
|
||||
"color": "#fee99d"
|
||||
},
|
||||
{
|
||||
"label": "dg-lab-v3",
|
||||
"color": "#fee99d"
|
||||
},
|
||||
{
|
||||
"label": "t:game",
|
||||
"color": "#019bf1"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_authrespond",
|
||||
"project_link": "nonebot-plugin-authrespond",
|
||||
"author": "cubstaryow",
|
||||
"tags": [
|
||||
{
|
||||
"label": "黑名单",
|
||||
"color": "#e81616"
|
||||
},
|
||||
{
|
||||
"label": "cubplugins",
|
||||
"color": "#28a5d1"
|
||||
},
|
||||
{
|
||||
"label": "权限控制",
|
||||
"color": "#c75d59"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_shutdown_hook",
|
||||
"project_link": "nonebot-plugin-shutdown-hook",
|
||||
"author": "Sclock",
|
||||
"tags": [
|
||||
{
|
||||
"label": "工具",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_plus_one",
|
||||
"project_link": "nonebot-plugin-plus-one",
|
||||
"author": "yejue",
|
||||
"tags": [
|
||||
{
|
||||
"label": "复读姬",
|
||||
"color": "#df3cda"
|
||||
},
|
||||
{
|
||||
"label": "船新版本",
|
||||
"color": "#28d98e"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_aising",
|
||||
"project_link": "nonebot-plugin-aising",
|
||||
"author": "CCYellowStar2",
|
||||
"tags": [
|
||||
{
|
||||
"label": "唱歌",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "NeuCoSVC",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_qqshell",
|
||||
"project_link": "nonebot-plugin-qqshell",
|
||||
"author": "yejue",
|
||||
"tags": [
|
||||
{
|
||||
"label": "SSH",
|
||||
"color": "#cd2a8f"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_lynchpined",
|
||||
"project_link": "nonebot-plugin-lynchpined",
|
||||
"author": "050644zf",
|
||||
"tags": [],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_gakuenImasCalculator",
|
||||
"project_link": "nonebot-plugin-gakuenImasCalculator",
|
||||
"author": "ikarosf",
|
||||
"tags": [
|
||||
{
|
||||
"label": "游戏",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "工具",
|
||||
"color": "#ea5f52"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_beauty_rater",
|
||||
"project_link": "nonebot-plugin-beauty-rater",
|
||||
"author": "KomoriDev",
|
||||
"tags": [
|
||||
{
|
||||
"label": "🌐",
|
||||
"color": "#e7f4ff"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_xjie_weather",
|
||||
"project_link": "nonebot-plugin-xjie-weather",
|
||||
"author": "ajdgg",
|
||||
"tags": [],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_wwgachalogs",
|
||||
"project_link": "nonebot-plugin-wwgachalogs",
|
||||
"author": "BraveCowardp",
|
||||
"tags": [
|
||||
{
|
||||
"label": "鸣潮",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_saalc",
|
||||
"project_link": "nonebot-plugin-saalc",
|
||||
"author": "AzideCupric",
|
||||
"tags": [
|
||||
{
|
||||
"label": "SAA",
|
||||
"color": "#17a5fe"
|
||||
},
|
||||
{
|
||||
"label": "Alconna",
|
||||
"color": "#fe9517"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_easymarkdown",
|
||||
"project_link": "nonebot-plugin-easymarkdown",
|
||||
"author": "phquathi",
|
||||
"tags": [
|
||||
{
|
||||
"label": "code",
|
||||
"color": "#5284ea"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_multigpt",
|
||||
"project_link": "nonebot-plugin-multigpt",
|
||||
"author": "syagina",
|
||||
"tags": [
|
||||
{
|
||||
"label": "GPT",
|
||||
"color": "#52d7ea"
|
||||
},
|
||||
{
|
||||
"label": "PPT",
|
||||
"color": "#ea52c7"
|
||||
},
|
||||
{
|
||||
"label": "多模",
|
||||
"color": "#eac252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_mcsm",
|
||||
"project_link": "nonebot-plugin-mcsm",
|
||||
"author": "LiLuo-B",
|
||||
"tags": [
|
||||
{
|
||||
"label": "MCSM",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "应用托管",
|
||||
"color": "#5272ea"
|
||||
},
|
||||
{
|
||||
"label": "服务器管理",
|
||||
"color": "#4cc275"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_helldivers",
|
||||
"project_link": "nonebot-plugin-helldivers",
|
||||
"author": "SherkeyXD",
|
||||
"tags": [
|
||||
{
|
||||
"label": "helldivers",
|
||||
"color": "#ffd700"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_mahjong_hand_guess",
|
||||
"project_link": "nonebot-plugin-mahjong-hand-guess",
|
||||
"author": "ElainaFanBoy",
|
||||
"tags": [
|
||||
{
|
||||
"label": "game",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_asmr",
|
||||
"project_link": "nonebot-plugin-asmr",
|
||||
"author": "CCYellowStar2",
|
||||
"tags": [
|
||||
{
|
||||
"label": "asmr",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "音声",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_ntqq_restart",
|
||||
"project_link": "nonebot-plugin-ntqq-restart",
|
||||
"author": "kanbereina",
|
||||
"tags": [
|
||||
{
|
||||
"label": "llonebot",
|
||||
"color": "#f9e642"
|
||||
},
|
||||
{
|
||||
"label": "NTQQ",
|
||||
"color": "#ede9e9"
|
||||
},
|
||||
{
|
||||
"label": "Windows",
|
||||
"color": "#52aaea"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_eve_tool",
|
||||
"project_link": "nonebot-plugin-eve-tool",
|
||||
"author": "zifox666",
|
||||
"tags": [
|
||||
{
|
||||
"label": "game",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "eve",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_daily_task",
|
||||
"project_link": "nonebot-plugin-daily-task",
|
||||
"author": "WMGray",
|
||||
"tags": [
|
||||
{
|
||||
"label": "每日任务",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_biliforward",
|
||||
"project_link": "nonebot-plugin-biliforward",
|
||||
"author": "BraveCowardp",
|
||||
"tags": [
|
||||
{
|
||||
"label": "bilibili",
|
||||
"color": "#ea5252"
|
||||
},
|
||||
{
|
||||
"label": "哔哩哔哩",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_obastatus",
|
||||
"project_link": "nonebot-plugin-obastatus",
|
||||
"author": "Dongyanmio",
|
||||
"tags": [
|
||||
{
|
||||
"label": "BMCLAPI",
|
||||
"color": "#5f82ba"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_dcqq_relay",
|
||||
"project_link": "nonebot-plugin-dcqq-relay",
|
||||
"author": "Autuamn",
|
||||
"tags": [
|
||||
{
|
||||
"label": "消息互通",
|
||||
"color": "#428fdb"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
},
|
||||
{
|
||||
"module_name": "nonebot_plugin_ncupdate",
|
||||
"project_link": "nonebot-plugin-ncupdate",
|
||||
"author": "tianyisama",
|
||||
"tags": [
|
||||
{
|
||||
"label": "NapCat",
|
||||
"color": "#ea5252"
|
||||
}
|
||||
],
|
||||
"is_official": false
|
||||
}
|
||||
]
|
||||
|
787
envs/pydantic-v1/poetry.lock
generated
787
envs/pydantic-v1/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
895
envs/pydantic-v2/poetry.lock
generated
895
envs/pydantic-v2/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
377
envs/test/poetry.lock
generated
377
envs/test/poetry.lock
generated
@@ -1,14 +1,14 @@
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
description = "Reusable constraint types to use with typing.Annotated"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"},
|
||||
{file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
|
||||
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
|
||||
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -44,13 +44,13 @@ requests = ">=2.21,<3.0"
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2024.2.2"
|
||||
version = "2024.7.4"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
|
||||
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
|
||||
{file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
|
||||
{file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -165,63 +165,63 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.5.0"
|
||||
version = "7.5.4"
|
||||
description = "Code coverage measurement for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"},
|
||||
{file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"},
|
||||
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"},
|
||||
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"},
|
||||
{file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"},
|
||||
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"},
|
||||
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"},
|
||||
{file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"},
|
||||
{file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"},
|
||||
{file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"},
|
||||
{file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"},
|
||||
{file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"},
|
||||
{file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"},
|
||||
{file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"},
|
||||
{file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"},
|
||||
{file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-win32.whl", hash = "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8"},
|
||||
{file = "coverage-7.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-win32.whl", hash = "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806"},
|
||||
{file = "coverage-7.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-win32.whl", hash = "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b"},
|
||||
{file = "coverage-7.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-win32.whl", hash = "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f"},
|
||||
{file = "coverage-7.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-win32.whl", hash = "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace"},
|
||||
{file = "coverage-7.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d"},
|
||||
{file = "coverage-7.5.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5"},
|
||||
{file = "coverage-7.5.4.tar.gz", hash = "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -298,22 +298,22 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "7.1.0"
|
||||
version = "8.0.0"
|
||||
description = "Read metadata from Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
|
||||
{file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
|
||||
{file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"},
|
||||
{file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
zipp = ">=0.5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
perf = ["ipython"]
|
||||
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
|
||||
test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
@@ -514,13 +514,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "nonebot2"
|
||||
version = "2.2.1"
|
||||
version = "2.3.1"
|
||||
description = "An asynchronous python bot framework."
|
||||
optional = false
|
||||
python-versions = ">=3.8,<4.0"
|
||||
python-versions = "<4.0,>=3.9"
|
||||
files = [
|
||||
{file = "nonebot2-2.2.1-py3-none-any.whl", hash = "sha256:88f2bb456bf90922925bbe489a9effe3b09300f3aa50bfa75ee50d8a83d7330f"},
|
||||
{file = "nonebot2-2.2.1.tar.gz", hash = "sha256:fe57692300571b00724999238545d8d894523460e6835a11b326a2e1cdf98fc4"},
|
||||
{file = "nonebot2-2.3.1-py3-none-any.whl", hash = "sha256:91ac0abebe6c403c2443b11a49e065b79e6199460bdd61a32148366b35f81c4d"},
|
||||
{file = "nonebot2-2.3.1.tar.gz", hash = "sha256:ac5a1a1759f15310e9183b606ce6bdbe52a90287bf36a69201be548e23d41e6c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -560,13 +560,13 @@ typing-extensions = ">=4.0.0,<5.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.0"
|
||||
version = "24.1"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
||||
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
||||
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
|
||||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -586,109 +586,122 @@ testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.7.1"
|
||||
version = "2.8.2"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
|
||||
{file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
|
||||
{file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"},
|
||||
{file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.4.0"
|
||||
pydantic-core = "2.18.2"
|
||||
typing-extensions = ">=4.6.1"
|
||||
pydantic-core = "2.20.1"
|
||||
typing-extensions = [
|
||||
{version = ">=4.12.2", markers = "python_version >= \"3.13\""},
|
||||
{version = ">=4.6.1", markers = "python_version < \"3.13\""},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
email = ["email-validator (>=2.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.18.2"
|
||||
version = "2.20.1"
|
||||
description = "Core functionality for Pydantic validation and serialization"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"},
|
||||
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"},
|
||||
{file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"},
|
||||
{file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"},
|
||||
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"},
|
||||
{file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"},
|
||||
{file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"},
|
||||
{file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"},
|
||||
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"},
|
||||
{file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"},
|
||||
{file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"},
|
||||
{file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"},
|
||||
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"},
|
||||
{file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"},
|
||||
{file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"},
|
||||
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"},
|
||||
{file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"},
|
||||
{file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"},
|
||||
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"},
|
||||
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"},
|
||||
{file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"},
|
||||
{file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"},
|
||||
{file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"},
|
||||
{file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"},
|
||||
{file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"},
|
||||
{file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"},
|
||||
{file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"},
|
||||
{file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"},
|
||||
{file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"},
|
||||
{file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"},
|
||||
{file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"},
|
||||
{file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"},
|
||||
{file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"},
|
||||
{file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"},
|
||||
{file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"},
|
||||
{file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"},
|
||||
{file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"},
|
||||
{file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"},
|
||||
{file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"},
|
||||
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"},
|
||||
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"},
|
||||
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"},
|
||||
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"},
|
||||
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"},
|
||||
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"},
|
||||
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"},
|
||||
{file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"},
|
||||
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"},
|
||||
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"},
|
||||
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"},
|
||||
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"},
|
||||
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"},
|
||||
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"},
|
||||
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"},
|
||||
{file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"},
|
||||
{file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -707,13 +720,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.2.0"
|
||||
version = "8.2.2"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"},
|
||||
{file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"},
|
||||
{file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"},
|
||||
{file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -729,13 +742,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments
|
||||
|
||||
[[package]]
|
||||
name = "pytest-asyncio"
|
||||
version = "0.23.6"
|
||||
version = "0.23.7"
|
||||
description = "Pytest support for asyncio"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"},
|
||||
{file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"},
|
||||
{file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"},
|
||||
{file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -799,13 +812,13 @@ cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.31.0"
|
||||
version = "2.32.3"
|
||||
description = "Python HTTP for Humans."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
|
||||
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
|
||||
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
|
||||
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -831,24 +844,24 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.11.0"
|
||||
version = "4.12.2"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
|
||||
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
|
||||
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
|
||||
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
|
||||
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
|
||||
{file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
|
||||
{file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -859,13 +872,13 @@ zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "3.0.2"
|
||||
version = "3.0.3"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "werkzeug-3.0.2-py3-none-any.whl", hash = "sha256:3aac3f5da756f93030740bc235d3e09449efcf65f2f55e3602e1d851b8f48795"},
|
||||
{file = "werkzeug-3.0.2.tar.gz", hash = "sha256:e39b645a6ac92822588e7b39a692e7828724ceae0b0d702ef96701f90e70128d"},
|
||||
{file = "werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8"},
|
||||
{file = "werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1007,18 +1020,18 @@ multidict = ">=4.0"
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.18.1"
|
||||
version = "3.19.2"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"},
|
||||
{file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"},
|
||||
{file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"},
|
||||
{file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
|
@@ -17,8 +17,14 @@ from pydantic.fields import FieldInfo as PydanticFieldInfo
|
||||
|
||||
from nonebot.dependencies import Param, Dependent
|
||||
from nonebot.dependencies.utils import check_field_type
|
||||
from nonebot.typing import T_State, T_Handler, T_DependencyCache
|
||||
from nonebot.compat import FieldInfo, ModelField, PydanticUndefined, extract_field_info
|
||||
from nonebot.typing import (
|
||||
_STATE_FLAG,
|
||||
T_State,
|
||||
T_Handler,
|
||||
T_DependencyCache,
|
||||
origin_is_annotated,
|
||||
)
|
||||
from nonebot.utils import (
|
||||
get_name,
|
||||
run_sync,
|
||||
@@ -349,7 +355,9 @@ class StateParam(Param):
|
||||
cls, param: inspect.Parameter, allow_types: tuple[type[Param], ...]
|
||||
) -> Optional[Self]:
|
||||
# param type is T_State
|
||||
if param.annotation is T_State:
|
||||
if origin_is_annotated(
|
||||
get_origin(param.annotation)
|
||||
) and _STATE_FLAG in get_args(param.annotation):
|
||||
return cls()
|
||||
# legacy: param is named "state" and has no type annotation
|
||||
elif param.annotation == param.empty and param.name == "state":
|
||||
|
@@ -104,11 +104,23 @@ def is_none_type(type_: type[t.Any]) -> bool:
|
||||
def evaluate_forwardref(
|
||||
ref: t.ForwardRef, globalns: dict[str, t.Any], localns: dict[str, t.Any]
|
||||
) -> t.Any:
|
||||
return ref._evaluate(globalns, localns, frozenset())
|
||||
# Python 3.13/3.12.4+ made `recursive_guard` a kwarg,
|
||||
# so name it explicitly to avoid:
|
||||
# TypeError: ForwardRef._evaluate()
|
||||
# missing 1 required keyword-only argument: 'recursive_guard'
|
||||
return ref._evaluate(globalns, localns, recursive_guard=frozenset())
|
||||
|
||||
|
||||
# state
|
||||
T_State: TypeAlias = dict[t.Any, t.Any]
|
||||
# use annotated flag to avoid ForwardRef recreate generic type (py >= 3.11)
|
||||
class StateFlag:
|
||||
def __repr__(self) -> str:
|
||||
return "StateFlag()"
|
||||
|
||||
|
||||
_STATE_FLAG = StateFlag()
|
||||
|
||||
T_State: TypeAlias = t.Annotated[dict[t.Any, t.Any], _STATE_FLAG]
|
||||
"""事件处理状态 State 类型"""
|
||||
|
||||
_DependentCallable: TypeAlias = t.Union[
|
||||
|
980
poetry.lock
generated
980
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "nonebot2"
|
||||
version = "2.3.0"
|
||||
version = "2.3.2"
|
||||
description = "An asynchronous python bot framework."
|
||||
authors = ["yanyongyu <yyy@nonebot.dev>"]
|
||||
license = "MIT"
|
||||
|
@@ -7,6 +7,10 @@ async def get_bot(b: Bot) -> Bot:
|
||||
return b
|
||||
|
||||
|
||||
async def postpone_bot(b: "Bot") -> Bot:
|
||||
return b
|
||||
|
||||
|
||||
async def legacy_bot(bot):
|
||||
return bot
|
||||
|
||||
|
@@ -8,6 +8,10 @@ async def event(e: Event) -> Event:
|
||||
return e
|
||||
|
||||
|
||||
async def postpone_event(e: "Event") -> Event:
|
||||
return e
|
||||
|
||||
|
||||
async def legacy_event(event):
|
||||
return event
|
||||
|
||||
|
@@ -9,6 +9,10 @@ async def matcher(m: Matcher) -> Matcher:
|
||||
return m
|
||||
|
||||
|
||||
async def postpone_matcher(m: "Matcher") -> Matcher:
|
||||
return m
|
||||
|
||||
|
||||
async def legacy_matcher(matcher):
|
||||
return matcher
|
||||
|
||||
@@ -27,7 +31,7 @@ class BarMatcher(Matcher): ...
|
||||
|
||||
|
||||
async def union_matcher(
|
||||
m: Union[FooMatcher, BarMatcher]
|
||||
m: Union[FooMatcher, BarMatcher],
|
||||
) -> Union[FooMatcher, BarMatcher]:
|
||||
return m
|
||||
|
||||
|
@@ -25,6 +25,10 @@ async def state(x: T_State) -> T_State:
|
||||
return x
|
||||
|
||||
|
||||
async def postpone_state(x: "T_State") -> T_State:
|
||||
return x
|
||||
|
||||
|
||||
async def legacy_state(state):
|
||||
return state
|
||||
|
||||
|
@@ -129,6 +129,7 @@ async def test_bot(app: App):
|
||||
union_bot,
|
||||
legacy_bot,
|
||||
generic_bot,
|
||||
postpone_bot,
|
||||
not_legacy_bot,
|
||||
generic_bot_none,
|
||||
)
|
||||
@@ -138,6 +139,11 @@ async def test_bot(app: App):
|
||||
ctx.pass_params(bot=bot)
|
||||
ctx.should_return(bot)
|
||||
|
||||
async with app.test_dependent(postpone_bot, allow_types=[BotParam]) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
ctx.pass_params(bot=bot)
|
||||
ctx.should_return(bot)
|
||||
|
||||
async with app.test_dependent(legacy_bot, allow_types=[BotParam]) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
ctx.pass_params(bot=bot)
|
||||
@@ -188,6 +194,7 @@ async def test_event(app: App):
|
||||
legacy_event,
|
||||
event_message,
|
||||
generic_event,
|
||||
postpone_event,
|
||||
event_plain_text,
|
||||
not_legacy_event,
|
||||
generic_event_none,
|
||||
@@ -201,6 +208,10 @@ async def test_event(app: App):
|
||||
ctx.pass_params(event=fake_event)
|
||||
ctx.should_return(fake_event)
|
||||
|
||||
async with app.test_dependent(postpone_event, allow_types=[EventParam]) as ctx:
|
||||
ctx.pass_params(event=fake_event)
|
||||
ctx.should_return(fake_event)
|
||||
|
||||
async with app.test_dependent(legacy_event, allow_types=[EventParam]) as ctx:
|
||||
ctx.pass_params(event=fake_event)
|
||||
ctx.should_return(fake_event)
|
||||
@@ -273,6 +284,7 @@ async def test_state(app: App):
|
||||
legacy_state,
|
||||
command_start,
|
||||
regex_matched,
|
||||
postpone_state,
|
||||
not_legacy_state,
|
||||
command_whitespace,
|
||||
shell_command_args,
|
||||
@@ -302,6 +314,10 @@ async def test_state(app: App):
|
||||
ctx.pass_params(state=fake_state)
|
||||
ctx.should_return(fake_state)
|
||||
|
||||
async with app.test_dependent(postpone_state, allow_types=[StateParam]) as ctx:
|
||||
ctx.pass_params(state=fake_state)
|
||||
ctx.should_return(fake_state)
|
||||
|
||||
async with app.test_dependent(legacy_state, allow_types=[StateParam]) as ctx:
|
||||
ctx.pass_params(state=fake_state)
|
||||
ctx.should_return(fake_state)
|
||||
@@ -414,6 +430,7 @@ async def test_matcher(app: App):
|
||||
union_matcher,
|
||||
legacy_matcher,
|
||||
generic_matcher,
|
||||
postpone_matcher,
|
||||
not_legacy_matcher,
|
||||
generic_matcher_none,
|
||||
)
|
||||
@@ -425,6 +442,10 @@ async def test_matcher(app: App):
|
||||
ctx.pass_params(matcher=fake_matcher)
|
||||
ctx.should_return(fake_matcher)
|
||||
|
||||
async with app.test_dependent(postpone_matcher, allow_types=[MatcherParam]) as ctx:
|
||||
ctx.pass_params(matcher=fake_matcher)
|
||||
ctx.should_return(fake_matcher)
|
||||
|
||||
async with app.test_dependent(legacy_matcher, allow_types=[MatcherParam]) as ctx:
|
||||
ctx.pass_params(matcher=fake_matcher)
|
||||
ctx.should_return(fake_matcher)
|
||||
|
@@ -34,7 +34,7 @@ NoneBot 采用了一套自行定义的依赖注入系统,可以让事件的处
|
||||
|
||||
#### 什么是依赖注入
|
||||
|
||||
[**『依赖注入』**](https://zh.m.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC)意思是,在编程中,有一种方法可以让你的代码声明它工作和使用所需要的东西,即**『依赖』**。
|
||||
[**『依赖注入』**](https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC)意思是,在编程中,有一种方法可以让你的代码声明它工作和使用所需要的东西,即**『依赖』**。
|
||||
|
||||
系统(在这里是指 NoneBot)将负责做任何需要的事情,为你的代码提供这些必要依赖(即**『注入』**依赖性)
|
||||
|
||||
|
@@ -154,7 +154,7 @@ rule = keyword("hello", "hi")
|
||||
```python
|
||||
from nonebot import on_keyword
|
||||
|
||||
matcher = on_keyword("hello", "hi")
|
||||
matcher = on_keyword({"hello", "hi"})
|
||||
```
|
||||
|
||||
### `command`
|
||||
|
@@ -487,7 +487,7 @@ matcher = on_alconna(
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna import Match, on_alconna
|
||||
from nonebot_plugin_alconna.builtins.plugins.discord import DiscordSlashExtension
|
||||
from nonebot_plugin_alconna.builtins.extensions.discord import DiscordSlashExtension
|
||||
|
||||
|
||||
alc = Alconna(
|
||||
|
@@ -150,7 +150,7 @@ async def test_weather(app: App):
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message("/天气 北京"),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
```
|
||||
|
||||
@@ -165,7 +165,7 @@ async def test_weather(app: App):
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message("/天气 北京"),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
async with app.test_matcher(weather) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
@@ -184,7 +184,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@@ -56,7 +56,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -95,7 +95,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -131,7 +131,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -202,7 +202,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -264,7 +264,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@@ -58,7 +58,7 @@ const navbar = {
|
||||
docId: "developer/plugin-publishing",
|
||||
},
|
||||
{ label: "社区", type: "doc", docId: "community/contact" },
|
||||
{ label: "开源之夏", type: "doc", docId: "ospp/2023" },
|
||||
{ label: "开源之夏", type: "doc", docId: "ospp/2024" },
|
||||
{ label: "商店", to: "/store/plugins" },
|
||||
{ label: "更新日志", to: "/changelog" },
|
||||
{ label: "论坛", href: "https://discussions.nonebot.dev" },
|
||||
@@ -251,6 +251,12 @@ const siteConfig = {
|
||||
src: "https://cdn.wwads.cn/js/makemoney.js",
|
||||
async: true,
|
||||
},
|
||||
// uwu logo
|
||||
{
|
||||
type: "text/javascript",
|
||||
charset: "UTF-8",
|
||||
src: "/uwu.js",
|
||||
},
|
||||
],
|
||||
|
||||
presets: [
|
||||
|
@@ -47,6 +47,7 @@ function HomeHero(): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="home-hero">
|
||||
<img src="/img/uwu.svg" alt="uwu" className="home-hero-uwu" />
|
||||
<img src={logo!.src} alt={logo!.alt} className="home-hero-logo" />
|
||||
<h1 className="home-hero-title">
|
||||
<span className="text-primary">None</span>
|
||||
|
@@ -39,3 +39,16 @@
|
||||
@apply inline-block !max-w-[600px];
|
||||
}
|
||||
}
|
||||
|
||||
.home-hero-uwu {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
[data-uwu="true"] .home-hero-uwu {
|
||||
@apply block max-w-xs;
|
||||
}
|
||||
|
||||
[data-uwu="true"] .home-hero-logo,
|
||||
[data-uwu="true"] .home-hero-title {
|
||||
@apply hidden;
|
||||
}
|
||||
|
@@ -5,6 +5,86 @@ toc_max_heading_level: 2
|
||||
|
||||
# 更新日志
|
||||
|
||||
## v2.3.2
|
||||
|
||||
### 🐛 Bug 修复
|
||||
|
||||
- Fix: 修复 ForwardRef eval 时参数 recursive_guard 缺失 [@he0119](https://github.com/he0119) ([#2778](https://github.com/nonebot/nonebot2/pull/2778))
|
||||
|
||||
### 📝 文档
|
||||
|
||||
- Docs: 修改导航栏开源之夏链接 [@KomoriDev](https://github.com/KomoriDev) ([#2798](https://github.com/nonebot/nonebot2/pull/2798))
|
||||
- Docs: `on_keyword` 参数类型错误 [@TaskManagerOL](https://github.com/TaskManagerOL) ([#2795](https://github.com/nonebot/nonebot2/pull/2795))
|
||||
- Docs: 修复单元测试示例代码 [@mobyw](https://github.com/mobyw) ([#2741](https://github.com/nonebot/nonebot2/pull/2741))
|
||||
- Docs: 修改依赖注入定义链接 [@Weltolk](https://github.com/Weltolk) ([#2733](https://github.com/nonebot/nonebot2/pull/2733))
|
||||
|
||||
### 🍻 插件发布
|
||||
|
||||
- Plugin: 指令更新NapCat [@noneflow](https://github.com/noneflow) ([#2791](https://github.com/nonebot/nonebot2/pull/2791))
|
||||
- Plugin: QQ群-Discord 互通 [@noneflow](https://github.com/noneflow) ([#2788](https://github.com/nonebot/nonebot2/pull/2788))
|
||||
- Plugin: nonebot_plugin_obastatus [@noneflow](https://github.com/noneflow) ([#2780](https://github.com/nonebot/nonebot2/pull/2780))
|
||||
- Plugin: b站消息转发 [@noneflow](https://github.com/noneflow) ([#2785](https://github.com/nonebot/nonebot2/pull/2785))
|
||||
- Plugin: Daily Task [@noneflow](https://github.com/noneflow) ([#2769](https://github.com/nonebot/nonebot2/pull/2769))
|
||||
- Plugin: EVE ONLINE 多功能机器人
|
||||
版本 - v0.2.3
|
||||
[@noneflow](https://github.com/noneflow) ([#2782](https://github.com/nonebot/nonebot2/pull/2782))
|
||||
- Plugin: NTQQ自动登录/断连重启 [@noneflow](https://github.com/noneflow) ([#2786](https://github.com/nonebot/nonebot2/pull/2786))
|
||||
- Plugin: asmr [@noneflow](https://github.com/noneflow) ([#2775](https://github.com/nonebot/nonebot2/pull/2775))
|
||||
- Plugin: 日麻猜手牌小游戏 [@noneflow](https://github.com/noneflow) ([#2777](https://github.com/nonebot/nonebot2/pull/2777))
|
||||
- Plugin: 绝地潜兵信息查询小助手 [@noneflow](https://github.com/noneflow) ([#2772](https://github.com/nonebot/nonebot2/pull/2772))
|
||||
- Plugin: MCSM小助手 [@noneflow](https://github.com/noneflow) ([#2773](https://github.com/nonebot/nonebot2/pull/2773))
|
||||
- Plugin: 多模态AI工具 [@noneflow](https://github.com/noneflow) ([#2758](https://github.com/nonebot/nonebot2/pull/2758))
|
||||
- Plugin: nonebot-plugin-easymarkdown [@noneflow](https://github.com/noneflow) ([#2767](https://github.com/nonebot/nonebot2/pull/2767))
|
||||
- Plugin: 峯驰外包 [@noneflow](https://github.com/noneflow) ([#2765](https://github.com/nonebot/nonebot2/pull/2765))
|
||||
- Plugin: 鸣潮抽卡记录分析 [@noneflow](https://github.com/noneflow) ([#2763](https://github.com/nonebot/nonebot2/pull/2763))
|
||||
- Plugin: nonebot-plugin-xjie-weather [@noneflow](https://github.com/noneflow) ([#2756](https://github.com/nonebot/nonebot2/pull/2756))
|
||||
- Plugin: 颜值评分 [@noneflow](https://github.com/noneflow) ([#2752](https://github.com/nonebot/nonebot2/pull/2752))
|
||||
- Plugin: 学园偶像大师算分插件 [@noneflow](https://github.com/noneflow) ([#2750](https://github.com/nonebot/nonebot2/pull/2750))
|
||||
- Plugin: nonebot-plugin-lynchpined [@noneflow](https://github.com/noneflow) ([#2748](https://github.com/nonebot/nonebot2/pull/2748))
|
||||
- Plugin: QQShell [@noneflow](https://github.com/noneflow) ([#2745](https://github.com/nonebot/nonebot2/pull/2745))
|
||||
- Plugin: ai唱歌 [@noneflow](https://github.com/noneflow) ([#2743](https://github.com/nonebot/nonebot2/pull/2743))
|
||||
- Plugin: 复读姬+1 PlusOne [@noneflow](https://github.com/noneflow) ([#2732](https://github.com/nonebot/nonebot2/pull/2732))
|
||||
- Plugin: 高优先级关闭信号钩子插件 [@noneflow](https://github.com/noneflow) ([#2737](https://github.com/nonebot/nonebot2/pull/2737))
|
||||
- Plugin: 插件响应鉴权 [@noneflow](https://github.com/noneflow) ([#2727](https://github.com/nonebot/nonebot2/pull/2727))
|
||||
- Plugin: DG-Lab-Play [@noneflow](https://github.com/noneflow) ([#2729](https://github.com/nonebot/nonebot2/pull/2729))
|
||||
|
||||
## v2.3.1
|
||||
|
||||
### 🐛 Bug 修复
|
||||
|
||||
- Fix: State ForwardRef 检测错误 [@yanyongyu](https://github.com/yanyongyu) ([#2698](https://github.com/nonebot/nonebot2/pull/2698))
|
||||
|
||||
### 📝 文档
|
||||
|
||||
- Docs: 修正 匹配扩展 中的示例 [@KomoriDev](https://github.com/KomoriDev) ([#2722](https://github.com/nonebot/nonebot2/pull/2722))
|
||||
- Docs: 更新 Mirai 适配器说明 [@RF-Tar-Railt](https://github.com/RF-Tar-Railt) ([#2715](https://github.com/nonebot/nonebot2/pull/2715))
|
||||
- Docs: 添加 Tailchat 适配器说明 [@eya46](https://github.com/eya46) ([#2694](https://github.com/nonebot/nonebot2/pull/2694))
|
||||
- Docs: 添加 uwu logo [@StarHeartHunt](https://github.com/StarHeartHunt) ([#2689](https://github.com/nonebot/nonebot2/pull/2689))
|
||||
|
||||
### 💫 杂项
|
||||
|
||||
- Plugin: 移除已在 PyPI 上删除的 `covid` 插件和 `molar-mass` 插件 [@NCBM](https://github.com/NCBM) ([#2712](https://github.com/nonebot/nonebot2/pull/2712))
|
||||
|
||||
### 🍻 插件发布
|
||||
|
||||
- Plugin: 自定义人格和AI绘图的混合聊天BOT [@noneflow](https://github.com/noneflow) ([#2724](https://github.com/nonebot/nonebot2/pull/2724))
|
||||
- Plugin: nonebot-plugin-calc24 [@noneflow](https://github.com/noneflow) ([#2721](https://github.com/nonebot/nonebot2/pull/2721))
|
||||
- Plugin: nonebot-plugin-tsugu-bangdream-bot [@noneflow](https://github.com/noneflow) ([#2719](https://github.com/nonebot/nonebot2/pull/2719))
|
||||
- Plugin: 科大讯飞星火大语言模型官方API聊天机器人插件 [@noneflow](https://github.com/noneflow) ([#2717](https://github.com/nonebot/nonebot2/pull/2717))
|
||||
- Plugin: nonebot_plugin_valve_server_query [@noneflow](https://github.com/noneflow) ([#2711](https://github.com/nonebot/nonebot2/pull/2711))
|
||||
- Plugin: 库洛游戏信息 [@noneflow](https://github.com/noneflow) ([#2706](https://github.com/nonebot/nonebot2/pull/2706))
|
||||
- Plugin: BanG Dream! Tsugu Frontend [@noneflow](https://github.com/noneflow) ([#2708](https://github.com/nonebot/nonebot2/pull/2708))
|
||||
- Plugin: 神秘学助手 [@noneflow](https://github.com/noneflow) ([#2700](https://github.com/nonebot/nonebot2/pull/2700))
|
||||
- Plugin: nonebot-plugin-furryfusion [@noneflow](https://github.com/noneflow) ([#2705](https://github.com/nonebot/nonebot2/pull/2705))
|
||||
- Plugin: nonebot-plugin-RanFurryPic [@noneflow](https://github.com/noneflow) ([#2703](https://github.com/nonebot/nonebot2/pull/2703))
|
||||
- Plugin: with_ai_agents [@noneflow](https://github.com/noneflow) ([#2697](https://github.com/nonebot/nonebot2/pull/2697))
|
||||
- Plugin: 番剧下载 [@noneflow](https://github.com/noneflow) ([#2691](https://github.com/nonebot/nonebot2/pull/2691))
|
||||
|
||||
### 🍻 适配器发布
|
||||
|
||||
- Adapter: Mirai [@noneflow](https://github.com/noneflow) ([#2714](https://github.com/nonebot/nonebot2/pull/2714))
|
||||
- Adapter: Tailchat [@noneflow](https://github.com/noneflow) ([#2693](https://github.com/nonebot/nonebot2/pull/2693))
|
||||
|
||||
## v2.3.0
|
||||
|
||||
### 💥 破坏性变更
|
||||
|
1
website/static/img/uwu.svg
Normal file
1
website/static/img/uwu.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 50 KiB |
2
website/static/uwu.js
Normal file
2
website/static/uwu.js
Normal file
@@ -0,0 +1,2 @@
|
||||
if (location.search.includes("?uwu"))
|
||||
document.documentElement.setAttribute("data-uwu", "true");
|
@@ -1,44 +0,0 @@
|
||||
---
|
||||
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))
|
@@ -1,44 +0,0 @@
|
||||
---
|
||||
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))
|
@@ -34,7 +34,7 @@ NoneBot 采用了一套自行定义的依赖注入系统,可以让事件的处
|
||||
|
||||
#### 什么是依赖注入
|
||||
|
||||
[**『依赖注入』**](https://zh.m.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC)意思是,在编程中,有一种方法可以让你的代码声明它工作和使用所需要的东西,即**『依赖』**。
|
||||
[**『依赖注入』**](https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC)意思是,在编程中,有一种方法可以让你的代码声明它工作和使用所需要的东西,即**『依赖』**。
|
||||
|
||||
系统(在这里是指 NoneBot)将负责做任何需要的事情,为你的代码提供这些必要依赖(即**『注入』**依赖性)
|
||||
|
||||
|
@@ -154,7 +154,7 @@ rule = keyword("hello", "hi")
|
||||
```python
|
||||
from nonebot import on_keyword
|
||||
|
||||
matcher = on_keyword("hello", "hi")
|
||||
matcher = on_keyword({"hello", "hi"})
|
||||
```
|
||||
|
||||
### `command`
|
||||
|
@@ -150,7 +150,7 @@ async def test_weather(app: App):
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message("/天气 北京"),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
```
|
||||
|
||||
@@ -165,7 +165,7 @@ async def test_weather(app: App):
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message("/天气 北京"),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
async with app.test_matcher(weather) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
@@ -184,7 +184,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@@ -56,7 +56,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -95,7 +95,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -131,7 +131,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -202,7 +202,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -264,7 +264,7 @@ def make_event(message: str = "") -> MessageEvent:
|
||||
time=datetime.now(),
|
||||
self_id="test",
|
||||
message=Message(message),
|
||||
user=User(user_id=123456789),
|
||||
user=User(id="user"),
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@@ -8,7 +8,7 @@ slug: /
|
||||
|
||||
NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架(下称 NoneBot),它基于 Python 的类型注解和异步优先特性(兼容同步),能够为你的需求实现提供便捷灵活的支持。同时,NoneBot 拥有大量的开发者为其开发插件,用户无需编写任何代码,仅需完成环境配置及插件安装,就可以正常使用 NoneBot。
|
||||
|
||||
需要注意的是,NoneBot 仅支持 **Python 3.8 以上版本**
|
||||
需要注意的是,NoneBot 仅支持 **Python 3.9 以上版本**
|
||||
|
||||
## 特色
|
||||
|
||||
@@ -34,7 +34,7 @@ NoneBot 采用了一套自行定义的依赖注入系统,可以让事件的处
|
||||
|
||||
#### 什么是依赖注入
|
||||
|
||||
[**『依赖注入』**](https://zh.m.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC)意思是,在编程中,有一种方法可以让你的代码声明它工作和使用所需要的东西,即**『依赖』**。
|
||||
[**『依赖注入』**](https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC)意思是,在编程中,有一种方法可以让你的代码声明它工作和使用所需要的东西,即**『依赖』**。
|
||||
|
||||
系统(在这里是指 NoneBot)将负责做任何需要的事情,为你的代码提供这些必要依赖(即**『注入』**依赖性)
|
||||
|
@@ -89,7 +89,7 @@ async def _(bot): ... # 兼容性处理
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="3.9" label="Python 3.9">
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
@@ -127,7 +127,7 @@ async def _(event): ... # 兼容性处理
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="3.9" label="Python 3.9">
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
@@ -191,7 +191,7 @@ async def _(e: ActionFailed | NetworkError): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="3.9" label="Python 3.9">
|
||||
|
||||
```python {6,9}
|
||||
from typing import Union
|
||||
@@ -216,8 +216,8 @@ async def _(e: Union[ActionFailed, NetworkError]): ...
|
||||
|
||||
子依赖使用 `Depends` 标记进行定义,其参数即依赖的函数或可调用对象,同样会被解析为 `Dependent` 对象,将会在依赖注入期间执行。我们来看一个例子:
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {5,15}
|
||||
from typing import Annotated
|
||||
@@ -239,7 +239,7 @@ async def _(event: Annotated[Event, Depends(check)]):
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3,13}
|
||||
from nonebot import on_command
|
||||
@@ -287,8 +287,8 @@ async def _():
|
||||
|
||||
NoneBot 在执行子依赖时,会将其返回值缓存起来。当我们在使用子依赖时,`Depends` 具有一个参数 `use_cache`,默认为 `True`。此时在事件处理流程中,多次使用同一个子依赖时,将会使用缓存中的结果而不会重复执行。这在很多情景中非常有用,例如:
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {7}
|
||||
import random
|
||||
@@ -302,7 +302,7 @@ async def _(x: Annotated[int, Depends(random_result)]):
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {6}
|
||||
import random
|
||||
@@ -319,8 +319,8 @@ async def _(x: int = Depends(random_result)):
|
||||
|
||||
此时,在同一事件处理流程中,这个随机函数的返回值将会保持一致。如果我们希望每次都重新执行子依赖,可以将 `use_cache` 设置为 `False`。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {7}
|
||||
import random
|
||||
@@ -334,7 +334,7 @@ async def _(x: Annotated[int, Depends(random_result, use_cache=False)]):
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {6}
|
||||
import random
|
||||
@@ -357,8 +357,8 @@ async def _(x: int = Depends(random_result, use_cache=False)):
|
||||
|
||||
在依赖注入系统中,我们可以对子依赖的返回值进行自动类型转换与校验。这个功能由 Pydantic 支持,因此我们通过参数类型注解自动使用 Pydantic 支持的类型转换。例如:
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {6,9}
|
||||
from typing import Annotated
|
||||
@@ -374,7 +374,7 @@ async def _(user_id: Annotated[int, Depends(get_user_id, validate=True)]):
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {4,7}
|
||||
from nonebot.params import Depends
|
||||
@@ -392,8 +392,8 @@ async def _(user_id: int = Depends(get_user_id, validate=True)):
|
||||
|
||||
在进行类型自动转换的同时,Pydantic 还支持对数据进行更多的限制,如:大于、小于、长度等。使用方法如下:
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {7,10}
|
||||
from typing import Annotated
|
||||
@@ -410,7 +410,7 @@ async def _(user_id: Annotated[int, Depends(get_user_id, validate=Field(gt=100))
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {5,8}
|
||||
from pydantic import Field
|
||||
@@ -431,8 +431,8 @@ async def _(user_id: int = Depends(get_user_id, validate=Field(gt=100))):
|
||||
|
||||
在前面的事例中,我们使用了函数作为子依赖。实际上,我们还可以使用类作为依赖。当我们在实例化一个类的时候,其实我们就在调用它,类本身也是一个可调用对象。例如:
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {16}
|
||||
from typing import Annotated
|
||||
@@ -455,7 +455,7 @@ async def _(data: Annotated[ClassDependency, Depends(ClassDependency)]):
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {15}
|
||||
from dataclasses import dataclass
|
||||
@@ -481,8 +481,8 @@ async def _(data: ClassDependency = Depends(ClassDependency)):
|
||||
|
||||
可以看到,我们使用 `dataclass` 定义了一个类。由于这个类的 `__init__` 方法可以被依赖注入系统解析,因此,我们可以将其作为子依赖进行声明。特别地,对于类依赖,`Depends` 的参数可以为空,NoneBot 将会使用参数的类型注解进行解析与推断:
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
@@ -492,7 +492,7 @@ async def _(data: Annotated[ClassDependency, Depends()]):
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python
|
||||
async def _(data: ClassDependency = Depends()):
|
||||
@@ -510,11 +510,12 @@ NoneBot 的依赖注入支持依赖项在事件处理流程结束后进行一些
|
||||
|
||||
我们可以看下述代码段, 使用 `httpx.AsyncClient` 异步网络 IO,并在事件处理流程中共用一个 client:
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {15}
|
||||
from typing import Annotated, AsyncGenerator
|
||||
from typing import Annotated
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
import httpx
|
||||
from nonebot.params import Depends
|
||||
@@ -533,10 +534,10 @@ async def _(x: Annotated[httpx.AsyncClient, Depends(get_client)]):
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {15}
|
||||
from typing import AsyncGenerator
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
import httpx
|
||||
from nonebot.params import Depends
|
||||
@@ -566,11 +567,10 @@ async def _(x: httpx.AsyncClient = Depends(get_client)):
|
||||
在 Python 里,为类定义 `__call__` 方法就可以使得这个类的实例成为一个可调用对象。因此,我们也可以将定义了 `__call__` 方法的类的实例作为依赖。事实上,NoneBot 的[内置响应规则](./matcher.md#内置响应规则)就广泛使用了这种方式,以 `is_type` 规则为例:
|
||||
|
||||
```python
|
||||
from typing import Type
|
||||
from nonebot.adapters import Event
|
||||
|
||||
class IsTypeRule:
|
||||
def __init__(self, *types: Type[Event]):
|
||||
def __init__(self, *types: type[Event]):
|
||||
self.types = types
|
||||
|
||||
async def __call__(self, event: Event) -> bool:
|
||||
@@ -587,8 +587,8 @@ class IsTypeRule:
|
||||
|
||||
获取当前事件的类型。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -598,7 +598,7 @@ async def _(foo: Annotated[str, EventType()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import EventType
|
||||
@@ -613,8 +613,8 @@ async def _(foo: str = EventType()): ...
|
||||
|
||||
获取当前事件的消息。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {5}
|
||||
from typing import Annotated
|
||||
@@ -625,7 +625,7 @@ async def _(foo: Annotated[Message, EventMessage()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {4}
|
||||
from nonebot.adapters import Message
|
||||
@@ -641,8 +641,8 @@ async def _(foo: Message = EventMessage()): ...
|
||||
|
||||
获取当前事件的消息纯文本部分。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -652,7 +652,7 @@ async def _(foo: Annotated[str, EventPlainText()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import EventPlainText
|
||||
@@ -667,8 +667,8 @@ async def _(foo: str = EventPlainText()): ...
|
||||
|
||||
获取当前事件是否与机器人相关。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -678,7 +678,7 @@ async def _(foo: Annotated[bool, EventToMe()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import EventToMe
|
||||
@@ -693,8 +693,8 @@ async def _(foo: bool = EventToMe()): ...
|
||||
|
||||
获取当前命令型消息的元组形式命令名。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -704,13 +704,12 @@ async def _(foo: Annotated[tuple[str, ...], Command()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {4}
|
||||
from typing import Tuple
|
||||
from nonebot.params import Command
|
||||
|
||||
async def _(foo: Tuple[str, ...] = Command()): ...
|
||||
async def _(foo: tuple[str, ...] = Command()): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -724,8 +723,8 @@ async def _(foo: Tuple[str, ...] = Command()): ...
|
||||
|
||||
获取当前命令型消息的文本形式命令名。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -735,7 +734,7 @@ async def _(foo: Annotated[str, RawCommand()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import RawCommand
|
||||
@@ -754,8 +753,8 @@ async def _(foo: str = RawCommand()): ...
|
||||
|
||||
获取命令型消息命令后跟随的参数。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {5}
|
||||
from typing import Annotated
|
||||
@@ -766,7 +765,7 @@ async def _(foo: Annotated[Message, CommandArg()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {4}
|
||||
from nonebot.adapters import Message
|
||||
@@ -786,8 +785,8 @@ async def _(foo: Message = CommandArg()): ...
|
||||
|
||||
获取命令型消息命令前缀。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -797,7 +796,7 @@ async def _(foo: Annotated[str, CommandStart()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import CommandStart
|
||||
@@ -816,8 +815,8 @@ async def _(foo: str = CommandStart()): ...
|
||||
|
||||
获取命令型消息命令与参数间空白符。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -827,7 +826,7 @@ async def _(foo: Annotated[str, CommandWhitespace()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import CommandWhitespace
|
||||
@@ -854,10 +853,16 @@ from typing import Annotated
|
||||
from nonebot.params import ShellCommandArgs
|
||||
|
||||
async def _(foo: Annotated[list[str | MessageSegment], ShellCommandArgv()]): ...
|
||||
```
|
||||
|
||||
```python {4}
|
||||
from nonebot.params import ShellCommandArgs
|
||||
|
||||
async def _(foo: list[str | MessageSegment] = ShellCommandArgv()): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.9" label="Python 3.9+">
|
||||
<TabItem value="3.9" label="Python 3.9">
|
||||
|
||||
```python {4}
|
||||
from typing import Union, Annotated
|
||||
@@ -866,14 +871,11 @@ from nonebot.params import ShellCommandArgs
|
||||
async def _(foo: Annotated[list[Union[str, MessageSegment]], ShellCommandArgv()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
|
||||
```python {4}
|
||||
from typing import List, Union
|
||||
from typing import Union
|
||||
from nonebot.params import ShellCommandArgs
|
||||
|
||||
async def _(foo: List[Union[str, MessageSegment]] = ShellCommandArgv()): ...
|
||||
async def _(foo: list[Union[str, MessageSegment]] = ShellCommandArgv()): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -889,8 +891,8 @@ async def _(foo: List[Union[str, MessageSegment]] = ShellCommandArgv()): ...
|
||||
由于 `ArgumentParser` 在解析到 `--help` 参数时也会抛出异常,这种情况下错误码为 `0` 且错误信息即为帮助信息。
|
||||
:::
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {14,22}
|
||||
from typing import Annotated
|
||||
@@ -919,7 +921,7 @@ async def _(foo: Annotated[Namespace, ShellCommandArgs()]):
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {12,20}
|
||||
from nonebot import on_shell_command
|
||||
@@ -952,8 +954,8 @@ async def _(foo: Namespace = ShellCommandArgs()):
|
||||
|
||||
获取正则匹配结果的对象。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {5}
|
||||
from re import Match
|
||||
@@ -964,10 +966,10 @@ async def _(foo: Annotated[Match[str], RegexMatched()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {4}
|
||||
from typing import Match
|
||||
from re import Match
|
||||
from nonebot.params import RegexMatched
|
||||
|
||||
async def _(foo: Match[str] = RegexMatched()): ...
|
||||
@@ -980,8 +982,8 @@ async def _(foo: Match[str] = RegexMatched()): ...
|
||||
|
||||
获取正则匹配结果的文本。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -991,7 +993,7 @@ async def _(foo: Annotated[str, RegexStr()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import RegexStr
|
||||
@@ -1006,8 +1008,8 @@ async def _(foo: str = RegexStr()): ...
|
||||
|
||||
获取正则匹配结果的 group 元组。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Any, Annotated
|
||||
@@ -1017,13 +1019,13 @@ async def _(foo: Annotated[tuple[Any, ...], RegexGroup()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {4}
|
||||
from typing import Tuple, Any
|
||||
from typing import Any
|
||||
from nonebot.params import RegexGroup
|
||||
|
||||
async def _(foo: Tuple[Any, ...] = RegexGroup()): ...
|
||||
async def _(foo: tuple[Any, ...] = RegexGroup()): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -1033,8 +1035,8 @@ async def _(foo: Tuple[Any, ...] = RegexGroup()): ...
|
||||
|
||||
获取正则匹配结果的 group 字典。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Any, Annotated
|
||||
@@ -1044,13 +1046,13 @@ async def _(foo: Annotated[dict[str, Any], RegexDict()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {4}
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
from nonebot.params import RegexDict
|
||||
|
||||
async def _(foo: Dict[str, Any] = RegexDict()): ...
|
||||
async def _(foo: dict[str, Any] = RegexDict()): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -1060,8 +1062,8 @@ async def _(foo: Dict[str, Any] = RegexDict()): ...
|
||||
|
||||
获取触发响应器的消息前缀字符串。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -1071,7 +1073,7 @@ async def _(foo: Annotated[str, Startswith()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import Startswith
|
||||
@@ -1086,8 +1088,8 @@ async def _(foo: str = Startswith()): ...
|
||||
|
||||
获取触发响应器的消息后缀字符串。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -1097,7 +1099,7 @@ async def _(foo: Annotated[str, Endswith()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import Endswith
|
||||
@@ -1112,8 +1114,8 @@ async def _(foo: str = Endswith()): ...
|
||||
|
||||
获取触发响应器的消息字符串。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -1123,7 +1125,7 @@ async def _(foo: Annotated[str, Fullmatch()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import Fullmatch
|
||||
@@ -1138,8 +1140,8 @@ async def _(foo: str = Fullmatch()): ...
|
||||
|
||||
获取触发响应器的关键字字符串。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {4}
|
||||
from typing import Annotated
|
||||
@@ -1149,7 +1151,7 @@ async def _(foo: Annotated[str, Keyword()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {3}
|
||||
from nonebot.params import Keyword
|
||||
@@ -1164,8 +1166,8 @@ async def _(foo: str = Keyword()): ...
|
||||
|
||||
获取某次 `receive` 接收的事件。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {7}
|
||||
from typing import Annotated
|
||||
@@ -1178,7 +1180,7 @@ async def _(foo: Annotated[Event, Received("id")]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {5}
|
||||
from nonebot.adapters import Event
|
||||
@@ -1195,8 +1197,8 @@ async def _(foo: Event = Received("id")): ...
|
||||
|
||||
获取最近一次 `receive` 接收的事件。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {7}
|
||||
from typing import Annotated
|
||||
@@ -1209,7 +1211,7 @@ async def _(foo: Annotated[Event, LastReceived()]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {5}
|
||||
from nonebot.adapters import Event
|
||||
@@ -1226,8 +1228,8 @@ async def _(foo: Event = LastReceived()): ...
|
||||
|
||||
获取某次 `got` 接收的参数。如果 `Arg` 参数留空,则使用函数的参数名作为要获取的参数。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {7,8}
|
||||
from typing import Annotated
|
||||
@@ -1241,7 +1243,7 @@ async def _(foo: Annotated[Message, Arg("key")]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {5,6}
|
||||
from nonebot.params import Arg
|
||||
@@ -1259,8 +1261,8 @@ async def _(foo: Message = Arg("key")): ...
|
||||
|
||||
获取某次 `got` 接收的参数,并转换为字符串。如果 `Arg` 参数留空,则使用函数的参数名作为要获取的参数。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {6,7}
|
||||
from typing import Annotated
|
||||
@@ -1273,7 +1275,7 @@ async def _(foo: Annotated[str, ArgStr("key")]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {4,5}
|
||||
from nonebot.params import ArgStr
|
||||
@@ -1290,8 +1292,8 @@ async def _(foo: str = ArgStr("key")): ...
|
||||
|
||||
获取某次 `got` 接收的参数的纯文本部分。如果 `Arg` 参数留空,则使用函数的参数名作为要获取的参数。
|
||||
|
||||
<Tabs groupId="python">
|
||||
<TabItem value="3.9" label="Python 3.9+" default>
|
||||
<Tabs groupId="annotated">
|
||||
<TabItem value="annotated" label="Use Annotated" default>
|
||||
|
||||
```python {6,7}
|
||||
from typing import Annotated
|
||||
@@ -1304,7 +1306,7 @@ async def _(foo: Annotated[str, ArgPlainText("key")]): ...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="3.8" label="Python 3.8+">
|
||||
<TabItem value="no-annotated" label="Without Annotated">
|
||||
|
||||
```python {4,5}
|
||||
from nonebot.params import ArgPlainText
|
@@ -154,7 +154,7 @@ rule = keyword("hello", "hi")
|
||||
```python
|
||||
from nonebot import on_keyword
|
||||
|
||||
matcher = on_keyword("hello", "hi")
|
||||
matcher = on_keyword({"hello", "hi"})
|
||||
```
|
||||
|
||||
### `command`
|
@@ -823,7 +823,7 @@ description: nonebot.adapters 模块
|
||||
|
||||
- **返回**
|
||||
|
||||
- untyped
|
||||
- TF
|
||||
|
||||
### _method_ `format_map(mapping)` {#MessageTemplate-format-map}
|
||||
|
@@ -17,6 +17,52 @@ pip install nonebot2[aiohttp]
|
||||
本驱动仅支持客户端连接
|
||||
:::
|
||||
|
||||
## _class_ `Session(params=None, headers=None, cookies=None, version=HTTPVersion.H11, timeout=None, proxy=None)` {#Session}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `params` (QueryTypes)
|
||||
|
||||
- `headers` (HeaderTypes)
|
||||
|
||||
- `cookies` (CookieTypes)
|
||||
|
||||
- `version` (str | [HTTPVersion](index.md#HTTPVersion))
|
||||
|
||||
- `timeout` (float | None)
|
||||
|
||||
- `proxy` (str | None)
|
||||
|
||||
### _async method_ `request(setup)` {#Session-request}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `setup` ([Request](index.md#Request))
|
||||
|
||||
- **返回**
|
||||
|
||||
- [Response](index.md#Response)
|
||||
|
||||
### _async method_ `setup()` {#Session-setup}
|
||||
|
||||
- **参数**
|
||||
|
||||
empty
|
||||
|
||||
- **返回**
|
||||
|
||||
- None
|
||||
|
||||
### _async method_ `close()` {#Session-close}
|
||||
|
||||
- **参数**
|
||||
|
||||
empty
|
||||
|
||||
- **返回**
|
||||
|
||||
- None
|
||||
|
||||
## _class_ `Mixin(<auto>)` {#Mixin}
|
||||
|
||||
- **说明:** AIOHTTP Mixin
|
||||
@@ -45,6 +91,26 @@ pip install nonebot2[aiohttp]
|
||||
|
||||
- AsyncGenerator[[WebSocket](index.md#WebSocket), None]
|
||||
|
||||
### _method_ `get_session(params=None, headers=None, cookies=None, version=HTTPVersion.H11, timeout=None, proxy=None)` {#Mixin-get-session}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `params` (QueryTypes)
|
||||
|
||||
- `headers` (HeaderTypes)
|
||||
|
||||
- `cookies` (CookieTypes)
|
||||
|
||||
- `version` (str | [HTTPVersion](index.md#HTTPVersion))
|
||||
|
||||
- `timeout` (float | None)
|
||||
|
||||
- `proxy` (str | None)
|
||||
|
||||
- **返回**
|
||||
|
||||
- Session
|
||||
|
||||
## _class_ `WebSocket(*, request, session, websocket)` {#WebSocket}
|
||||
|
||||
- **说明:** AIOHTTP Websocket Wrapper
|
||||
@@ -67,12 +133,14 @@ pip install nonebot2[aiohttp]
|
||||
|
||||
- untyped
|
||||
|
||||
### _async method_ `close(code=1000)` {#WebSocket-close}
|
||||
### _async method_ `close(code=1000, reason="")` {#WebSocket-close}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `code` (int)
|
||||
|
||||
- `reason` (str)
|
||||
|
||||
- **返回**
|
||||
|
||||
- untyped
|
@@ -139,7 +139,7 @@ pip install nonebot2[fastapi]
|
||||
|
||||
- None
|
||||
|
||||
### _method_ `run(host=None, port=None, *, app=None, **kwargs)` {#Driver-run}
|
||||
### _method_ `run(host=None, port=None, *args, app=None, **kwargs)` {#Driver-run}
|
||||
|
||||
- **说明:** 使用 `uvicorn` 启动 FastAPI
|
||||
|
||||
@@ -149,6 +149,8 @@ pip install nonebot2[fastapi]
|
||||
|
||||
- `port` (int | None)
|
||||
|
||||
- `*args`
|
||||
|
||||
- `app` (str | None)
|
||||
|
||||
- `**kwargs`
|
110
website/versioned_docs/version-2.3.1/api/drivers/httpx.md
Normal file
110
website/versioned_docs/version-2.3.1/api/drivers/httpx.md
Normal file
@@ -0,0 +1,110 @@
|
||||
---
|
||||
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_ `Session(params=None, headers=None, cookies=None, version=HTTPVersion.H11, timeout=None, proxy=None)` {#Session}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `params` (QueryTypes)
|
||||
|
||||
- `headers` (HeaderTypes)
|
||||
|
||||
- `cookies` (CookieTypes)
|
||||
|
||||
- `version` (str | [HTTPVersion](index.md#HTTPVersion))
|
||||
|
||||
- `timeout` (float | None)
|
||||
|
||||
- `proxy` (str | None)
|
||||
|
||||
### _async method_ `request(setup)` {#Session-request}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `setup` ([Request](index.md#Request))
|
||||
|
||||
- **返回**
|
||||
|
||||
- [Response](index.md#Response)
|
||||
|
||||
### _async method_ `setup()` {#Session-setup}
|
||||
|
||||
- **参数**
|
||||
|
||||
empty
|
||||
|
||||
- **返回**
|
||||
|
||||
- None
|
||||
|
||||
### _async method_ `close()` {#Session-close}
|
||||
|
||||
- **参数**
|
||||
|
||||
empty
|
||||
|
||||
- **返回**
|
||||
|
||||
- None
|
||||
|
||||
## _class_ `Mixin(<auto>)` {#Mixin}
|
||||
|
||||
- **说明:** HTTPX Mixin
|
||||
|
||||
- **参数**
|
||||
|
||||
auto
|
||||
|
||||
### _async method_ `request(setup)` {#Mixin-request}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `setup` ([Request](index.md#Request))
|
||||
|
||||
- **返回**
|
||||
|
||||
- [Response](index.md#Response)
|
||||
|
||||
### _method_ `get_session(params=None, headers=None, cookies=None, version=HTTPVersion.H11, timeout=None, proxy=None)` {#Mixin-get-session}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `params` (QueryTypes)
|
||||
|
||||
- `headers` (HeaderTypes)
|
||||
|
||||
- `cookies` (CookieTypes)
|
||||
|
||||
- `version` (str | [HTTPVersion](index.md#HTTPVersion))
|
||||
|
||||
- `timeout` (float | None)
|
||||
|
||||
- `proxy` (str | None)
|
||||
|
||||
- **返回**
|
||||
|
||||
- Session
|
||||
|
||||
## _class_ `Driver(env, config)` {#Driver}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `env` ([Env](../config.md#Env))
|
||||
|
||||
- `config` ([Config](../config.md#Config))
|
@@ -503,12 +503,14 @@ description: nonebot.drivers 模块
|
||||
|
||||
- type[D]
|
||||
|
||||
**2.** `(driver, *mixins) -> type[CombinedDriver]`
|
||||
**2.** `(driver, _m, *mixins) -> type[CombinedDriver]`
|
||||
|
||||
- **参数**
|
||||
|
||||
- `driver` (type[D])
|
||||
|
||||
- `_m` (type[[Mixin](#Mixin)])
|
||||
|
||||
- `*mixins` (type[[Mixin](#Mixin)])
|
||||
|
||||
- **返回**
|
||||
@@ -535,6 +537,28 @@ description: nonebot.drivers 模块
|
||||
|
||||
- [Response](#Response)
|
||||
|
||||
### _abstract method_ `get_session(params=None, headers=None, cookies=None, version=HTTPVersion.H11, timeout=None, proxy=None)` {#HTTPClientMixin-get-session}
|
||||
|
||||
- **说明:** 获取一个 HTTP 会话
|
||||
|
||||
- **参数**
|
||||
|
||||
- `params` (QueryTypes)
|
||||
|
||||
- `headers` (HeaderTypes)
|
||||
|
||||
- `cookies` (CookieTypes)
|
||||
|
||||
- `version` (str | [HTTPVersion](#HTTPVersion))
|
||||
|
||||
- `timeout` (float | None)
|
||||
|
||||
- `proxy` (str | None)
|
||||
|
||||
- **返回**
|
||||
|
||||
- HTTPClientSession
|
||||
|
||||
## _class_ `HTTPServerSetup(<auto>)` {#HTTPServerSetup}
|
||||
|
||||
- **说明:** HTTP 服务器路由配置。
|
@@ -115,7 +115,7 @@ pip install nonebot2[quart]
|
||||
|
||||
- None
|
||||
|
||||
### _method_ `run(host=None, port=None, *, app=None, **kwargs)` {#Driver-run}
|
||||
### _method_ `run(host=None, port=None, *args, app=None, **kwargs)` {#Driver-run}
|
||||
|
||||
- **说明:** 使用 `uvicorn` 启动 Quart
|
||||
|
||||
@@ -125,6 +125,8 @@ pip install nonebot2[quart]
|
||||
|
||||
- `port` (int | None)
|
||||
|
||||
- `*args`
|
||||
|
||||
- `app` (str | None)
|
||||
|
||||
- `**kwargs`
|
@@ -21,11 +21,11 @@ pip install nonebot2[websockets]
|
||||
|
||||
- **参数**
|
||||
|
||||
- `func` ((P) -> Awaitable[T])
|
||||
- `func` ((P) -> Coroutine[Any, Any, T])
|
||||
|
||||
- **返回**
|
||||
|
||||
- (P) -> Awaitable[T]
|
||||
- (P) -> Coroutine[Any, Any, T]
|
||||
|
||||
## _class_ `Mixin(<auto>)` {#Mixin}
|
||||
|
@@ -15,12 +15,6 @@ description: nonebot.matcher 模块
|
||||
|
||||
empty
|
||||
|
||||
### _instance-var_ `handlers` {#Matcher-handlers}
|
||||
|
||||
- **类型:** list[[Dependent](dependencies/index.md#Dependent)[Any]]
|
||||
|
||||
- **说明:** 事件响应器拥有的事件处理函数列表
|
||||
|
||||
### _class-var_ `type` {#Matcher-type}
|
||||
|
||||
- **类型:** ClassVar[str]
|
||||
@@ -39,6 +33,12 @@ description: nonebot.matcher 模块
|
||||
|
||||
- **说明:** 事件响应器触发权限
|
||||
|
||||
### _class-var_ `handlers` {#Matcher-handlers}
|
||||
|
||||
- **类型:** ClassVar[list[[Dependent](dependencies/index.md#Dependent)[Any]]]
|
||||
|
||||
- **说明:** 事件响应器拥有的事件处理函数列表
|
||||
|
||||
### _class-var_ `priority` {#Matcher-priority}
|
||||
|
||||
- **类型:** ClassVar[int]
|
@@ -38,7 +38,7 @@ description: nonebot.plugin 模块
|
||||
- `require` => [`require`](load.md#require)
|
||||
- `PluginMetadata` => [`PluginMetadata`](model.md#PluginMetadata)
|
||||
|
||||
## _def_ `get_plugin(name)` {#get-plugin}
|
||||
## _def_ `get_plugin(plugin_id)` {#get-plugin}
|
||||
|
||||
- **说明**
|
||||
|
||||
@@ -46,9 +46,11 @@ description: nonebot.plugin 模块
|
||||
|
||||
如果为 `load_plugins` 文件夹导入的插件,则为文件(夹)名。
|
||||
|
||||
如果为嵌套的子插件,标识符为 `父插件标识符:子插件文件(夹)名`。
|
||||
|
||||
- **参数**
|
||||
|
||||
- `name` (str): 插件名,即 [Plugin.name](model.md#Plugin-name)。
|
||||
- `plugin_id` (str): 插件标识符,即 [Plugin.id\_](model.md#Plugin-id-)。
|
||||
|
||||
- **返回**
|
||||
|
||||
@@ -84,7 +86,7 @@ description: nonebot.plugin 模块
|
||||
|
||||
## _def_ `get_available_plugin_names()` {#get-available-plugin-names}
|
||||
|
||||
- **说明:** 获取当前所有可用的插件名(包含尚未加载的插件)。
|
||||
- **说明:** 获取当前所有可用的插件标识符(包含尚未加载的插件)。
|
||||
|
||||
- **参数**
|
||||
|
@@ -124,15 +124,11 @@ description: nonebot.plugin.load 模块
|
||||
|
||||
## _def_ `require(name)` {#require}
|
||||
|
||||
- **说明**
|
||||
|
||||
获取一个插件的导出内容。
|
||||
|
||||
如果为 `load_plugins` 文件夹导入的插件,则为文件(夹)名。
|
||||
- **说明:** 声明依赖插件。
|
||||
|
||||
- **参数**
|
||||
|
||||
- `name` (str): 插件名,即 [Plugin.name](model.md#Plugin-name)。
|
||||
- `name` (str): 插件模块名或插件标识符,仅在已声明插件的情况下可使用标识符。
|
||||
|
||||
- **返回**
|
||||
|
@@ -17,37 +17,31 @@ description: nonebot.plugin.manager 模块
|
||||
|
||||
- `plugins` (Iterable[str] | None): 独立插件模块名集合。
|
||||
|
||||
- `search_path` (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}
|
||||
### _property_ `controlled_modules` {#PluginManager-controlled-modules}
|
||||
|
||||
- **说明:** 搜索插件并缓存插件名称。
|
||||
- **类型:** dict[str, str]
|
||||
|
||||
- **参数**
|
||||
|
||||
empty
|
||||
|
||||
- **返回**
|
||||
|
||||
- set[str]
|
||||
- **说明:** 返回当前插件管理器中控制的插件标识符与模块路径映射字典。
|
||||
|
||||
### _method_ `load_plugin(name)` {#PluginManager-load-plugin}
|
||||
|
||||
@@ -55,11 +49,11 @@ description: nonebot.plugin.manager 模块
|
||||
|
||||
加载指定插件。
|
||||
|
||||
对于独立插件,可以使用完整插件模块名或者插件名称。
|
||||
可以使用完整插件模块名或者插件标识符加载。
|
||||
|
||||
- **参数**
|
||||
|
||||
- `name` (str): 插件名称。
|
||||
- `name` (str): 插件名称或插件标识符。
|
||||
|
||||
- **返回**
|
||||
|
||||
@@ -105,7 +99,7 @@ description: nonebot.plugin.manager 模块
|
||||
|
||||
- `fullname` (str)
|
||||
|
||||
- `path`
|
||||
- `path` (str)
|
||||
|
||||
### _method_ `create_module(spec)` {#PluginLoader-create-module}
|
||||
|
@@ -93,7 +93,7 @@ description: nonebot.plugin.model 模块
|
||||
|
||||
- **类型:** str
|
||||
|
||||
- **说明:** 插件索引标识,NoneBot 使用 文件/文件夹 名称作为标识符
|
||||
- **说明:** 插件名称,NoneBot 使用 文件/文件夹 名称作为插件名称
|
||||
|
||||
### _instance-var_ `module` {#Plugin-module}
|
||||
|
||||
@@ -130,3 +130,9 @@ description: nonebot.plugin.model 模块
|
||||
- **类型:** set[Plugin]
|
||||
|
||||
- **说明:** 子插件集合
|
||||
|
||||
### _property_ `id_` {#Plugin-id-}
|
||||
|
||||
- **类型:** str
|
||||
|
||||
- **说明:** 插件索引标识
|
@@ -24,6 +24,16 @@ description: nonebot.typing 模块
|
||||
|
||||
- untyped
|
||||
|
||||
## _def_ `type_has_args(type_)` {#type-has-args}
|
||||
|
||||
- **参数**
|
||||
|
||||
- `type_` (type[Any])
|
||||
|
||||
- **返回**
|
||||
|
||||
- bool
|
||||
|
||||
## _def_ `origin_is_union(origin)` {#origin-is-union}
|
||||
|
||||
- **参数**
|
||||
@@ -96,6 +106,12 @@ description: nonebot.typing 模块
|
||||
|
||||
- Any
|
||||
|
||||
## _class_ `StateFlag(<auto>)` {#StateFlag}
|
||||
|
||||
- **参数**
|
||||
|
||||
auto
|
||||
|
||||
## _var_ `T_State` {#T-State}
|
||||
|
||||
- **类型:** dict[Any, Any]
|
@@ -143,7 +143,7 @@ description: nonebot.utils 模块
|
||||
|
||||
- **参数**
|
||||
|
||||
- `cm` (ContextManager[T])
|
||||
- `cm` (AbstractContextManager[T])
|
||||
|
||||
- **返回**
|
||||
|
@@ -102,17 +102,16 @@ async def got_location(location: str = ArgPlainText()):
|
||||
|
||||
</details>
|
||||
|
||||
```python {5-10,14-16,18-19}
|
||||
```python {5-9,13-15,17-18}
|
||||
from nonebot.rule import to_me
|
||||
from arclet.alconna import Alconna, Args
|
||||
from nonebot_plugin_alconna import Match, on_alconna
|
||||
|
||||
weather = on_alconna(
|
||||
Alconna("天气", Args["location?", str]),
|
||||
aliases={"weather", "天气预报"},
|
||||
rule=to_me(),
|
||||
)
|
||||
weather.shortcut("weather", {"command": "天气"})
|
||||
weather.shortcut("天气预报", {"command": "天气"})
|
||||
|
||||
|
||||
@weather.handle()
|
||||
@@ -133,7 +132,7 @@ async def got_location(location: str):
|
||||
或阅读 [Alconna 基本介绍](./command.md) 一节。
|
||||
|
||||
关于更多 `on_alconna` 的使用方法,可参考 [插件文档](https://github.com/nonebot/plugin-alconna/blob/master/docs.md),
|
||||
或阅读 [响应规则的使用](./matcher.md) 一节。
|
||||
或阅读 [响应规则的使用](./matcher.mdx) 一节。
|
||||
|
||||
## 交流与反馈
|
||||
|
@@ -5,7 +5,7 @@ description: Alconna 基本介绍
|
||||
|
||||
# Alconna 本体
|
||||
|
||||
[`Alconna`](https://github.com/ArcletProject/Alconna) 隶属于 `ArcletProject`,是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
|
||||
[`Alconna`](https://github.com/ArcletProject/Alconna) 隶属于 `ArcletProject`,是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
|
||||
|
||||
我们通过一个例子来讲解 **Alconna** 的核心 —— `Args`, `Subcommand`, `Option`:
|
||||
|
||||
@@ -34,9 +34,7 @@ print(res.all_matched_args)
|
||||
|
||||
这段代码通过`Alconna`创捷了一个接受主命令名为`pip`, 子命令为`install`且子命令接受一个 **Args** 参数`package`和二个 **Option** 参数`-r`和`-i`的命令参数解析器, 通过`parse`方法返回解析结果 **Arparma** 的实例。
|
||||
|
||||
## 组成
|
||||
|
||||
### 命令头
|
||||
## 命令头
|
||||
|
||||
命令头是指命令的前缀 (Prefix) 与命令名 (Command) 的组合,例如 !help 中的 ! 与 help。
|
||||
|
||||
@@ -55,15 +53,15 @@ print(res.all_matched_args)
|
||||
| [123, "foo"] | "bar" | `[123, "bar"]` 或 `"foobar"` 或 `["foo", "bar"]` | 混合头 |
|
||||
| [(int, "foo"), (456, "bar")] | "baz" | `[123, "foobaz"]` 或 `[456, "foobaz"]` 或 `[456, "barbaz"]` | 对头 |
|
||||
|
||||
无前缀的类型头:此时会将传入的值尝试转为 BasePattern,例如 `int` 会转为 `nepattern.INTEGER`。此时命令头会匹配对应的类型, 例如 `int` 会匹配 `123` 或 `"456"`,但不会匹配 `"foo"`。同时,Alconna 会将命令头匹配到的值转为对应的类型,例如 `int` 会将 `"123"` 转为 `123`。
|
||||
对于无前缀的类型头,此时会将传入的值尝试转为 BasePattern,例如 `int` 会转为 `nepattern.INTEGER`。如此该命令头会匹配对应的类型, 例如 `int` 会匹配 `123` 或 `"456"`,但不会匹配 `"foo"`。解析后,Alconna 会将命令头匹配到的值转为对应的类型,例如 `int` 会将 `"123"` 转为 `123`。
|
||||
|
||||
:::tip
|
||||
|
||||
**正则只在命令名上生效,命令前缀中的正则会被转义**
|
||||
**正则内容只在命令名上生效,前缀中的正则会被转义**
|
||||
|
||||
:::
|
||||
|
||||
除了通过传入 `re:xxx` 来使用正则表达式外,Alconna 还提供了一种更加简洁的方式来使用正则表达式,那就是 Bracket Header:
|
||||
除了通过传入 `re:xxx` 来使用正则表达式外,Alconna 还提供了一种更加简洁的方式来使用正则表达式,称为 Bracket Header:
|
||||
|
||||
```python
|
||||
from alconna import Alconna
|
||||
@@ -82,34 +80,34 @@ Bracket Header 类似 python 里的 f-string 写法,通过 "{}" 声明匹配
|
||||
- "{:\d+}" ⇔ "(\d+)"
|
||||
- "{foo:int}" ⇔ "(?P<foo>\d+)",其中 "int" 部分若能转为 `BasePattern` 则读取里面的表达式
|
||||
|
||||
### 参数声明(Args)
|
||||
## 参数声明(Args)
|
||||
|
||||
`Args` 是用于声明命令参数的组件, 可以通过以下几种方式构造 **Args** :
|
||||
`Args` 是用于声明命令参数的组件, 可以通过以下几种方式构造 **Args** :
|
||||
|
||||
- `Args[key, var, default][key1, var1, default1][...]`
|
||||
- `Args[(key, var, default)]`
|
||||
- `Args.key[var, default]`
|
||||
|
||||
其中,key **一定**是字符串,而 var 一般为参数的类型,default 为具体的值或者 **arclet.alconna.args.Field**
|
||||
其中,key **一定**是字符串,而 var 一般为参数的类型,default 为具体的值或者 **arclet.alconna.args.Field**
|
||||
|
||||
其与函数签名类似,但是允许含有默认值的参数在前;同时支持 keyword-only 参数不依照构造顺序传入 (但是仍需要在非 keyword-only 参数之后)。
|
||||
|
||||
#### key
|
||||
### key
|
||||
|
||||
`key` 的作用是用以标记解析出来的参数并存放于 **Arparma** 中,以方便用户调用。
|
||||
`key` 的作用是用以标记解析出来的参数并存放于 **Arparma** 中,以方便用户调用。
|
||||
|
||||
其有三种为 Args 注解的标识符: `?`、`/`、 `!`, 标识符与 key 之间建议以 `;` 分隔:
|
||||
其有三种为 Args 注解的标识符: `?`、`/`、 `!`, 标识符与 key 之间建议以 `;` 分隔:
|
||||
|
||||
- `!` 标识符表示该处传入的参数应**不是**规定的类型,或**不在**指定的值中。
|
||||
- `?` 标识符表示该参数为**可选**参数,会在无参数匹配时跳过。
|
||||
- `/` 标识符表示该参数的类型注解需要隐藏。
|
||||
- `!` 标识符表示该处传入的参数应**不是**规定的类型,或**不在**指定的值中。
|
||||
- `?` 标识符表示该参数为**可选**参数,会在无参数匹配时跳过。
|
||||
- `/` 标识符表示该参数的类型注解需要隐藏。
|
||||
|
||||
另外,对于参数的注释也可以标记在 `key` 中,其与 key 或者标识符 以 `#` 分割:
|
||||
`foo#这是注释;?` 或 `foo?#这是注释`
|
||||
另外,对于参数的注释也可以标记在 `key` 中,其与 key 或者标识符 以 `#` 分割:
|
||||
`foo#这是注释;?` 或 `foo?#这是注释`
|
||||
|
||||
:::tip
|
||||
|
||||
`Args` 中的 `key` 在实际命令中并不需要传入(keyword 参数除外):
|
||||
`Args` 中的 `key` 在实际命令中并不需要传入(keyword 参数除外):
|
||||
|
||||
```python
|
||||
from arclet.alconna import Alconna, Args
|
||||
@@ -120,7 +118,7 @@ alc.parse("test --foo abc") # 错误
|
||||
alc.parse("test abc") # 正确
|
||||
```
|
||||
|
||||
若需要 `test --foo abc`,你应该使用 `Option`:
|
||||
若需要 `test --foo abc`,你应该使用 `Option`:
|
||||
|
||||
```python
|
||||
from arclet.alconna import Alconna, Args, Option
|
||||
@@ -131,11 +129,11 @@ alc = Alconna("test", Option("--foo", Args["foo", str]))
|
||||
|
||||
:::
|
||||
|
||||
#### var
|
||||
### var
|
||||
|
||||
var 负责命令参数的**类型检查**与**类型转化**
|
||||
|
||||
`Args` 的`var`表面上看需要传入一个 `type`,但实际上它需要的是一个 `nepattern.BasePattern` 的实例:
|
||||
`Args` 的`var`表面上看需要传入一个 `type`,但实际上它需要的是一个 `nepattern.BasePattern` 的实例:
|
||||
|
||||
```python
|
||||
from arclet.alconna import Args
|
||||
@@ -146,89 +144,109 @@ from nepattern import BasePattern
|
||||
args = Args["foo", BasePattern("@\d+")]
|
||||
```
|
||||
|
||||
示例中可以传入 `str` 是因为 `str` 已经注册在了 `nepattern.global_patterns` 中,因此会替换为 `nepattern.global_patterns[str]`
|
||||
`pip` 示例中可以传入 `str` 是因为 `str` 已经注册在了 `nepattern.global_patterns` 中,因此会替换为 `nepattern.global_patterns[str]`
|
||||
|
||||
`nepattern.global_patterns`默认支持的类型有:
|
||||
|
||||
- `str`: 匹配任意字符串
|
||||
- `int`: 匹配整数
|
||||
- `float`: 匹配浮点数
|
||||
- `bool`: 匹配 `True` 与 `False` 以及他们小写形式
|
||||
- `hex`: 匹配 `0x` 开头的十六进制字符串
|
||||
- `bool`: 匹配 `True` 与 `False` 以及他们小写形式
|
||||
- `hex`: 匹配 `0x` 开头的十六进制字符串
|
||||
- `url`: 匹配网址
|
||||
- `email`: 匹配 `xxxx@xxx` 的字符串
|
||||
- `ipv4`: 匹配 `xxx.xxx.xxx.xxx` 的字符串
|
||||
- `list`: 匹配类似 `["foo","bar","baz"]` 的字符串
|
||||
- `dict`: 匹配类似 `{"foo":"bar","baz":"qux"}` 的字符串
|
||||
- `datetime`: 传入一个 `datetime` 支持的格式字符串,或时间戳
|
||||
- `email`: 匹配 `xxxx@xxx` 的字符串
|
||||
- `ipv4`: 匹配 `xxx.xxx.xxx.xxx` 的字符串
|
||||
- `list`: 匹配类似 `["foo","bar","baz"]` 的字符串
|
||||
- `dict`: 匹配类似 `{"foo":"bar","baz":"qux"}` 的字符串
|
||||
- `datetime`: 传入一个 `datetime` 支持的格式字符串,或时间戳
|
||||
- `Any`: 匹配任意类型
|
||||
- `AnyString`: 匹配任意类型,转为 `str`
|
||||
- `Number`: 匹配 `int` 与 `float`,转为 `int`
|
||||
- `AnyString`: 匹配任意类型,转为 `str`
|
||||
- `Number`: 匹配 `int` 与 `float`,转为 `int`
|
||||
|
||||
同时可以使用 typing 中的类型:
|
||||
|
||||
- `Literal[X]`: 匹配其中的任意一个值
|
||||
- `Union[X, Y]`: 匹配其中的任意一个类型
|
||||
- `Optional[xxx]`: 会自动将默认值设为 `None`,并在解析失败时使用默认值
|
||||
- `List[X]`: 匹配一个列表,其中的元素为 `X` 类型
|
||||
- `Dict[X, Y]`: 匹配一个字典,其中的 key 为 `X` 类型,value 为 `Y` 类型
|
||||
- `Optional[xxx]`: 会自动将默认值设为 `None`,并在解析失败时使用默认值
|
||||
- `List[X]`: 匹配一个列表,其中的元素为 `X` 类型
|
||||
- `Dict[X, Y]`: 匹配一个字典,其中的 key 为 `X` 类型,value 为 `Y` 类型
|
||||
- ...
|
||||
|
||||
:::tip
|
||||
|
||||
几类特殊的传入标记:
|
||||
|
||||
- `"foo"`: 匹配字符串 "foo" (若没有某个 `BasePattern` 与之关联)
|
||||
- `RawStr("foo")`: 匹配字符串 "foo" (不会被 `BasePattern` 替换)
|
||||
- `"foo"`: 匹配字符串 "foo" (若没有某个 `BasePattern` 与之关联)
|
||||
- `RawStr("foo")`: 匹配字符串 "foo" (即使有 `BasePattern` 与之关联也不会被替换)
|
||||
- `"foo|bar|baz"`: 匹配 "foo" 或 "bar" 或 "baz"
|
||||
- `[foo, bar, Baz, ...]`: 匹配其中的任意一个值或类型
|
||||
- `Callable[[X], Y]`: 匹配一个参数为 `X` 类型的值,并返回通过该函数调用得到的 `Y` 类型的值
|
||||
- `"re:xxx"`: 匹配一个正则表达式 `xxx`,会返回 Match[0]
|
||||
- `"rep:xxx"`: 匹配一个正则表达式 `xxx`,会返回 `re.Match` 对象
|
||||
- `Callable[[X], Y]`: 匹配一个参数为 `X` 类型的值,并返回通过该函数调用得到的 `Y` 类型的值
|
||||
- `"re:xxx"`: 匹配一个正则表达式 `xxx`,会返回 Match[0]
|
||||
- `"rep:xxx"`: 匹配一个正则表达式 `xxx`,会返回 `re.Match` 对象
|
||||
- `{foo: bar, baz: qux}`: 匹配字典中的任意一个键, 并返回对应的值 (特殊的键 ... 会匹配任意的值)
|
||||
- ...
|
||||
|
||||
**特别的**,你可以不传入 `var`,此时会使用 `key` 作为 `var`, 匹配 `key` 字符串。
|
||||
|
||||
:::
|
||||
|
||||
`MultiVar` 则是一个特殊的标注,用于告知解析器该参数可以接受多个值,其构造方法形如 `MultiVar(str)`。 同样的还有 `KeyWordVar`,其构造方法形如 `KeyWordVar(str)`,用于告知解析器该参数为一个 keyword-only 参数。
|
||||
#### MultiVar 与 KeyWordVar
|
||||
|
||||
`MultiVar` 是一个特殊的标注,用于告知解析器该参数可以接受多个值,类似于函数中的 `*args`,其构造方法形如 `MultiVar(str)`。
|
||||
|
||||
同样的还有 `KeyWordVar`,类似于函数中的 `*, name: type`,其构造方法形如 `KeyWordVar(str)`,用于告知解析器该参数为一个 keyword-only 参数。
|
||||
|
||||
:::tip
|
||||
|
||||
`MultiVar` 与 `KeyWordVar` 组合时,代表该参数为一个可接受多个 key-value 的参数,其构造方法形如 `MultiVar(KeyWordVar(str))`
|
||||
`MultiVar` 与 `KeyWordVar` 组合时,代表该参数为一个可接受多个 key-value 的参数,类似于函数中的 `**kwargs`,其构造方法形如 `MultiVar(KeyWordVar(str))`
|
||||
|
||||
`MultiVar` 与 `KeyWordVar` 也可以传入 `default` 参数,用于指定默认值
|
||||
`MultiVar` 与 `KeyWordVar` 也可以传入 `default` 参数,用于指定默认值
|
||||
|
||||
`MultiVar` 不能在 `KeyWordVar` 之后传入
|
||||
`MultiVar` 不能在 `KeyWordVar` 之后传入
|
||||
|
||||
:::
|
||||
|
||||
### Option 和 Subcommand
|
||||
### default
|
||||
|
||||
`Option` 可以传入一组 `alias`,如 `Option("--foo|-F|--FOO|-f")` 或 `Option("--foo", alias=["-F"]`
|
||||
`default` 传入的是该参数的默认值或者 `Field`,以携带对于该参数的更多信息。
|
||||
|
||||
传入别名后,`option` 会选择其中长度最长的作为选项名称。若传入为 "--foo|-f",则命令名称为 "--foo"
|
||||
默认情况下 (即不声明) `default` 的值为特殊值 `Empty`。这也意味着你可以将默认值设置为 `None` 表示默认值为空值。
|
||||
|
||||
`Field` 构造需要的参数说明如下:
|
||||
|
||||
- default: 参数单元的默认值
|
||||
- alias: 参数单元默认值的别名
|
||||
- completion: 参数单元的补全说明生成函数
|
||||
- unmatch_tips: 参数单元的错误提示生成函数,其接收一个表示匹配失败的元素的参数
|
||||
- missing_tips: 参数单元的缺失提示生成函数
|
||||
|
||||
## 选项与子命令(Option & Subcommand)
|
||||
|
||||
`Option` 和 `Subcommand` 可以传入一组 `alias`,如 `Option("--foo|-F|--FOO|-f")`,`Subcommand("foo", alias=["F"])`
|
||||
|
||||
传入别名后,选项与子命令会选择其中长度最长的作为其名称。若传入为 "--foo|-f",则命令名称为 "--foo"
|
||||
|
||||
:::tip 特别提醒!!!
|
||||
|
||||
在 Alconna 中 Option 的名字或别名**没有要求**必须在前面写上 `-`
|
||||
Option 的名字或别名**没有要求**必须在前面写上 `-`
|
||||
|
||||
Option 与 Subcommand 的唯一区别在于 Subcommand 可以传入自己的 **Option** 与 **Subcommand**
|
||||
|
||||
:::
|
||||
|
||||
`Subcommand` 可以传入自己的 **Option** 与 **Subcommand**
|
||||
|
||||
他们拥有如下共同参数:
|
||||
|
||||
- `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"]))
|
||||
```
|
||||
|
||||
- `default`: 默认值,在该组件未被解析时使用使用该值替换。
|
||||
特别的,使用 `OptionResult` 或 `SubcomanndResult` 可以设置包括参数字典在内的默认值:
|
||||
特别的,使用 `OptionResult` 或 `SubcomanndResult` 可以设置包括参数字典在内的默认值:
|
||||
|
||||
```python
|
||||
from arclet.alconna import Option, OptionResult
|
||||
@@ -237,25 +255,27 @@ opt1 = Option("--foo", default=False)
|
||||
opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1}))
|
||||
```
|
||||
|
||||
`Option` 可以特别设置传入一类 `Action`,作为解析操作
|
||||
### Action
|
||||
|
||||
`Action` 分为三类:
|
||||
`Option` 可以特别设置传入一类 `Action`,作为解析操作
|
||||
|
||||
`Action` 分为三类:
|
||||
|
||||
- `store`: 无 Args 时, 仅存储一个值, 默认为 Ellipsis; 有 Args 时, 后续的解析结果会覆盖之前的值
|
||||
- `append`: 无 Args 时, 将多个值存为列表, 默认为 Ellipsis; 有 Args 时, 每个解析结果会追加到列表中, 当存在默认值并且不为列表时, 会自动将默认值变成列表, 以保证追加的正确性
|
||||
- `count`: 无 Args 时, 计数器加一; 有 Args 时, 表现与 STORE 相同, 当存在默认值并且不为数字时, 会自动将默认值变成 1, 以保证计数器的正确性。
|
||||
|
||||
`Alconna` 提供了预制的几类 `Action`:
|
||||
`Alconna` 提供了预制的几类 `Action`:
|
||||
|
||||
- `store`(默认),`store_value`,`store_true`,`store_false`
|
||||
- `append`,`append_value`
|
||||
- `count`
|
||||
|
||||
### Arparma
|
||||
## 解析结果(Arparma)
|
||||
|
||||
`Alconna.parse` 会返回由 **Arparma** 承载的解析结果
|
||||
`Alconna.parse` 会返回由 **Arparma** 承载的解析结果
|
||||
|
||||
`Arparma` 会有如下参数:
|
||||
`Arparma` 有如下属性:
|
||||
|
||||
- 调试类
|
||||
|
||||
@@ -272,22 +292,53 @@ opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1}))
|
||||
- other_args: 除主参数外的其他解析结果
|
||||
- all_matched_args: 所有 Args 的解析结果
|
||||
|
||||
`Arparma` 同时提供了便捷的查询方法 `query[type]()`,会根据传入的 `path` 查找参数并返回
|
||||
`Arparma` 同时提供了便捷的查询方法 `query[type]()`,会根据传入的 `path` 查找参数并返回
|
||||
|
||||
`path` 支持如下:
|
||||
`path` 支持如下:
|
||||
|
||||
- `main_args`, `options`, ...: 返回对应的属性
|
||||
- `main_args`, `options`, ...: 返回对应的属性
|
||||
- `args`: 返回 all_matched_args
|
||||
- `main_args.xxx`, `options.xxx`, ...: 返回字典中 `xxx`键对应的值
|
||||
- `args.xxx`: 返回 all_matched_args 中 `xxx`键对应的值
|
||||
- `options.foo`, `foo`: 返回选项 `foo` 的解析结果 (OptionResult)
|
||||
- `options.foo.value`, `foo.value`: 返回选项 `foo` 的解析值
|
||||
- `options.foo.args`, `foo.args`: 返回选项 `foo` 的解析参数字典
|
||||
- `options.foo.args.bar`, `foo.bar`: 返回选项 `foo` 的参数字典中 `bar` 键对应的值 ...
|
||||
- `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` 键对应的值 ...
|
||||
|
||||
## 元数据(CommandMeta)
|
||||
|
||||
`Alconna` 的元数据相当于其配置,拥有以下条目:
|
||||
|
||||
- `description`: 命令的描述
|
||||
- `usage`: 命令的用法
|
||||
- `example`: 命令的使用样例
|
||||
- `author`: 命令的作者
|
||||
- `fuzzy_match`: 命令是否开启模糊匹配
|
||||
- `fuzzy_threshold`: 模糊匹配阈值
|
||||
- `raise_exception`: 命令是否抛出异常
|
||||
- `hide`: 命令是否对 manager 隐藏
|
||||
- `hide_shortcut`: 命令的快捷指令是否在 help 信息中隐藏
|
||||
- `keep_crlf`: 命令解析时是否保留换行字符
|
||||
- `compact`: 命令是否允许第一个参数紧随头部
|
||||
- `strict`: 命令是否严格匹配,若为 False 则未知参数将作为名为 $extra 的参数
|
||||
- `context_style`: 命令上下文插值的风格,None 为关闭,bracket 为 {...},parentheses 为 $(...)
|
||||
- `extra`: 命令的自定义额外信息
|
||||
|
||||
元数据一定使用 `meta=...` 形式传入:
|
||||
|
||||
```python
|
||||
from arclet.alconna import Alconna, CommandMeta
|
||||
|
||||
alc = Alconna(..., meta=CommandMeta("foo", example="bar"))
|
||||
```
|
||||
|
||||
## 命名空间配置
|
||||
|
||||
命名空间配置 (以下简称命名空间) 相当于`Alconna`的设置,`Alconna`默认使用“Alconna”命名空间,命名空间有以下几个属性:
|
||||
命名空间配置 (以下简称命名空间) 相当于 `Alconna` 的默认配置,其优先度低于 `CommandMeta`。
|
||||
|
||||
`Alconna` 默认使用 "Alconna" 命名空间。
|
||||
|
||||
命名空间有以下几个属性:
|
||||
|
||||
- name: 命名空间名称
|
||||
- prefixes: 默认前缀配置
|
||||
@@ -296,9 +347,11 @@ opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1}))
|
||||
- fuzzy_match: 默认是否开启模糊匹配
|
||||
- raise_exception: 默认是否抛出异常
|
||||
- builtin_option_name: 默认的内置选项名称(--help, --shortcut, --comp)
|
||||
- disable_builtin_options: 默认禁用的内置选项(--help, --shortcut, --comp)
|
||||
- enable_message_cache: 默认是否启用消息缓存
|
||||
- compact: 默认是否开启紧凑模式
|
||||
- strict: 命令是否严格匹配
|
||||
- context_style: 命令上下文插值的风格
|
||||
- ...
|
||||
|
||||
### 新建命名空间并替换
|
||||
@@ -307,7 +360,7 @@ opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1}))
|
||||
from arclet.alconna import Alconna, namespace, Namespace, Subcommand, Args, config
|
||||
|
||||
|
||||
ns = Namespace("foo", prefixes=["/"]) # 创建 "foo"命名空间配置, 它要求创建的Alconna的主命令前缀必须是/
|
||||
ns = Namespace("foo", prefixes=["/"]) # 创建 "foo"命名空间配置, 它要求创建的Alconna的主命令前缀必须是/
|
||||
|
||||
alc = Alconna("pip", Subcommand("install", Args["package", str]), namespace=ns) # 在创建Alconna时候传入命名空间以替换默认命名空间
|
||||
|
||||
@@ -342,15 +395,15 @@ with namespace(config.default_namespace.name) as np:
|
||||
|
||||
快捷命令可以做到标识一段命令, 并且传递参数给原命令
|
||||
|
||||
一般情况下你可以通过 `Alconna.shortcut` 进行快捷指令操作 (创建,删除)
|
||||
一般情况下你可以通过 `Alconna.shortcut` 进行快捷指令操作 (创建,删除)
|
||||
|
||||
`shortcut` 的第一个参数为快捷指令名称,第二个参数为 `ShortcutArgs`,作为快捷指令的配置:
|
||||
`shortcut` 的第一个参数为快捷指令名称,第二个参数为 `ShortcutArgs`,作为快捷指令的配置:
|
||||
|
||||
```python
|
||||
class ShortcutArgs(TypedDict):
|
||||
"""快捷指令参数"""
|
||||
|
||||
command: NotRequired[DataCollection[Any]]
|
||||
command: NotRequired[str]
|
||||
"""快捷指令的命令"""
|
||||
args: NotRequired[list[Any]]
|
||||
"""快捷指令的附带参数"""
|
||||
@@ -358,6 +411,10 @@ class ShortcutArgs(TypedDict):
|
||||
"""是否允许命令后随参数"""
|
||||
prefix: NotRequired[bool]
|
||||
"""是否调用时保留指令前缀"""
|
||||
wrapper: NotRequired[ShortcutRegWrapper]
|
||||
"""快捷指令的正则匹配结果的额外处理函数"""
|
||||
humanized: NotRequired[str]
|
||||
"""快捷指令的人类可读描述"""
|
||||
```
|
||||
|
||||
### args的使用
|
||||
@@ -400,28 +457,28 @@ alc.parse("echo hello world!")
|
||||
# hello world!
|
||||
```
|
||||
|
||||
当 `fuzzy` 为 False 时,第一个例子中传入 `"涩图1张 abc"` 之类的快捷指令将视为解析失败
|
||||
当 `fuzzy` 为 False 时,第一个例子中传入 `"涩图1张 abc"` 之类的快捷指令将视为解析失败
|
||||
|
||||
快捷指令允许三类特殊的 placeholder:
|
||||
|
||||
- `{%X}`: 如 `setu {%0}`,表示此处填入快捷指令后随的第 X 个参数。
|
||||
- `{%X}`: 如 `setu {%0}`,表示此处填入快捷指令后随的第 X 个参数。
|
||||
|
||||
例如,若快捷指令为 `涩图`, 配置为 `{"command": "setu {%0}"}`, 则指令 `涩图 1` 相当于 `setu 1`
|
||||
例如,若快捷指令为 `涩图`, 配置为 `{"command": "setu {%0}"}`, 则指令 `涩图 1` 相当于 `setu 1`
|
||||
|
||||
- `{*}`: 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。
|
||||
- `{*}`: 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。
|
||||
|
||||
- `{X}`: 表示此处填入可能的正则匹配的组:
|
||||
|
||||
- 若 `command` 中存在匹配组 `(xxx)`,则 `{X}` 表示第 X 个匹配组的内容
|
||||
- 若 `command` 中存储匹配组 `(?P<xxx>...)`, 则 `{X}` 表示 **名字** 为 X 的匹配结果
|
||||
- 若 `command` 中存在匹配组 `(xxx)`,则 `{X}` 表示第 X 个匹配组的内容
|
||||
- 若 `command` 中存储匹配组 `(?P<xxx>...)`, 则 `{X}` 表示 **名字** 为 X 的匹配结果
|
||||
|
||||
除此之外, 通过 **Alconna** 内置选项 `--shortcut` 可以动态操作快捷指令
|
||||
除此之外, 通过 **Alconna** 内置选项 `--shortcut` 可以动态操作快捷指令
|
||||
|
||||
例如:
|
||||
|
||||
- `cmd --shortcut <key> <cmd>` 来增加一个快捷指令
|
||||
- `cmd --shortcut list` 来列出当前指令的所有快捷指令
|
||||
- `cmd --shortcut delete key` 来删除一个快捷指令
|
||||
- `cmd --shortcut <key> <cmd>` 来增加一个快捷指令
|
||||
- `cmd --shortcut list` 来列出当前指令的所有快捷指令
|
||||
- `cmd --shortcut delete key` 来删除一个快捷指令
|
||||
|
||||
```python
|
||||
from arclet.alconna import Alconna, Args
|
||||
@@ -437,7 +494,7 @@ alc.parse("eval --shortcut list")
|
||||
|
||||
## 紧凑命令
|
||||
|
||||
`Alconna`, `Option` 与 `Subcommand` 可以设置 `compact=True` 使得解析命令时允许名称与后随参数之间没有分隔:
|
||||
`Alconna`, `Option` 与 `Subcommand` 可以设置 `compact=True` 使得解析命令时允许名称与后随参数之间没有分隔:
|
||||
|
||||
```python
|
||||
from arclet.alconna import Alconna, Option, CommandMeta, Args
|
||||
@@ -459,7 +516,7 @@ print(alc.parse("gcc -Fabc -Fdef -Fxyz").query[list]("flag.content"))
|
||||
# ['abc', 'def', 'xyz']
|
||||
```
|
||||
|
||||
当 `Option` 的 `action` 为 `count` 时,其自动支持 `compact` 特性:
|
||||
当 `Option` 的 `action` 为 `count` 时,其自动支持 `compact` 特性:
|
||||
|
||||
```python
|
||||
from arclet.alconna import Alconna, Option, count
|
||||
@@ -472,8 +529,6 @@ print(alc.parse("pp -vvv").query[int]("verbose.value"))
|
||||
|
||||
## 模糊匹配
|
||||
|
||||
模糊匹配通过在 Alconna 中设置其 CommandMeta 开启
|
||||
|
||||
模糊匹配会应用在任意需要进行名称判断的地方,如 **命令名称**,**选项名称** 和 **参数名称** (如指定需要传入参数名称)。
|
||||
|
||||
```python
|
||||
@@ -490,7 +545,7 @@ alc.parse("test_fuzy")
|
||||
|
||||
半自动补全为用户提供了推荐后续输入的功能
|
||||
|
||||
补全默认通过 `--comp` 或 `-cp` 或 `?` 触发:(命名空间配置可修改名称)
|
||||
补全默认通过 `--comp` 或 `-cp` 或 `?` 触发:(命名空间配置可修改名称)
|
||||
|
||||
```python
|
||||
from arclet.alconna import Alconna, Args, Option
|
||||
@@ -515,9 +570,9 @@ output
|
||||
|
||||
## Duplication
|
||||
|
||||
**Duplication** 用来提供更好的自动补全,类似于 **ArgParse** 的 **Namespace**
|
||||
**Duplication** 用来提供更好的自动补全,类似于 **ArgParse** 的 **Namespace**
|
||||
|
||||
普通情况下使用,需要利用到 **ArgsStub**、**OptionStub** 和 **SubcommandStub** 三个部分
|
||||
普通情况下使用,需要利用到 **ArgsStub**、**OptionStub** 和 **SubcommandStub** 三个部分
|
||||
|
||||
以pip为例,其对应的 Duplication 应如下构造:
|
||||
|
||||
@@ -526,20 +581,20 @@ from arclet.alconna import Alconna, Args, Option, OptionResult, Duplication, Sub
|
||||
|
||||
|
||||
class MyDup(Duplication):
|
||||
verbose: OptionResult
|
||||
install: SubcommandStub
|
||||
verbose: OptionResult
|
||||
install: SubcommandStub
|
||||
|
||||
|
||||
alc = Alconna(
|
||||
"pip",
|
||||
Subcommand(
|
||||
"install",
|
||||
Args["package", str],
|
||||
Option("-r|--requirement", Args["file", str]),
|
||||
Option("-i|--index-url", Args["url", str]),
|
||||
),
|
||||
Option("-v|--version"),
|
||||
Option("-v|--verbose", action=count),
|
||||
"pip",
|
||||
Subcommand(
|
||||
"install",
|
||||
Args["package", str],
|
||||
Option("-r|--requirement", Args["file", str]),
|
||||
Option("-i|--index-url", Args["url", str]),
|
||||
),
|
||||
Option("-v|--version"),
|
||||
Option("-v|--verbose", action=count),
|
||||
)
|
||||
|
||||
res = alc.parse("pip -v install ...") # 不使用duplication获得的提示较少
|
||||
@@ -551,7 +606,7 @@ print(result.install)
|
||||
# SubcommandStub(_origin=Subcommand('install', args=Args('package': str)), _value=Ellipsis, available=True, args=ArgsStub(_origin=Args('package': str), _value={'package': '...'}, available=True), dest='install', options=[OptionStub(_origin=Option('requirement', args=Args('file': str)), _value=None, available=False, args=ArgsStub(_origin=Args('file': str), _value={}, available=False), dest='requirement', aliases=['r', 'requirement'], name='requirement'), OptionStub(_origin=Option('index-url', args=Args('url': str)), _value=None, available=False, args=ArgsStub(_origin=Args('url': str), _value={}, available=False), dest='index-url', aliases=['index-url', 'i'], name='index-url')], subcommands=[], name='install')
|
||||
```
|
||||
|
||||
**Duplication** 也可以如 **Namespace** 一样直接标明参数名称和类型:
|
||||
**Duplication** 也可以如 **Namespace** 一样直接标明参数名称和类型:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
@@ -563,3 +618,23 @@ class MyDup(Duplication):
|
||||
file: Optional[str] = None
|
||||
url: Optional[str] = None
|
||||
```
|
||||
|
||||
## 上下文插值
|
||||
|
||||
当 `context_style` 条目被设置后,传入的命令中符合上下文插值的字段会被自动替换成当前上下文中的信息。
|
||||
|
||||
上下文可以在 `parse` 中传入:
|
||||
|
||||
```python
|
||||
from arclet.alconna import Alconna, Args, CommandMeta
|
||||
|
||||
alc = Alconna("test", Args["foo", int], meta=CommandMeta(context_style="parentheses"))
|
||||
|
||||
alc.parse("test $(bar)", {"bar": 123})
|
||||
# {"foo": 123}
|
||||
```
|
||||
|
||||
context_style 的值分两种:
|
||||
|
||||
- `"bracket"`: 插值格式为 `{...}`,例如 `{foo}`
|
||||
- `"parentheses"`: 插值格式为 `$(...)`,例如 `$(bar)`
|
@@ -46,3 +46,31 @@ description: 配置项
|
||||
- **默认值**: `[]`
|
||||
|
||||
全局加载的扩展,路径以 . 分隔,如 `foo.bar.baz:DemoExtension`。
|
||||
|
||||
## alconna_context_style
|
||||
|
||||
- **类型**: `Optional[Literal["bracket", "parentheses"]]`
|
||||
- **默认值**: `None`
|
||||
|
||||
全局命令上下文插值的风格,None 为关闭,bracket 为 `{...}`,parentheses 为 `$(...)`。
|
||||
|
||||
## alconna_enable_saa_patch
|
||||
|
||||
- **类型**: `bool`
|
||||
- **默认值**: `False`
|
||||
|
||||
是否启用 SAA 补丁。
|
||||
|
||||
## alconna_apply_filehost
|
||||
|
||||
- **类型**: `bool`
|
||||
- **默认值**: `False`
|
||||
|
||||
是否启用文件托管。
|
||||
|
||||
## alconna_apply_fetch_targets
|
||||
|
||||
- **类型**: `bool`
|
||||
- **默认值**: `False`
|
||||
|
||||
是否启动时拉取一次发送对象列表。
|
@@ -3,13 +3,14 @@ sidebar_position: 3
|
||||
description: 响应规则的使用
|
||||
---
|
||||
|
||||
import Messenger from "@site/src/components/Messenger";
|
||||
|
||||
# Alconna 插件
|
||||
|
||||
展示:
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna.adapters.onebot12 import Image
|
||||
from nonebot_plugin_alconna import At, on_alconna
|
||||
from nonebot_plugin_alconna import At, Image, on_alconna
|
||||
from arclet.alconna import Args, Option, Alconna, Arparma, MultiVar, Subcommand
|
||||
|
||||
|
||||
@@ -30,8 +31,8 @@ rg = on_alconna(alc, auto_send_output=True)
|
||||
@rg.handle()
|
||||
async def _(result: Arparma):
|
||||
if result.find("list"):
|
||||
img = await ob12_gen_role_group_list_image()
|
||||
await rg.finish(Image(img))
|
||||
img: bytes = await gen_role_group_list_image()
|
||||
await rg.finish(Image(raw=img))
|
||||
if result.find("add"):
|
||||
group = await create_role_group(result.query[str]("add.name"))
|
||||
if result.find("add.member"):
|
||||
@@ -42,7 +43,7 @@ async def _(result: Arparma):
|
||||
|
||||
## 响应器使用
|
||||
|
||||
本插件基于 **Alconna**,为 **Nonebot** 提供了一类新的事件响应器辅助函数 `on_alconna`:
|
||||
本插件基于 **Alconna**,为 **Nonebot** 提供了一类新的事件响应器辅助函数 `on_alconna`:
|
||||
|
||||
```python
|
||||
def on_alconna(
|
||||
@@ -61,10 +62,10 @@ def on_alconna(
|
||||
):
|
||||
```
|
||||
|
||||
- `command`: Alconna 命令或字符串,字符串将通过 `AlconnaFormat` 转换为 Alconna 命令
|
||||
- `command`: Alconna 命令或字符串,字符串将通过 `AlconnaFormat` 转换为 Alconna 命令
|
||||
- `skip_for_unmatch`: 是否在命令不匹配时跳过该响应
|
||||
- `auto_send_output`: 是否自动发送输出信息并跳过响应
|
||||
- `aliases`: 命令别名, 作用类似于 `on_command` 中的 aliases
|
||||
- `aliases`: 命令别名, 作用类似于 `on_command` 中的 aliases
|
||||
- `comp_config`: 补全会话配置, 不传入则不启用补全会话
|
||||
- `extensions`: 需要加载的匹配扩展, 可以是扩展类或扩展实例
|
||||
- `exclude_ext`: 需要排除的匹配扩展, 可以是扩展类或扩展的id
|
||||
@@ -72,14 +73,14 @@ def on_alconna(
|
||||
- `use_cmd_start`: 是否使用 COMMAND_START 作为命令前缀
|
||||
- `use_cmd_sep`: 是否使用 COMMAND_SEP 作为命令分隔符
|
||||
|
||||
`on_alconna` 返回的是 `Matcher` 的子类 `AlconnaMatcher` ,其拓展了如下方法:
|
||||
`on_alconna` 返回的是 `Matcher` 的子类 `AlconnaMatcher` ,其拓展了如下方法:
|
||||
|
||||
- `.assign(path, value, or_not)`: 用于对包含多个选项/子命令的命令的分派处理(具体请看[条件控制](./matcher.md#条件控制))
|
||||
- `.got_path(path, prompt, middleware)`: 在 `got` 方法的基础上,会以 path 对应的参数为准,读取传入 message 的最后一个消息段并验证转换
|
||||
- `.set_path_arg(key, value)`, `.get_path_arg(key)`: 类似 `set_arg` 和 `got_arg`,为 `got_path` 的特化版本
|
||||
- `.reject_path(path[, prompt, fallback])`: 类似于 `reject_arg`,对应 `got_path`
|
||||
- `.dispatch`: 同样的分派处理,但是是类似 `CommandGroup` 一样返回新的 `AlconnaMatcher`
|
||||
- `.got`, `send`, `reject`, ... : 拓展了 prompt 类型,即支持使用 `UniMessage` 作为 prompt
|
||||
- `.assign(path, value, or_not)`: 用于对包含多个选项/子命令的命令的分派处理(具体请看[条件控制](./matcher.mdx#条件控制))
|
||||
- `.got_path(path, prompt, middleware)`: 在 `got` 方法的基础上,会以 path 对应的参数为准,读取传入 message 的最后一个消息段并验证转换
|
||||
- `.set_path_arg(key, value)`, `.get_path_arg(key)`: 类似 `set_arg` 和 `got_arg`,为 `got_path` 的特化版本
|
||||
- `.reject_path(path[, prompt, fallback])`: 类似于 `reject_arg`,对应 `got_path`
|
||||
- `.dispatch`: 同样的分派处理,但是是类似 `CommandGroup` 一样返回新的 `AlconnaMatcher`
|
||||
- `.got`, `send`, `reject`, ... : 拓展了 prompt 类型,即支持使用 `UniMessage` 作为 prompt
|
||||
|
||||
实例:
|
||||
|
||||
@@ -88,7 +89,7 @@ from nonebot import require
|
||||
require("nonebot_plugin_alconna")
|
||||
|
||||
from arclet.alconna import Alconna, Option, Args
|
||||
from nonebot_plugin_alconna import on_alconna, AlconnaMatch, Match, UniMessage
|
||||
from nonebot_plugin_alconna import on_alconna, Match, UniMessage
|
||||
|
||||
|
||||
login = on_alconna(Alconna(["/"], "login", Args["password?", str], Option("-r|--recall"))) # 这里["/"]指命令前缀必须是/
|
||||
@@ -96,13 +97,13 @@ login = on_alconna(Alconna(["/"], "login", Args["password?", str], Option("-r|--
|
||||
# /login -r 触发
|
||||
@login.assign("recall")
|
||||
async def login_exit():
|
||||
await login.finish("已退出")
|
||||
await login.finish("已退出")
|
||||
|
||||
# /login xxx 触发
|
||||
@login.assign("password")
|
||||
async def login_handle(pw: Match[str] = AlconnaMatch("password")):
|
||||
if pw.available:
|
||||
login.set_path_arg("password", pw.result)
|
||||
async def login_handle(pw: Match[str]):
|
||||
if pw.available:
|
||||
login.set_path_arg("password", pw.result)
|
||||
|
||||
# /login 触发
|
||||
@login.got_path("password", prompt=UniMessage.template("{:At(user, $event.get_user_id())} 请输入密码"))
|
||||
@@ -115,24 +116,24 @@ async def login_got(password: str):
|
||||
|
||||
本插件提供了一系列依赖注入函数,便于在响应函数中获取解析结果:
|
||||
|
||||
- `AlconnaResult`: `CommandResult` 类型的依赖注入函数
|
||||
- `AlconnaMatches`: `Arparma` 类型的依赖注入函数
|
||||
- `AlconnaDuplication`: `Duplication` 类型的依赖注入函数
|
||||
- `AlconnaMatch`: `Match` 类型的依赖注入函数
|
||||
- `AlconnaQuery`: `Query` 类型的依赖注入函数
|
||||
- `AlconnaResult`: `CommandResult` 类型的依赖注入函数
|
||||
- `AlconnaMatches`: `Arparma` 类型的依赖注入函数
|
||||
- `AlconnaDuplication`: `Duplication` 类型的依赖注入函数
|
||||
- `AlconnaMatch`: `Match` 类型的依赖注入函数
|
||||
- `AlconnaQuery`: `Query` 类型的依赖注入函数
|
||||
|
||||
同时,基于 [`Annotated` 支持](https://github.com/nonebot/nonebot2/pull/1832),添加了两类注解:
|
||||
同时,基于 [`Annotated` 支持](https://github.com/nonebot/nonebot2/pull/1832),添加了两类注解:
|
||||
|
||||
- `AlcMatches`:同 `AlconnaMatches`
|
||||
- `AlcResult`:同 `AlconnaResult`
|
||||
- `AlcMatches`:同 `AlconnaMatches`
|
||||
- `AlcResult`:同 `AlconnaResult`
|
||||
|
||||
可以看到,本插件提供了几类额外的模型:
|
||||
|
||||
- `CommandResult`: 解析结果,包括了源命令 `source: Alconna` ,解析结果 `result: Arparma`,以及可能的输出信息 `output: str | None` 字段
|
||||
- `Match`: 匹配项,表示参数是否存在于 `all_matched_args` 内,可用 `Match.available` 判断是否匹配,`Match.result` 获取匹配的值
|
||||
- `Query`: 查询项,表示参数是否可由 `Arparma.query` 查询并获得结果,可用 `Query.available` 判断是否查询成功,`Query.result` 获取查询结果
|
||||
- `CommandResult`: 解析结果,包括了源命令 `source: Alconna` ,解析结果 `result: Arparma`,以及可能的输出信息 `output: str | None` 字段
|
||||
- `Match`: 匹配项,表示参数是否存在于 `all_matched_args` 内,可用 `Match.available` 判断是否匹配,`Match.result` 获取匹配的值
|
||||
- `Query`: 查询项,表示参数是否可由 `Arparma.query` 查询并获得结果,可用 `Query.available` 判断是否查询成功,`Query.result` 获取查询结果
|
||||
|
||||
**Alconna** 默认依赖注入的目标参数皆不需要使用依赖注入函数, 该效果对于 `AlconnaMatcher.got_path` 下的 Arg 同样有效:
|
||||
**Alconna** 默认依赖注入的目标参数皆不需要使用依赖注入函数, 该效果对于 `AlconnaMatcher.got_path` 下的 Arg 同样有效:
|
||||
|
||||
```python
|
||||
async def handle(
|
||||
@@ -224,8 +225,8 @@ async def handle_test4(qux: Query[bool] = Query("baz.qux", False)):
|
||||
| [飞书](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 |
|
||||
| [钉钉](https://github.com/nonebot/adapter-ding) | adapters.ding |
|
||||
| [Dodo](https://github.com/nonebot/adapter-dodo) | adapters.dodo |
|
||||
| [Console](https://github.com/nonebot/adapter-console) | adapters.console |
|
||||
| [开黑啦](https://github.com/Tian-que/nonebot-adapter-kaiheila) | adapters.kook |
|
||||
| [Mirai](https://github.com/ieew/nonebot_adapter_mirai2) | adapters.mirai |
|
||||
@@ -233,7 +234,6 @@ async def handle_test4(qux: Query[bool] = Query("baz.qux", False)):
|
||||
| [MineCraft](https://github.com/17TheWord/nonebot-adapter-minecraft) | adapters.minecraft |
|
||||
| [BiliBili Live](https://github.com/wwweww/adapter-bilibili) | adapters.bilibili |
|
||||
| [Walle-Q](https://github.com/onebot-walle/nonebot_adapter_walleq) | adapters.onebot12 |
|
||||
| [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 |
|
||||
@@ -341,7 +341,7 @@ async def tt(target: Union[str, At]):
|
||||
|
||||
## 响应器创建装饰
|
||||
|
||||
本插件提供了一个 `funcommand` 装饰器, 其用于将一个接受任意参数, 返回 `str` 或 `Message` 或 `MessageSegment` 的函数转换为命令响应器:
|
||||
本插件提供了一个 `funcommand` 装饰器, 其用于将一个接受任意参数, 返回 `str` 或 `Message` 或 `MessageSegment` 的函数转换为命令响应器:
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna import funcommand
|
||||
@@ -369,7 +369,7 @@ async def echo_exit(msg: Match[str] = AlconnaMatch("msg")):
|
||||
|
||||
## 类Koishi构造器
|
||||
|
||||
本插件提供了一个 `Command` 构造器,其基于 `arclet.alconna.tools` 中的 `AlconnaString`, 以类似 `Koishi` 中注册命令的方式来构建一个 **AlconnaMatcher** :
|
||||
本插件提供了一个 `Command` 构造器,其基于 `arclet.alconna.tools` 中的 `AlconnaString`, 以类似 `Koishi` 中注册命令的方式来构建一个 **AlconnaMatcher** :
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna import Command, Arparma
|
||||
@@ -389,7 +389,7 @@ async def _(arp: Arparma):
|
||||
await book.send(str(arp.options))
|
||||
```
|
||||
|
||||
甚至,你可以设置 `action` 来设定响应行为:
|
||||
甚至,你可以设置 `action` 来设定响应行为:
|
||||
|
||||
```python
|
||||
book = (
|
||||
@@ -403,9 +403,9 @@ book = (
|
||||
)
|
||||
```
|
||||
|
||||
## 返回值回调
|
||||
## 返回值中间件
|
||||
|
||||
在 `AlconnaMatch`,`AlconnaQuery` 或 `got_path` 中,你可以使用 `middleware` 参数来传入一个对返回值进行处理的函数:
|
||||
在 `AlconnaMatch`,`AlconnaQuery` 或 `got_path` 中,你可以使用 `middleware` 参数来传入一个对返回值进行处理的函数:
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna import image_fetch
|
||||
@@ -422,13 +422,13 @@ async def mask_h(matcher: AlconnaMatcher, img: Match[bytes] = AlconnaMatch("img"
|
||||
await matcher.send(result.content)
|
||||
```
|
||||
|
||||
其中,`image_fetch` 是一个中间件,其接受一个 `Image` 对象,并提取图片的二进制数据返回。
|
||||
其中,`image_fetch` 是一个中间件,其接受一个 `Image` 对象,并提取图片的二进制数据返回。
|
||||
|
||||
## 匹配拓展
|
||||
|
||||
本插件提供了一个 `Extension` 类,其用于自定义 AlconnaMatcher 的部分行为
|
||||
本插件提供了一个 `Extension` 类,其用于自定义 AlconnaMatcher 的部分行为
|
||||
|
||||
例如 `LLMExtension` (仅举例):
|
||||
例如一个 `LLMExtension` 可以如下实现 (仅举例):
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna import Extension, Alconna, on_alconna, Interface
|
||||
@@ -467,14 +467,15 @@ matcher = on_alconna(
|
||||
...
|
||||
```
|
||||
|
||||
那么添加了 `LLMExtension` 的响应器便能接受任何能通过 llm 翻译为具体命令的自然语言消息,同时可以在响应器中为所有 `llm` 参数注入模型变量。
|
||||
那么添加了 `LLMExtension` 的响应器便能接受任何能通过 llm 翻译为具体命令的自然语言消息,同时可以在响应器中为所有 `llm` 参数注入模型变量。
|
||||
|
||||
目前 `Extension` 的功能有:
|
||||
目前 `Extension` 的功能有:
|
||||
|
||||
- `validate`: 对于事件的来源适配器或 bot 选择是否接受响应
|
||||
- `output_converter`: 输出信息的自定义转换方法
|
||||
- `message_provider`: 从传入事件中自定义提取消息的方法
|
||||
- `receive_provider`: 对传入的消息 (Message 或 UniMessage) 的额外处理
|
||||
- `context_provider`: 对命令上下文的额外处理
|
||||
- `permission_check`: 命令对消息解析并确认头部匹配(即确认选择响应)时对发送者的权限判断
|
||||
- `parse_wrapper`: 对命令解析结果的额外处理
|
||||
- `send_wrapper`: 对发送的消息 (Message 或 UniMessage) 的额外处理
|
||||
@@ -482,11 +483,11 @@ matcher = on_alconna(
|
||||
- `catch`: 自定义依赖注入处理函数
|
||||
- `post_init`: 响应器创建后对命令对象的额外处理
|
||||
|
||||
例如内置的 `DiscordSlashExtension`,其可自动将 Alconna 对象翻译成 slash 指令并注册,且将收到的指令交互事件转为指令供命令解析:
|
||||
例如内置的 `DiscordSlashExtension`,其可自动将 Alconna 对象翻译成 slash 指令并注册,且将收到的指令交互事件转为指令供命令解析:
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna import Match, on_alconna
|
||||
from nonebot_plugin_alconna.adapters.discord import DiscordSlashExtension
|
||||
from nonebot_plugin_alconna.builtins.extensions.discord import DiscordSlashExtension
|
||||
|
||||
|
||||
alc = Alconna(
|
||||
@@ -508,8 +509,99 @@ async def remove(plugin: Match[str], time: Match[int]):
|
||||
await matcher.finish(f"removed {plugin.result} with {time.result if time.available else -1}")
|
||||
```
|
||||
|
||||
目前插件提供了 4 个内置的 `Extension`,它们在 `nonebot_plugin_alconna.builtins.extensions` 下:
|
||||
|
||||
- `ReplyRecordExtension`: 将消息事件中的回复暂存在 extension 中,使得解析用的消息不带回复信息,同时可以在后续的处理中获取回复信息。
|
||||
- `DiscordSlashExtension`: 将 Alconna 的命令自动转换为 Discord 的 Slash Command,并将 Slash Command 的交互事件转换为消息交给 Alconna 处理。
|
||||
- `MarkdownOutputExtension`: 将 Alconna 的自动输出转换为 Markdown 格式
|
||||
- `TelegramSlashExtension`: 将 Alconna 的命令注册在 Telegram 上以获得提示。
|
||||
|
||||
:::tip
|
||||
|
||||
全局的 Extension 可延迟加载 (即若有全局拓展加载于部分 AlconnaMatcher 之后,这部分响应器会被追加拓展)
|
||||
|
||||
:::
|
||||
|
||||
## 补全会话
|
||||
|
||||
补全会话基于 [`半自动补全`](./command.md#半自动补全),用于指令参数缺失或参数错误时给予交互式提示,类似于 `got-reject`:
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna import Alconna, Args, Field, At, on_alconna
|
||||
|
||||
alc = Alconna(
|
||||
"添加教师",
|
||||
Args["name", str, Field(completion=lambda: "请输入姓名")],
|
||||
Args["phone", int, Field(completion=lambda: "请输入手机号")],
|
||||
Args["at", [str, At], Field(completion=lambda: "请输入教师号")],
|
||||
)
|
||||
|
||||
cmd = on_alconna(alc, comp_config={"lite": True}, skip_for_unmatch=False)
|
||||
|
||||
@cmd.handle()
|
||||
async def handle(result: Arparma):
|
||||
cmd.finish("添加成功")
|
||||
```
|
||||
|
||||
此时,当用户输入 `添加教师` 时,会自动提示用户输入姓名,手机号和教师号,用户输入后会自动进入下一个提示:
|
||||
|
||||
<Messenger
|
||||
msgs={[
|
||||
{ position: "right", msg: "添加教师" },
|
||||
{ position: "left", msg: "以下是建议的输入: \n- name: 请输入姓名" },
|
||||
{ position: "right", msg: "foo" },
|
||||
{ position: "left", msg: "以下是建议的输入: \n- phone: 请输入手机号" },
|
||||
{ position: "right", msg: "12345" },
|
||||
{ position: "left", msg: "以下是建议的输入: \n- at: 请输入教师号" },
|
||||
{ position: "right", msg: "@me" },
|
||||
{ position: "left", msg: "添加成功" },
|
||||
]}
|
||||
/>
|
||||
|
||||
补全会话配置如下:
|
||||
|
||||
```python
|
||||
class CompConfig(TypedDict):
|
||||
tab: NotRequired[str]
|
||||
"""用于切换提示的指令的名称"""
|
||||
enter: NotRequired[str]
|
||||
"""用于输入提示的指令的名称"""
|
||||
exit: NotRequired[str]
|
||||
"""用于退出会话的指令的名称"""
|
||||
timeout: NotRequired[int]
|
||||
"""超时时间"""
|
||||
hide_tabs: NotRequired[bool]
|
||||
"""是否隐藏所有提示"""
|
||||
hides: NotRequired[Set[Literal["tab", "enter", "exit"]]]
|
||||
"""隐藏的指令"""
|
||||
disables: NotRequired[Set[Literal["tab", "enter", "exit"]]]
|
||||
"""禁用的指令"""
|
||||
lite: NotRequired[bool]
|
||||
"""是否使用简洁版本的补全会话(相当于同时配置 disables、hides、hide_tabs)"""
|
||||
```
|
||||
|
||||
## 内置插件
|
||||
|
||||
类似于 Nonebot 本身提供的内置插件,`nonebot_plugin_alconna` 提供了两个内置插件:`echo` 和 `help`。
|
||||
|
||||
你可以用本插件的 `load_builtin_plugin(s)` 来加载它们:
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna import load_builtin_plugins
|
||||
|
||||
load_builtin_plugins("echo", "help")
|
||||
```
|
||||
|
||||
其中 `help` 仅能列出所有 Alconna 指令。
|
||||
|
||||
<Messenger
|
||||
msgs={[
|
||||
{ position: "right", msg: "/帮助" },
|
||||
{
|
||||
position: "left",
|
||||
msg: "# 当前可用的命令有:\n 0 /echo : echo 指令\n 1 /help : 显示所有命令帮助\n# 输入'命令名 -h|--help' 查看特定命令的语法",
|
||||
},
|
||||
{ position: "right", msg: "/echo [图片]" },
|
||||
{ position: "left", msg: "[图片]" },
|
||||
]}
|
||||
/>
|
@@ -12,27 +12,30 @@ import TabItem from "@theme/TabItem";
|
||||
|
||||
## 通用消息段
|
||||
|
||||
适配器下的消息段标注会匹配适配器特定的 `MessageSegment`, 而通用消息段与适配器消息段的区别在于:
|
||||
通用消息段会匹配多个适配器中相似类型的消息段,并返回 `uniseg` 模块中定义的 [`Segment` 模型](https://nonebot.dev/docs/next/best-practice/alconna/utils#%E9%80%9A%E7%94%A8%E6%B6%88%E6%81%AF%E6%AE%B5), 以达到**跨平台接收消息**的作用。
|
||||
适配器下的消息段标注会匹配适配器特定的 `MessageSegment`, 而通用消息段与适配器消息段的区别在于:
|
||||
通用消息段会匹配多个适配器中相似类型的消息段,并返回 `uniseg` 模块中定义的 [`Segment` 模型](https://nonebot.dev/docs/next/best-practice/alconna/utils#%E9%80%9A%E7%94%A8%E6%B6%88%E6%81%AF%E6%AE%B5), 以达到**跨平台接收消息**的作用。
|
||||
|
||||
`nonebot-plugin-alconna.uniseg` 提供了类似 `MessageSegment` 的通用消息段,并可在 `Alconna` 下直接标注使用:
|
||||
|
||||
```python
|
||||
class Segment:
|
||||
"""基类标注"""
|
||||
children: List["Segment"]
|
||||
|
||||
class Text(Segment):
|
||||
"""Text对象, 表示一类文本元素"""
|
||||
text: str
|
||||
style: Optional[str]
|
||||
styles: Dict[Tuple[int, int], List[str]]
|
||||
|
||||
class At(Segment):
|
||||
"""At对象, 表示一类提醒某用户的元素"""
|
||||
type: Literal["user", "role", "channel"]
|
||||
flag: Literal["user", "role", "channel"]
|
||||
target: str
|
||||
display: Optional[str]
|
||||
|
||||
class AtAll(Segment):
|
||||
"""AtAll对象, 表示一类提醒所有人的元素"""
|
||||
here: bool
|
||||
|
||||
class Emoji(Segment):
|
||||
"""Emoji对象, 表示一类表情元素"""
|
||||
@@ -42,17 +45,23 @@ class Emoji(Segment):
|
||||
class Media(Segment):
|
||||
url: Optional[str]
|
||||
id: Optional[str]
|
||||
path: Optional[str]
|
||||
raw: Optional[bytes]
|
||||
path: Optional[Union[str, Path]]
|
||||
raw: Optional[Union[bytes, BytesIO]]
|
||||
mimetype: Optional[str]
|
||||
name: str
|
||||
|
||||
to_url: ClassVar[Optional[MediaToUrl]]
|
||||
|
||||
class Image(Media):
|
||||
"""Image对象, 表示一类图片元素"""
|
||||
|
||||
class Audio(Media):
|
||||
"""Audio对象, 表示一类音频元素"""
|
||||
duration: Optional[int]
|
||||
|
||||
class Voice(Media):
|
||||
"""Voice对象, 表示一类语音元素"""
|
||||
duration: Optional[int]
|
||||
|
||||
class Video(Media):
|
||||
"""Video对象, 表示一类视频元素"""
|
||||
@@ -73,17 +82,41 @@ class Reference(Segment):
|
||||
"""Reference对象,表示一类引用消息。转发消息 (Forward) 也属于此类"""
|
||||
id: Optional[str]
|
||||
"""此处不一定是消息ID,可能是其他ID,如消息序号等"""
|
||||
content: Optional[Union[Message, str, List[Union[RefNode, CustomNode]]]]
|
||||
children: List[Union[RefNode, CustomNode]]
|
||||
|
||||
class Card(Segment):
|
||||
type: Literal["xml", "json"]
|
||||
raw: str
|
||||
class Hyper(Segment):
|
||||
"""Hyper对象,表示一类超级消息。如卡片消息、ark消息、小程序等"""
|
||||
format: Literal["xml", "json"]
|
||||
raw: Optional[str]
|
||||
content: Optional[Union[dict, list]]
|
||||
|
||||
class Other(Segment):
|
||||
"""其他 Segment"""
|
||||
origin: MessageSegment
|
||||
|
||||
```
|
||||
|
||||
此类消息段通过 `UniMessage.export` 可以转为特定的 `MessageSegment`
|
||||
:::tips
|
||||
|
||||
或许你注意到了 `Segment` 上有一个 `children` 属性。
|
||||
|
||||
这是因为在 [`Satori`](https://satori.js.org/zh-CN/) 协议的规定下,一类元素可以用其子元素来代表一类兼容性消息
|
||||
(例如,qq 的商场表情在某些平台上可以用图片代替)。
|
||||
|
||||
为此,本插件提供了两种方式来表达 "获取子元素" 的方法:
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna.builtins.uniseg.chronocat import MarketFace
|
||||
from nonebot_plugin_alconna import Args, Image, Alconna, select, select_first
|
||||
|
||||
# 表示这个指令需要的图片要么直接是 Image 要么是在 MarketFace 元素内的 Image
|
||||
alc1 = Alconna("make_meme", Args["img", [Image, Image.from_(MarketFace)]])
|
||||
|
||||
# 表示这个指令需要的图片会在目标元素下进行搜索,将所有符合 Image 的元素选出来并将第一个作为结果
|
||||
alc2 = Alconna("make_meme", Args["img", select(Image, index=0)]) # 也可以使用 select_first(Image)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 通用消息序列
|
||||
|
||||
@@ -127,6 +160,7 @@ matcher = on_xxx(...)
|
||||
@matcher.handle()
|
||||
async def _(message: Message = EventMessage()):
|
||||
msg = await UniMessage.generate(message=message)
|
||||
msg1 = UniMessage.generate_without_reply(message=message)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -148,6 +182,21 @@ async def handle_test():
|
||||
await test.send(await UniMessage(Image(path="path/to/img")).export())
|
||||
```
|
||||
|
||||
除此之外 `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)
|
||||
```
|
||||
|
||||
而在 `AlconnaMatcher` 下,`got`, `send`, `reject` 等可以发送消息的方法皆支持使用 `UniMessage`,不需要手动调用 export 方法:
|
||||
|
||||
```python
|
||||
@@ -168,24 +217,9 @@ 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` 参数必须手动传入。
|
||||
在响应器以外的地方,除非启用了 `alconna_apply_fetch_targets` 配置项,否则 `bot` 参数必须手动传入。
|
||||
|
||||
:::
|
||||
|
||||
@@ -391,7 +425,7 @@ message.count(Text) == 2
|
||||
message.get(Text, 1) == UniMessage([Text("test1")])
|
||||
```
|
||||
|
||||
### 消息发送
|
||||
## 消息发送
|
||||
|
||||
前面提到,通用消息可用 `UniMessage.send` 发送自身:
|
||||
|
||||
@@ -408,6 +442,25 @@ async def send(
|
||||
|
||||
实际上,`UniMessage` 同时提供了获取消息事件 id 与消息发送对象的方法:
|
||||
|
||||
<Tabs groupId="get_unimsg">
|
||||
<TabItem value="depend" label="使用依赖注入">
|
||||
|
||||
通过提供的 `MessageTarget`, `MessageId` 或 `MsgTarget`, `MsgId` 依赖注入器来获取消息事件 id 与消息发送对象。
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna.uniseg import MessageId, MsgTarget
|
||||
|
||||
|
||||
matcher = on_xxx(...)
|
||||
|
||||
@matcher.handle()
|
||||
asycn def _(target: MsgTarget, msg_id: MessageId):
|
||||
...
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="method" label="使用 UniMessage 的方法">
|
||||
|
||||
```python
|
||||
from nonebot import Event, Bot
|
||||
from nonebot_plugin_alconna.uniseg import UniMessage, Target
|
||||
@@ -422,24 +475,116 @@ asycn def _(bot: Bot, event: Event):
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
`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
|
||||
parent_id: str
|
||||
"""父级id;若为频道则为guild_id,其他情况下可能为空字符串(例如 Feishu 下可作为部门 id)"""
|
||||
channel: bool
|
||||
"""是否为频道,仅当目标平台符合频道概念时"""
|
||||
private: bool
|
||||
"""是否为私聊"""
|
||||
source: str = ""
|
||||
source: str
|
||||
"""可能的事件id"""
|
||||
self_id: Union[str, None]
|
||||
"""机器人id,若为 None 则 Bot 对象会随机选择"""
|
||||
selector: Union[Callable[[Bot], Awaitable[bool]], None]
|
||||
"""选择器,用于在多个 Bot 对象中选择特定 Bot"""
|
||||
extra: Dict[str, Any]
|
||||
"""额外信息,用于适配器扩展"""
|
||||
```
|
||||
|
||||
是用来描述响应消息时的发送对象。
|
||||
其构造时需要如下参数:
|
||||
|
||||
同样的,你可以通过依赖注入的方式在响应器中直接获取它们。
|
||||
- `id` 为目标id;若为群聊则为 group_id 或者 channel_id,若为私聊则为user_id
|
||||
- `parent_id` 为父级id;若为频道则为 guild_id,其他情况下可能为空字符串(例如 Feishu 下可作为部门 id)
|
||||
- `channel` 为是否为频道,仅当目标平台符合频道概念时
|
||||
- `private` 为是否为私聊
|
||||
- `source` 为可能的事件id
|
||||
- `self_id` 为机器人id,若为 None 则 Bot 对象会随机选择
|
||||
- `selector` 为选择器,用于在多个 Bot 对象中选择特定 Bot
|
||||
- `scope` 为适配器范围,用于传入内置的特定选择器
|
||||
- `adapter` 为适配器名称,若为 None 则需要明确指定 Bot 对象
|
||||
- `platform` 为平台名称,仅当目标适配器存在多个平台时使用
|
||||
- `extra` 为额外信息,用于适配器扩展
|
||||
|
||||
通过 `Target` 对象,我们可以在 `UniMessage.send` 中指定发送对象:
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna.uniseg import UniMessage, MsgTarget, Target, SupportScope
|
||||
|
||||
|
||||
matcher = on_xxx(...)
|
||||
|
||||
@matcher.handle()
|
||||
async def _(target: MsgTarget):
|
||||
await UniMessage("Hello!").send(target=target)
|
||||
target1 = Target("xxxx", scope=SupportScope.qq_client)
|
||||
await UniMessage("Hello!").send(target=target1)
|
||||
```
|
||||
|
||||
### 主动发送消息
|
||||
|
||||
`UniMessage.send` 也可以用于主动发送消息:
|
||||
|
||||
```python
|
||||
from nonebot_plugin_alconna.uniseg import UniMessage, Target, SupportScope
|
||||
from nonebot import get_driver
|
||||
|
||||
|
||||
driver = get_driver()
|
||||
|
||||
@driver.on_startup
|
||||
async def on_startup():
|
||||
target = Target("xxxx", scope=SupportScope.qq_client)
|
||||
await UniMessage("Hello!").send(target=target)
|
||||
```
|
||||
|
||||
## 自定义消息段
|
||||
|
||||
`uniseg` 提供了部分方法来允许用户自定义 Segment 的序列化和反序列化:
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
|
||||
from nonebot.adapters import Bot
|
||||
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
||||
from nonebot.adapters.satori import Custom, Message, MessageSegment
|
||||
|
||||
from nonebot_plugin_alconna.uniseg.builder import MessageBuilder
|
||||
from nonebot_plugin_alconna.uniseg.exporter import MessageExporter
|
||||
from nonebot_plugin_alconna.uniseg import Segment, custom_handler, custom_register
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarketFace(Segment):
|
||||
tabId: str
|
||||
faceId: str
|
||||
key: str
|
||||
|
||||
|
||||
@custom_register(MarketFace, "chronocat:marketface")
|
||||
def mfbuild(builder: MessageBuilder, seg: BaseMessageSegment):
|
||||
if not isinstance(seg, Custom):
|
||||
raise ValueError("MarketFace can only be built from Satori Message")
|
||||
return MarketFace(**seg.data)(*builder.generate(seg.children))
|
||||
|
||||
|
||||
@custom_handler(MarketFace)
|
||||
async def mfexport(exporter: MessageExporter, seg: MarketFace, bot: Bot, fallback: bool):
|
||||
if exporter.get_message_type() is Message:
|
||||
return MessageSegment("chronocat:marketface", seg.data)(await exporter.export(seg.children, bot, fallback))
|
||||
|
||||
```
|
||||
|
||||
具体而言,你可以使用 `custom_register` 来增加一个从 MessageSegment 到 Segment 的处理方法;使用 `custom_handler` 来增加一个从 Segment 到 MessageSegment 的处理方法。
|
@@ -0,0 +1,145 @@
|
||||
import TabItem from "@theme/TabItem";
|
||||
import Tabs from "@theme/Tabs";
|
||||
|
||||
# 数据库
|
||||
|
||||
[`nonebot-plugin-orm`](https://github.com/nonebot/plugin-orm) 是 NoneBot 的数据库支持插件。
|
||||
本插件基于 [SQLAlchemy](https://www.sqlalchemy.org/) 和 [Alembic](https://alembic.sqlalchemy.org/),提供了许多与 NoneBot 紧密集成的功能:
|
||||
|
||||
- 多 Engine / Connection 支持
|
||||
- Session 管理
|
||||
- 关系模型管理、依赖注入支持
|
||||
- 数据库迁移
|
||||
|
||||
## 安装
|
||||
|
||||
<Tabs groupId="install">
|
||||
<TabItem value="cli" label="使用 nb-cli">
|
||||
|
||||
```shell
|
||||
nb plugin install nonebot-plugin-orm
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```shell
|
||||
pip install nonebot-plugin-orm
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="pdm" label="使用 pdm">
|
||||
|
||||
```shell
|
||||
pdm add nonebot-plugin-orm
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## 数据库驱动和后端
|
||||
|
||||
本插件只提供了 ORM 功能,没有数据库后端,也没有直接连接数据库后端的能力。
|
||||
所以你需要另行安装数据库驱动和数据库后端,并且配置数据库连接信息。
|
||||
|
||||
### SQLite
|
||||
|
||||
[SQLite](https://www.sqlite.org/) 是一个轻量级的嵌入式数据库,它的数据以单文件的形式存储在本地,不需要单独的数据库后端。
|
||||
SQLite 非常适合用于开发环境和小型应用,但是不适合用于大型应用的生产环境。
|
||||
|
||||
虽然不需要另行安装数据库后端,但你仍然需要安装数据库驱动:
|
||||
|
||||
<Tabs groupId="install">
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```shell
|
||||
pip install "nonebot-plugin-orm[sqlite]"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="pdm" label="使用 pdm">
|
||||
|
||||
```shell
|
||||
pdm add "nonebot-plugin-orm[sqlite]"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
默认情况下,数据库文件为 `<data path>/nonebot-plugin-orm/db.sqlite3`(数据目录由 [nonebot-plugin-localstore](../data-storing) 提供)。
|
||||
或者,你可以通过配置 `SQLALCHEMY_DATABASE_URL` 来指定数据库文件路径:
|
||||
|
||||
```shell
|
||||
SQLALCHEMY_DATABASE_URL=sqlite+aiosqlite:///file_path
|
||||
```
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
[PostgreSQL](https://www.postgresql.org/) 是世界上最先进的开源关系数据库之一,对各种高级且广泛应用的功能有最好的支持,是中小型应用的首选数据库。
|
||||
|
||||
<Tabs groupId="install">
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```shell
|
||||
pip install nonebot-plugin-orm[postgresql]
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="pdm" label="使用 pdm">
|
||||
|
||||
```shell
|
||||
pdm add nonebot-plugin-orm[postgresql]
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
```shell
|
||||
SQLALCHEMY_DATABASE_URL=postgresql+psycopg://user:password@host:port/dbname[?key=value&key=value...]
|
||||
```
|
||||
|
||||
### MySQL / MariaDB
|
||||
|
||||
[MySQL](https://www.mysql.com/) 和 [MariaDB](https://mariadb.com/) 是经典的开源关系数据库,适合用于中小型应用。
|
||||
|
||||
<Tabs groupId="install">
|
||||
<TabItem value="pip" label="使用 pip">
|
||||
|
||||
```shell
|
||||
pip install nonebot-plugin-orm[mysql]
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="pdm" label="使用 pdm">
|
||||
|
||||
```shell
|
||||
pdm add nonebot-plugin-orm[mysql]
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
```shell
|
||||
SQLALCHEMY_DATABASE_URL=mysql+aiomysql://user:password@host:port/dbname[?key=value&key=value...]
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
本插件提供了数据库迁移功能(此功能依赖于 [nb-cli 脚手架](../../quick-start#安装脚手架))。
|
||||
在安装了新的插件或机器人之后,你需要执行一次数据库迁移操作,将数据库同步至与机器人一致的状态:
|
||||
|
||||
```shell
|
||||
nb orm upgrade
|
||||
```
|
||||
|
||||
运行完毕后,可以检查一下:
|
||||
|
||||
```shell
|
||||
nb orm check
|
||||
```
|
||||
|
||||
如果输出是 `没有检测到新的升级操作`,那么恭喜你,数据库已经迁移完成了,你可以启动机器人了。
|
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "数据库",
|
||||
"position": 7
|
||||
}
|
@@ -0,0 +1,378 @@
|
||||
# 开发者指南
|
||||
|
||||
开发者指南内容较多,故分为了一个示例以及数个专题。
|
||||
阅读(并且最好跟随实践)示例后,你将会对使用 `nonebot-plugin-orm` 开发插件有一个基本的认识。
|
||||
如果想要更深入地学习关于 [SQLAlchemy](https://www.sqlalchemy.org/) 和 [Alembic](https://alembic.sqlalchemy.org/) 的知识,或者在使用过程中遇到了问题,可以查阅专题以及其官方文档。
|
||||
|
||||
## 示例
|
||||
|
||||
### 模型定义
|
||||
|
||||
首先,我们需要设计存储的数据的结构。
|
||||
例如天气插件,需要存储**什么地方 (`location`)** 的**天气是什么 (`weather`)**。
|
||||
其中,一个地方只会有一种天气,而不同地方可能有相同的天气。
|
||||
所以,我们可以设计出如下的模型:
|
||||
|
||||
```python title=weather/__init__.py showLineNumbers
|
||||
from nonebot_plugin_orm import Model
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
|
||||
class Weather(Model):
|
||||
location: Mapped[str] = mapped_column(primary_key=True)
|
||||
weather: Mapped[str]
|
||||
```
|
||||
|
||||
其中,`primary_key=True` 意味着此列 (`location`) 是主键,即内容是唯一的且非空的。
|
||||
每一个模型必须有至少一个主键。
|
||||
|
||||
我们可以用以下代码检查模型生成的数据库模式是否正确:
|
||||
|
||||
```python
|
||||
from sqlalchemy.schema import CreateTable
|
||||
|
||||
print(CreateTable(Weather.__table__))
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE TABLE weather_weather (
|
||||
location VARCHAR NOT NULL,
|
||||
weather VARCHAR NOT NULL,
|
||||
CONSTRAINT pk_weather_weather PRIMARY KEY (location)
|
||||
)
|
||||
```
|
||||
|
||||
可以注意到表名是 `weather_weather` 而不是 `Weather` 或者 `weather`。
|
||||
这是因为 `nonebot-plugin-orm` 会自动为模型生成一个表名,规则是:`<插件模块名>_<类名小写>`。
|
||||
|
||||
你也可以通过指定 `__tablename__` 属性来自定义表名:
|
||||
|
||||
```python {2}
|
||||
class Weather(Model):
|
||||
__tablename__ = "weather"
|
||||
...
|
||||
```
|
||||
|
||||
```sql {1}
|
||||
CREATE TABLE weather (
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
但是,并不推荐你这么做,因为这可能会导致不同插件间的表名重复,引发冲突。
|
||||
特别是当你会发布插件时,你并不知道其他插件会不会使用相同的表名。
|
||||
|
||||
### 首次迁移
|
||||
|
||||
我们成功定义了模型,现在启动机器人试试吧:
|
||||
|
||||
```shell
|
||||
$ nb run
|
||||
01-02 15:04:05 [SUCCESS] nonebot | NoneBot is initializing...
|
||||
01-02 15:04:05 [ERROR] nonebot_plugin_orm | 启动检查失败
|
||||
01-02 15:04:05 [ERROR] nonebot | Application startup failed. Exiting.
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
click.exceptions.UsageError: 检测到新的升级操作:
|
||||
[('add_table',
|
||||
Table('weather', MetaData(), Column('location', String(), table=<weather>, primary_key=True, nullable=False), Column('weather', String(), table=<weather>, nullable=False), schema=None))]
|
||||
```
|
||||
|
||||
咦,发生了什么?
|
||||
`nonebot-plugin-orm` 试图阻止我们启动机器人。
|
||||
原来是我们定义了模型,但是数据库中并没有对应的表,这会导致插件不能正常运行。
|
||||
所以,我们需要迁移数据库。
|
||||
|
||||
首先,我们需要创建一个迁移脚本:
|
||||
|
||||
```shell
|
||||
nb orm revision -m "first revision" --branch-label weather
|
||||
```
|
||||
|
||||
其中,`-m` 参数是迁移脚本的描述,`--branch-label` 参数是迁移脚本的分支,一般为插件模块名。
|
||||
执行命令过后,出现了一个 `weather/migrations` 目录,其中有一个 `xxxxxxxxxxxx_first_revision.py` 文件:
|
||||
|
||||
```shell {4,5}
|
||||
weather
|
||||
├── __init__.py
|
||||
├── config.py
|
||||
└── migrations
|
||||
└── xxxxxxxxxxxx_first_revision.py
|
||||
```
|
||||
|
||||
这就是我们创建的迁移脚本,它记录了数据库模式的变化。
|
||||
我们可以查看一下它的内容:
|
||||
|
||||
```python title=weather/migrations/xxxxxxxxxxxx_first_revision.py {25-33,39-41} showLineNumbers
|
||||
"""first revision
|
||||
|
||||
迁移 ID: xxxxxxxxxxxx
|
||||
父迁移:
|
||||
创建时间: 2006-01-02 15:04:05.999999
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision: str = "xxxxxxxxxxxx"
|
||||
down_revision: str | Sequence[str] | None = None
|
||||
branch_labels: str | Sequence[str] | None = ("weather",)
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade(name: str = "") -> None:
|
||||
if name:
|
||||
return
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"weather_weather",
|
||||
sa.Column("location", sa.String(), nullable=False),
|
||||
sa.Column("weather", sa.String(), nullable=False),
|
||||
sa.PrimaryKeyConstraint("location", name=op.f("pk_weather_weather")),
|
||||
info={"bind_key": "weather"},
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade(name: str = "") -> None:
|
||||
if name:
|
||||
return
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("weather_weather")
|
||||
# ### end Alembic commands ###
|
||||
```
|
||||
|
||||
可以注意到脚本的主体部分(其余是模版代码,请勿修改)是:
|
||||
|
||||
```python
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table( # CREATE TABLE
|
||||
"weather_weather", # weather_weather
|
||||
sa.Column("location", sa.String(), nullable=False), # location VARCHAR NOT NULL,
|
||||
sa.Column("weather", sa.String(), nullable=False), # weather VARCHAR NOT NULL,
|
||||
sa.PrimaryKeyConstraint("location", name=op.f("pk_weather_weather")), # CONSTRAINT pk_weather_weather PRIMARY KEY (location)
|
||||
info={"bind_key": "weather"},
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
```
|
||||
|
||||
```python
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("weather_weather") # DROP TABLE weather_weather;
|
||||
# ### end Alembic commands ###
|
||||
```
|
||||
|
||||
虽然我们不是很懂这些代码的意思,但是可以注意到它们几乎与 SQL 语句 (DDL) 一一对应。
|
||||
显然,它们是用来创建和删除表的。
|
||||
|
||||
我们还可以注意到,`upgrade()` 和 `downgrade()` 函数中的代码是**互逆**的。
|
||||
也就是说,执行一次 `upgrade()` 函数,再执行一次 `downgrade()` 函数后,数据库的模式就会回到原来的状态。
|
||||
|
||||
这就是迁移脚本的作用:记录数据库模式的变化,以便我们在不同的环境中(例如开发环境和生产环境)**可复现地**、**可逆地**同步数据库模式,正如 git 对我们的代码做的事情那样。
|
||||
|
||||
对了,不要忘记还有一段注释:`commands auto generated by Alembic - please adjust!`。
|
||||
它在提醒我们,这些代码是由 Alembic 自动生成的,我们应该检查它们,并且根据需要进行调整。
|
||||
|
||||
:::caution 注意
|
||||
迁移脚本冗长且繁琐,我们一般不会手写它们,而是由 Alembic 自动生成。
|
||||
一般情况下,Alembic 足够智能,可以正确地生成迁移脚本。
|
||||
但是,在复杂或有歧义的情况下,我们可能需要手动调整迁移脚本。
|
||||
所以,**永远要检查迁移脚本,并且在开发环境中测试!**
|
||||
|
||||
**迁移脚本中任何一处错误都足以使数据付之东流!**
|
||||
:::
|
||||
|
||||
确定迁移脚本正确后,我们就可以执行迁移脚本,将数据库模式同步到数据库中:
|
||||
|
||||
```shell
|
||||
nb orm upgrade
|
||||
```
|
||||
|
||||
现在,我们可以正常启动机器人了。
|
||||
|
||||
开发过程中,我们可能会频繁地修改模型,这意味着我们需要频繁地创建并执行迁移脚本,非常繁琐。
|
||||
实际上,此时我们不在乎数据安全,只需要数据库模式与模型定义一致即可。
|
||||
所以,我们可以关闭 `nonebot-plugin-orm` 的启动检查:
|
||||
|
||||
```shell title=.env.dev
|
||||
ALEMBIC_STARTUP_CHECK=false
|
||||
```
|
||||
|
||||
现在,每次启动机器人时,数据库模式会自动与模型定义同步,无需手动迁移。
|
||||
|
||||
### 会话管理
|
||||
|
||||
我们已经成功定义了模型,并且迁移了数据库,现在可以开始使用数据库了……吗?
|
||||
并不能,因为模型只是数据结构的定义,并不能通过它操作数据(如果你曾经使用过 [Tortoise ORM](https://tortoise.github.io/),可能会知道 `await Weather.get(location="上海")` 这样的面向对象编程。
|
||||
但是 SQLAlchemy 不同,选择了命令式编程)。
|
||||
我们需要使用**会话**操作数据:
|
||||
|
||||
```python title=weather/__init__.py {10,13} showLineNumbers
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters import Message
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot_plugin_orm import async_scoped_session
|
||||
|
||||
weather = on_command("天气")
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def _(session: async_scoped_session, args: Message = CommandArg()):
|
||||
location = args.extract_plain_text()
|
||||
|
||||
if wea := await session.get(Weather, location):
|
||||
await weather.finish(f"今天{location}的天气是{wea.weather}")
|
||||
|
||||
await weather.finish(f"未查询到{location}的天气")
|
||||
```
|
||||
|
||||
我们通过 `session: async_scoped_session` 依赖注入获得了一个会话,然后使用 `await session.get(Weather, location)` 查询数据库。
|
||||
`async_scoped_session` 是一个有作用域限制的会话,作用域为当前事件、当前事件响应器。
|
||||
会话产生的模型实例(例如此处的 `wea := await session.get(Weather, location)`)作用域与会话相同。
|
||||
|
||||
:::caution 注意
|
||||
此处提到的“会话”指的是 ORM 会话,而非 [NoneBot 会话](../../../appendices/session-control),两者的生命周期也是不同的(NoneBot 会话的生命周期中可能包含多个事件,不同的事件也会有不同的事件响应器)。
|
||||
具体而言,就是不要将 ORM 会话和模型实例存储在 NoneBot 会话状态中:
|
||||
|
||||
```python {12}
|
||||
from nonebot.params import ArgPlainText
|
||||
from nonebot.typing import T_State
|
||||
|
||||
|
||||
@weather.got("location", prompt="请输入地名")
|
||||
async def _(state: T_State, session: async_scoped_session, location: str = ArgPlainText()):
|
||||
wea = await session.get(Weather, location)
|
||||
|
||||
if not wea:
|
||||
await weather.finish(f"未查询到{location}的天气")
|
||||
|
||||
state["weather"] = wea # 不要这么做,除非你知道自己在做什么
|
||||
```
|
||||
|
||||
当然非要这么做也不是不可以:
|
||||
|
||||
```python {6}
|
||||
@weather.handle()
|
||||
async def _(state: T_State, session: async_scoped_session):
|
||||
# 通过 await session.merge(state["weather"]) 获得了此 ORM 会话中的相应模型实例,
|
||||
# 而非直接使用会话状态中的模型实例,
|
||||
# 因为先前的 ORM 会话已经关闭了。
|
||||
wea = await session.merge(state["weather"])
|
||||
await weather.finish(f"今天{state['location']}的天气是{wea.weather}")
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
当有数据更改时,我们需要提交事务,也要注意会话作用域问题:
|
||||
|
||||
```python title=weather/__init__.py {12,20} showLineNumbers
|
||||
from nonebot.params import Depends
|
||||
|
||||
|
||||
async def get_weather(
|
||||
session: async_scoped_session, args: Message = CommandArg()
|
||||
) -> Weather:
|
||||
location = args.extract_plain_text()
|
||||
|
||||
if not (wea := await session.get(Weather, location)):
|
||||
wea = Weather(location=location, weather="未知")
|
||||
session.add(wea)
|
||||
# await session.commit() # 不应该在其他地方提交事务
|
||||
|
||||
return wea
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def _(session: async_scoped_session, wea: Weather = Depends(get_weather)):
|
||||
await weather.send(f"今天的天气是{wea.weather}")
|
||||
await session.commit() # 而应该在事件响应器结束前提交事务
|
||||
```
|
||||
|
||||
当然我们也可以获得一个新的会话,不过此时就要手动管理会话了:
|
||||
|
||||
```python title=weather/__init__.py {5-6} showLineNumbers
|
||||
from nonebot_plugin_orm import get_session
|
||||
|
||||
|
||||
async def get_weather(location: str) -> str:
|
||||
session = get_session()
|
||||
async with session.begin():
|
||||
wea = await session.get(Weather, location)
|
||||
|
||||
if not wea:
|
||||
wea = Weather(location=location, weather="未知")
|
||||
session.add(wea)
|
||||
|
||||
return wea.weather
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def _(args: Message = CommandArg()):
|
||||
wea = await get_weather(args.extract_plain_text())
|
||||
await weather.send(f"今天的天气是{wea}")
|
||||
```
|
||||
|
||||
### 依赖注入
|
||||
|
||||
在上面的示例中,我们都是通过会话获得数据的。
|
||||
不过,我们也可以通过依赖注入获得数据:
|
||||
|
||||
```python title=weather/__init__.py {12-14} showLineNumbers
|
||||
from sqlalchemy import select
|
||||
from nonebot.params import Depends
|
||||
from nonebot_plugin_orm import SQLDepends
|
||||
|
||||
|
||||
def extract_arg_plain_text(args: Message = CommandArg()) -> str:
|
||||
return args.extract_plain_text()
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def _(
|
||||
wea: Weather = SQLDepends(
|
||||
select(Weather).where(Weather.location == Depends(extract_arg_plain_text))
|
||||
),
|
||||
):
|
||||
await weather.send(f"今天的天气是{wea.weather}")
|
||||
```
|
||||
|
||||
其中,`SQLDepends` 是一个特殊的依赖注入,它会根据类型标注和 SQL 语句提供数据,SQL 语句中也可以有子依赖。
|
||||
|
||||
不同的类型标注也会获得不同形式的数据:
|
||||
|
||||
```python title=weather/__init__.py {5} showLineNumbers
|
||||
from collections.abc import Sequence
|
||||
|
||||
@weather.handle()
|
||||
async def _(
|
||||
weas: Sequence[Weather] = SQLDepends(
|
||||
select(Weather).where(Weather.weather == Depends(extract_arg_plain_text))
|
||||
),
|
||||
):
|
||||
await weather.send(f"今天的天气是{weas[0].weather}的城市有{','.join(wea.location for wea in weas)}")
|
||||
```
|
||||
|
||||
支持的类型标注请参见 [依赖注入](dependency)。
|
||||
|
||||
我们也可以像 [类作为依赖](../../../advanced/dependency#类作为依赖) 那样,在类属性中声明子依赖:
|
||||
|
||||
```python title=weather/__init__.py {5-6,10} showLineNumbers
|
||||
from collections.abc import Sequence
|
||||
|
||||
class Weather(Model):
|
||||
location: Mapped[str] = mapped_column(primary_key=True)
|
||||
weather: Mapped[str] = Depends(extract_arg_plain_text)
|
||||
# weather: Annotated[Mapped[str], Depends(extract_arg_plain_text)] # Annotated 支持
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def _(weas: Sequence[Weather]):
|
||||
await weather.send(
|
||||
f"今天的天气是{weas[0].weather}的城市有{','.join(wea.location for wea in weas)}"
|
||||
)
|
||||
```
|
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "开发者指南",
|
||||
"position": 3
|
||||
}
|
@@ -0,0 +1,240 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
description: 依赖注入
|
||||
---
|
||||
|
||||
# 依赖注入
|
||||
|
||||
`nonebot-plugin-orm` 提供了强大且灵活的依赖注入,可以方便地帮助你获取数据库会话和查询数据。
|
||||
|
||||
## 数据库会话
|
||||
|
||||
### AsyncSession
|
||||
|
||||
新数据库会话,常用于有独立的数据库操作逻辑的插件。
|
||||
|
||||
```python {13,26}
|
||||
from nonebot import on_message
|
||||
from nonebot.params import Depends
|
||||
from nonebot_plugin_orm import AsyncSession, Model, async_scoped_session
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
message = on_message()
|
||||
|
||||
|
||||
class Message(Model):
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
|
||||
|
||||
async def get_message(session: AsyncSession) -> Message:
|
||||
# 等价于 session = get_session()
|
||||
async with session:
|
||||
msg = Message()
|
||||
|
||||
session.add(msg)
|
||||
await session.commit()
|
||||
await session.refresh(msg)
|
||||
|
||||
return msg
|
||||
|
||||
|
||||
@message.handle()
|
||||
async def _(session: async_scoped_session, msg: Message = Depends(get_message)):
|
||||
await session.rollback() # 无法回退 get_message() 中的更改
|
||||
await message.send(str(msg.id)) # msg 被存储,msg.id 递增
|
||||
```
|
||||
|
||||
### async_scoped_session
|
||||
|
||||
数据库作用域会话,常用于事件响应器和有与响应逻辑相关的数据库操作逻辑的插件。
|
||||
|
||||
```python {13,26}
|
||||
from nonebot import on_message
|
||||
from nonebot.params import Depends
|
||||
from nonebot_plugin_orm import Model, async_scoped_session
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
message = on_message()
|
||||
|
||||
|
||||
class Message(Model):
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
|
||||
|
||||
async def get_message(session: async_scoped_session) -> Message:
|
||||
# 等价于 session = get_scoped_session()
|
||||
msg = Message()
|
||||
|
||||
session.add(msg)
|
||||
await session.flush()
|
||||
await session.refresh(msg)
|
||||
|
||||
return msg
|
||||
|
||||
|
||||
@message.handle()
|
||||
async def _(session: async_scoped_session, msg: Message = Depends(get_message)):
|
||||
await session.rollback() # 可以回退 get_message() 中的更改
|
||||
await message.send(str(msg.id)) # msg 没有被存储,msg.id 不变
|
||||
```
|
||||
|
||||
## 查询数据
|
||||
|
||||
### Model
|
||||
|
||||
支持类作为依赖。
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
|
||||
from nonebot.params import Depends
|
||||
from nonebot_plugin_orm import Model
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
|
||||
def get_id() -> int: ...
|
||||
|
||||
|
||||
class Message(Model):
|
||||
id: Annotated[Mapped[int], Depends(get_id)] = mapped_column(
|
||||
primary_key=True, autoincrement=True
|
||||
)
|
||||
|
||||
|
||||
async def _(msg: Message):
|
||||
# 等价于 msg = (
|
||||
# await (await session.stream(select(Message).where(Message.id == get_id())))
|
||||
# .scalars()
|
||||
# .one_or_none()
|
||||
# )
|
||||
...
|
||||
```
|
||||
|
||||
### SQLDepends
|
||||
|
||||
参数为一个 SQL 语句,决定依赖注入的内容,SQL 语句中可以使用子依赖。
|
||||
|
||||
```python {11-13}
|
||||
from nonebot.params import Depends
|
||||
from nonebot_plugin_orm import Model, SQLDepends
|
||||
from sqlalchemy import select
|
||||
|
||||
|
||||
def get_id() -> int: ...
|
||||
|
||||
|
||||
async def _(
|
||||
model: Model = SQLDepends(select(Model).where(Model.id == Depends(get_id))),
|
||||
): ...
|
||||
```
|
||||
|
||||
参数可以是任意 SQL 语句,但不建议使用 `select` 以外的语句,因为语句可能没有返回值(`returning` 除外),而且代码不清晰。
|
||||
|
||||
### 类型标注
|
||||
|
||||
类型标注决定依赖注入的数据结构,主要影响以下几个层面:
|
||||
|
||||
- 迭代器(`session.execute()`)或异步迭代器(`session.stream()`)
|
||||
- 标量(`session.execute().scalars()`)或元组(`session.execute()`)
|
||||
- 一个(`session.execute().one_or_none()`,注意 `None` 时可能触发 [重载](../../../appendices/overload#重载))或全部(`session.execute()` / `session.execute().all()`)
|
||||
- 连续(`session().execute()`)或分块(`session.execute().partitions()`)
|
||||
|
||||
具体如下(可以使用父类型作为类型标注):
|
||||
|
||||
- ```python
|
||||
async def _(rows_partitions: AsyncIterator[Sequence[Tuple[Model, ...]]]):
|
||||
# 等价于 rows_partitions = await (await session.stream(sql).partitions())
|
||||
|
||||
async for partition in rows_partitions:
|
||||
for row in partition:
|
||||
print(row[0], row[1], ...)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(model_partitions: AsyncIterator[Sequence[Model]]):
|
||||
# 等价于 model_partitions = await (await session.stream(sql).scalars().partitions())
|
||||
|
||||
async for partition in model_partitions:
|
||||
for model in partition:
|
||||
print(model)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(row_partitions: Iterator[Sequence[Tuple[Model, ...]]]):
|
||||
# 等价于 row_partitions = await session.execute(sql).partitions()
|
||||
|
||||
for partition in rows_partitions:
|
||||
for row in partition:
|
||||
print(row[0], row[1], ...)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(model_partitions: Iterator[Sequence[Model]]):
|
||||
# 等价于 model_partitions = await (await session.execute(sql).scalars().partitions())
|
||||
|
||||
for partition in model_partitions:
|
||||
for model in partition:
|
||||
print(model)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(rows: sa_async.AsyncResult[Tuple[Model, ...]]):
|
||||
# 等价于 rows = await session.stream(sql)
|
||||
|
||||
async for row in rows:
|
||||
print(row[0], row[1], ...)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(models: sa_async.AsyncScalarResult[Model]):
|
||||
# 等价于 models = await session.stream(sql).scalars()
|
||||
|
||||
async for model in models:
|
||||
print(model)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(rows: sa.Result[Tuple[Model, ...]]):
|
||||
# 等价于 rows = await session.execute(sql)
|
||||
|
||||
for row in rows:
|
||||
print(row[0], row[1], ...)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(models: sa.ScalarResult[Model]):
|
||||
# 等价于 models = await session.execute(sql).scalars()
|
||||
|
||||
for model in models:
|
||||
print(model)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(rows: Sequence[Tuple[Model, ...]]):
|
||||
# 等价于 rows = await (await session.stream(sql).all())
|
||||
|
||||
for row in rows:
|
||||
print(row[0], row[1], ...)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(models: Sequence[Model]):
|
||||
# 等价于 models = await (await session.stream(sql).scalars().all())
|
||||
|
||||
for model in models:
|
||||
print(model)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(row: Tuple[Model, ...]):
|
||||
# 等价于 row = await (await session.stream(sql).one_or_none())
|
||||
|
||||
print(row[0], row[1], ...)
|
||||
```
|
||||
|
||||
- ```python
|
||||
async def _(model: Model):
|
||||
# 等价于 model = await (await session.stream(sql).scalars().one_or_none())
|
||||
|
||||
print(model)
|
||||
```
|
@@ -0,0 +1,147 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
description: 测试
|
||||
---
|
||||
|
||||
# 测试
|
||||
|
||||
百思不如一试,测试是发现问题的最佳方式。
|
||||
|
||||
不同的用户会有不同的配置,为了提高项目的兼容性,我们需要在不同数据库后端上测试。
|
||||
手动进行大量的、重复的测试不可靠,也不现实,因此我们推荐使用 [GitHub Actions](https://github.com/features/actions) 进行自动化测试:
|
||||
|
||||
```yaml title=.github/workflows/test.yml {12-42,52-53} showLineNumbers
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
db:
|
||||
- sqlite+aiosqlite:///db.sqlite3
|
||||
- postgresql+psycopg://postgres:postgres@localhost:5432/postgres
|
||||
- mysql+aiomysql://mysql:mysql@localhost:3306/mymysql
|
||||
|
||||
fail-fast: false
|
||||
|
||||
env:
|
||||
SQLALCHEMY_DATABASE_URL: ${{ matrix.db }}
|
||||
|
||||
services:
|
||||
postgresql:
|
||||
image: ${{ startsWith(matrix.db, 'postgresql') && 'postgres' || '' }}
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
mysql:
|
||||
image: ${{ startsWith(matrix.db, 'mysql') && 'mysql' || '' }}
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: mysql
|
||||
MYSQL_USER: mysql
|
||||
MYSQL_PASSWORD: mysql
|
||||
MYSQL_DATABASE: mymysql
|
||||
ports:
|
||||
- 3306:3306
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements.txt
|
||||
|
||||
- name: Run migrations
|
||||
run: pipx run nb-cli orm upgrade
|
||||
|
||||
- name: Run tests
|
||||
run: pytest
|
||||
```
|
||||
|
||||
如果项目还需要考虑跨平台和跨 Python 版本兼容,测试矩阵中还需要增加这两个维度。
|
||||
但是,我们没必要在所有平台和 Python 版本上运行所有数据库的测试,因为很显然,PostgreSQL 和 MySQL 这类独立的数据库后端不会受平台和 Python 影响,而且 Github Actions 的非 Linux 平台不支持运行独立服务:
|
||||
|
||||
| | Python 3.9 | Python 3.10 | Python 3.11 | Python 3.12 |
|
||||
| ----------- | ---------- | ----------- | ----------- | --------------------------- |
|
||||
| **Linux** | SQLite | SQLite | SQLite | SQLite / PostgreSQL / MySQL |
|
||||
| **Windows** | SQLite | SQLite | SQLite | SQLite |
|
||||
| **macOS** | SQLite | SQLite | SQLite | SQLite |
|
||||
|
||||
```yaml title=.github/workflows/test.yml {12-24} showLineNumbers
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
||||
db: ["sqlite+aiosqlite:///db.sqlite3"]
|
||||
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.12"
|
||||
db: postgresql+psycopg://postgres:postgres@localhost:5432/postgres
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.12"
|
||||
db: mysql+aiomysql://mysql:mysql@localhost:3306/mymysql
|
||||
|
||||
fail-fast: false
|
||||
|
||||
env:
|
||||
SQLALCHEMY_DATABASE_URL: ${{ matrix.db }}
|
||||
|
||||
services:
|
||||
postgresql:
|
||||
image: ${{ startsWith(matrix.db, 'postgresql') && 'postgres' || '' }}
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
mysql:
|
||||
image: ${{ startsWith(matrix.db, 'mysql') && 'mysql' || '' }}
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: mysql
|
||||
MYSQL_USER: mysql
|
||||
MYSQL_PASSWORD: mysql
|
||||
MYSQL_DATABASE: mymysql
|
||||
ports:
|
||||
- 3306:3306
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements.txt
|
||||
|
||||
- name: Run migrations
|
||||
run: pipx run nb-cli orm upgrade
|
||||
|
||||
- name: Run tests
|
||||
run: pytest
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user