Compare commits

...

313 Commits

Author SHA1 Message Date
github-actions[bot]
eceef1ebec 🔖 Release 2.0.0rc3 2023-01-22 08:17:26 +00:00
Ju4tCode
50aa8c53e0 🔖 bump version 2.0.0rc3 (#1620) 2023-01-22 16:10:57 +08:00
github-actions[bot]
558f740c13 📝 Update changelog 2023-01-22 07:31:16 +00:00
wwweww
8bffba7efd 🍻 publish adapter BilibiliLive (#1616) 2023-01-22 15:29:55 +08:00
github-actions[bot]
ce93ea13e7 📝 Update changelog 2023-01-22 07:26:24 +00:00
Limnium
174182d62a 🍻 publish plugin 反向词典 (#1618) 2023-01-22 15:25:20 +08:00
github-actions[bot]
36f047be7f 📝 Update changelog 2023-01-22 07:21:13 +00:00
lgc2333
9d73af0513 🍻 publish plugin PicMCStat (#1613) 2023-01-22 15:20:03 +08:00
github-actions[bot]
ecb0d78011 📝 Update changelog 2023-01-22 07:19:32 +00:00
Ju4tCode
5920efb6c5 📝 Docs: 修改更新部分文档 (#1615) 2023-01-22 15:18:26 +08:00
github-actions[bot]
5893fbe57d 📝 Update changelog 2023-01-22 03:41:58 +00:00
17TheWord
27557af636 🍻 publish adapter Spigot (#1611) 2023-01-22 11:40:49 +08:00
github-actions[bot]
b37c7995cb 📝 Update changelog 2023-01-22 03:32:38 +00:00
StarHeart
f46addbb85 Docs: 商店搜索大小写不敏感 (#1609) 2023-01-22 11:31:32 +08:00
github-actions[bot]
6f57a290d7 📝 Update changelog 2023-01-21 06:12:32 +00:00
bridgeL
ae66e45287 🍻 publish plugin 犯人在跳舞 (#1607) 2023-01-21 14:11:18 +08:00
github-actions[bot]
03cf7f290a 📝 Update changelog 2023-01-20 04:02:45 +00:00
Jigsaw
f203aaf4ca ✏️ Plugin: 移除 nonebot-plugin-puppet (#1605) 2023-01-20 12:01:35 +08:00
github-actions[bot]
9a2edbbeb1 📝 Update changelog 2023-01-18 07:26:26 +00:00
Rinfair-CSP-A016
bd9ca99f63 🍻 publish bot SuzunoBot (#1600) 2023-01-18 15:25:12 +08:00
github-actions[bot]
8be262d305 📝 Update changelog 2023-01-17 02:30:56 +00:00
nikissXI
b92d47b362 🍻 publish plugin 喵喵自记菜谱 (#1598) 2023-01-17 10:29:45 +08:00
github-actions[bot]
bdf8cb0d57 📝 Update changelog 2023-01-16 07:06:21 +00:00
itsevin
0cb65214c6 🍻 publish plugin 语音功能 (#1596) 2023-01-16 15:05:12 +08:00
github-actions[bot]
ccc2c5676a 📝 Update changelog 2023-01-16 06:54:42 +00:00
BigOrangeQWQ
6daec67ebd 🍻 publish plugin OrangeDice! (#1594) 2023-01-16 14:53:41 +08:00
github-actions[bot]
051851faed 📝 Update changelog 2023-01-16 06:43:25 +00:00
nikissXI
8d2fca3e12 🍻 publish plugin 简易谷歌翻译插件 (#1592) 2023-01-16 14:42:17 +08:00
github-actions[bot]
76f37c485c 📝 Update changelog 2023-01-16 03:54:28 +00:00
mengxinyuan638
0c7af0873f 🍻 publish plugin 哔哩哔哩q群登录 (#1590) 2023-01-16 11:53:03 +08:00
github-actions[bot]
31fa4ec5f4 📝 Update changelog 2023-01-16 03:06:47 +00:00
nikissXI
fda490d252 ✏️ Plugin: 更新 MC 的插件信息 (#1589) 2023-01-16 11:05:43 +08:00
github-actions[bot]
40e443fd1a 📝 Update changelog 2023-01-16 03:04:37 +00:00
Akirami
4a17e581d2 🔥 移除 nonebot-plugin-aidraw (#1588) 2023-01-16 11:03:24 +08:00
github-actions[bot]
081d212487 📝 Update changelog 2023-01-13 09:11:38 +00:00
mengxinyuan638
3d6774136f 🍻 publish plugin 原神实时公告 (#1584) 2023-01-13 17:10:28 +08:00
github-actions[bot]
fa934a156a 📝 Update changelog 2023-01-12 09:09:47 +00:00
bridgeL
bac5356a90 ✏️ Plugins: 更新 ayaka_games 插件名和描述 (#1586)
Co-authored-by: Su <wxlxy316@163.com>
2023-01-12 17:08:28 +08:00
github-actions[bot]
b289065f71 📝 Update changelog 2023-01-12 02:38:13 +00:00
dpm12345
09cf0f29ba ✏️ Plugin: 更新 tts_gal 插件名和描述 (#1581) 2023-01-12 10:36:58 +08:00
github-actions[bot]
244837dd7c 📝 Update changelog 2023-01-12 02:35:30 +00:00
mengxinyuan638
a0bc113912 🍻 publish bot 辞辞(cici)Bot (#1582) 2023-01-12 10:34:19 +08:00
github-actions[bot]
6f6a296105 📝 Update changelog 2023-01-12 02:29:19 +00:00
Monarchdos
a0d316127f 🍻 publish plugin 心灵鸡汤 (#1579) 2023-01-12 10:27:57 +08:00
github-actions[bot]
f0c0d7788f 📝 Update changelog 2023-01-11 08:52:35 +00:00
Akirami
3f7e2604f1 🔊 Feature: 添加事件响应器检查完成日志 (#1578)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
2023-01-11 16:51:20 +08:00
github-actions[bot]
f43c0087f7 📝 Update changelog 2023-01-10 08:47:48 +00:00
ericzhang-debug
e71d841045 🍻 publish plugin Bing每日图片获取 (#1576) 2023-01-10 16:46:32 +08:00
github-actions[bot]
a3af8da331 📝 Update changelog 2023-01-09 06:25:42 +00:00
Ju4tCode
8bdfdaef91 ⬆️ Fix: 屏蔽 fastapi 0.89.0 (#1574) 2023-01-09 14:24:30 +08:00
github-actions[bot]
afd13ed65d 📝 Update changelog 2023-01-09 02:39:02 +00:00
mengxinyuan638
d83751d0ca 🍻 publish plugin 星座运势 (#1571) 2023-01-09 10:37:45 +08:00
github-actions[bot]
63dd3b8fa7 📝 Update changelog 2023-01-09 02:32:39 +00:00
hmzz804
ae689605a5 🍻 publish plugin 回声洞 (#1570) 2023-01-09 10:31:25 +08:00
github-actions[bot]
bbaba1c955 📝 Update changelog 2023-01-08 08:51:53 +00:00
Cvandia
f1ee54e5c9 🍻 publish plugin 整点报时 (#1568) 2023-01-08 16:50:38 +08:00
github-actions[bot]
6f8e532afe 📝 Update changelog 2023-01-08 07:22:21 +00:00
Akirami
6f68ff61e5 🔥 移除 nonebot_plugin_super_resolution (#1561) 2023-01-08 15:21:05 +08:00
github-actions[bot]
a930fc0997 📝 Update changelog 2023-01-07 09:54:17 +00:00
Jigsaw
65da0947fe ✏️ update OlivOS.nb2's module_name (#1560) 2023-01-07 17:52:56 +08:00
github-actions[bot]
1b64a54421 📝 Update changelog 2023-01-07 08:03:05 +00:00
Akirami
d4e1bb7bf3 🐛 修复子插件加载失败时没有从父插件中移除的问题 (#1559) 2023-01-07 16:01:56 +08:00
github-actions[bot]
d737679ccd 📝 Update changelog 2023-01-07 07:59:53 +00:00
SkyDynamic
4cef5512ee 🍻 publish plugin Hypixel数据查询 (#1555) 2023-01-07 15:58:47 +08:00
github-actions[bot]
1d5d1602f0 📝 Update changelog 2023-01-06 12:08:47 +00:00
Ju4tCode
87e767fa25 remove default fastapi installation (#1557) 2023-01-06 20:07:28 +08:00
github-actions[bot]
c38437a22f 📝 Update changelog 2023-01-06 04:43:30 +00:00
Ju4tCode
cafb7bedb4 add pyright config (#1554) 2023-01-06 12:42:20 +08:00
github-actions[bot]
ace053f387 📝 Update changelog 2023-01-06 03:51:04 +00:00
cpuopt
d6e176d03b 🍻 publish plugin 查找图片出处 (#1552) 2023-01-06 11:49:51 +08:00
github-actions[bot]
2fca5b9664 📝 Update changelog 2023-01-06 03:44:58 +00:00
Monarchdos
cd93ace0dd 🍻 publish plugin 云签到 (#1549) 2023-01-06 11:43:47 +08:00
github-actions[bot]
b118cb6f22 📝 Update changelog 2023-01-06 03:00:44 +00:00
istrashguy
a69ccb4e6c 🍻 publish plugin 图像标注 (#1546) 2023-01-06 10:59:15 +08:00
github-actions[bot]
d5ec31d0a0 📝 Update changelog 2023-01-04 06:55:42 +00:00
CMHopeSunshine
62560635b2 🍻 publish plugin 对对联 (#1541) 2023-01-04 14:54:29 +08:00
github-actions[bot]
c00430c53f 📝 Update changelog 2023-01-04 06:34:51 +00:00
CMHopeSunshine
1dcda4bd77 🍻 publish plugin 群聊学习 (#1539) 2023-01-04 14:33:25 +08:00
github-actions[bot]
b60035f0e6 📝 Update changelog 2023-01-04 05:51:54 +00:00
Umamusume-Agnes-Digital
8551b13eab 🍻 publish plugin 求生之路2——服务器操作 (#1531) 2023-01-04 13:50:41 +08:00
github-actions[bot]
b448a6e083 📝 Update changelog 2023-01-04 05:10:19 +00:00
nikissXI
956b202087 🍻 publish plugin setu_customization (#1533) 2023-01-04 13:08:54 +08:00
github-actions[bot]
b95d49cd9c 📝 Update changelog 2023-01-04 04:59:45 +00:00
Akirami
006b9dd816 Feature: 支持给 FastAPIQuart 传递额外的参数 (#1543) 2023-01-04 12:58:26 +08:00
github-actions[bot]
a9125cd696 📝 Update changelog 2023-01-04 04:49:29 +00:00
uy/sun
ee5dcf0d42 👷 CI: 优化触发条件减少无效运行 (#1545)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-04 12:48:10 +08:00
github-actions[bot]
f13c1cc980 📝 Update changelog 2023-01-03 16:23:05 +00:00
ssttkkl
16c0a87929 🍻 publish plugin 主动消息撤回 (#1535) 2023-01-04 00:21:51 +08:00
github-actions[bot]
39d1554905 📝 Update changelog 2023-01-03 16:14:15 +00:00
ANGJustinl
37067229b0 🍻 publish plugin HttpCat🐱猫猫http状态码 (#1528) 2023-01-04 00:13:05 +08:00
pre-commit-ci[bot]
5ca708d3f4 ⬆️ auto update by pre-commit hooks (#1530)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-03 13:54:27 +08:00
github-actions[bot]
53dded52a7 📝 Update changelog 2023-01-01 07:23:20 +00:00
Akirami
f8cee790e7 Feature: 添加 logger 重导出 (#1526)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-01-01 15:22:07 +08:00
bridgeL
10447ff3c4 🍻 publish plugin 命令探查 (#1523) 2023-01-01 15:20:53 +08:00
github-actions[bot]
f08aec7894 📝 Update changelog 2023-01-01 07:09:08 +00:00
uy/sun
69edb98835 Feature: 将 block driver 转正为 none 驱动器 (#1522) 2023-01-01 15:08:00 +08:00
github-actions[bot]
c73ca2b43f 📝 Update changelog 2023-01-01 07:05:09 +00:00
bridgeL
848c6c5061 ✏️ Plugin: 删除 ayaka_who_is_suspect 插件 (#1525)
Co-authored-by: Su <wxlxy316@163.com>
2023-01-01 15:03:41 +08:00
github-actions[bot]
58f82bf881 📝 Update changelog 2022-12-31 12:43:05 +00:00
uy/sun
9b3e670cee 🐛 修复异常在 traceback 中无法正常显示信息 (#1521) 2022-12-31 20:41:53 +08:00
github-actions[bot]
f0bebb65b4 📝 Update changelog 2022-12-30 15:09:51 +00:00
ANGJustinl
bc845c94e2 🍻 publish plugin AnimalVoice_Convert (#1517) 2022-12-30 23:08:41 +08:00
github-actions[bot]
f4668bf0bc 📝 Update changelog 2022-12-30 03:32:03 +00:00
uy/sun
3493d69fcd 👷 CI: 添加插件加载测试 (#1519)
* 👷 添加插件加载测试

* 调整命名格式

* 添加 issue_comment 的触发条件
2022-12-30 11:30:47 +08:00
github-actions[bot]
125bcb943f 📝 Update changelog 2022-12-29 12:27:17 +00:00
OREOCODEDEV
e65eb3fb18 🍻 publish plugin 服务状态查询 (#1512) 2022-12-29 20:26:12 +08:00
github-actions[bot]
8045420f97 📝 Update changelog 2022-12-29 11:28:33 +00:00
ANGJustinl
03a90006e5 🍻 publish plugin 腾讯云图像变换 (#1514) 2022-12-29 19:27:09 +08:00
github-actions[bot]
adbe341076 📝 Update changelog 2022-12-27 10:54:55 +00:00
Johnny Hsieh
2a623b1c81 🔥 Plugin: 移除停止维护的 nonebot-plugin-filehost (#1516) 2022-12-27 18:53:46 +08:00
github-actions[bot]
c91926aea6 📝 Update changelog 2022-12-27 03:08:47 +00:00
IAXRetailer
63329257de 🍻 publish bot RanBot (#1510) 2022-12-27 11:07:31 +08:00
github-actions[bot]
c9dc6e648e 📝 Update changelog 2022-12-26 03:13:47 +00:00
Ikaros
78a818547e ✏️ Plugin: 更新 abstain_diary 插件名和描述 (#1509) 2022-12-26 11:12:40 +08:00
github-actions[bot]
41eed9d0e9 📝 Update changelog 2022-12-26 02:57:23 +00:00
StarHeart
e32019f15d 📝 Docs: 更新测试文档中的连接方式&细化插件发布描述 (#1504) 2022-12-26 10:56:16 +08:00
github-actions[bot]
59c033e2dd 📝 Update changelog 2022-12-26 02:55:30 +00:00
zhulinyv
49cf1ec5d3 🍻 publish plugin Ping (#1507) 2022-12-26 10:54:07 +08:00
github-actions[bot]
e5ad15d6d6 📝 Update changelog 2022-12-25 16:18:00 +00:00
zhulinyv
4cf9790a95 🍻 publish plugin 群友召唤术 (#1502) 2022-12-26 00:16:32 +08:00
github-actions[bot]
7467e66dab 📝 Update changelog 2022-12-25 16:06:28 +00:00
050644zf
516c1c220c 🍻 publish plugin 战地群聊天插件 (#1505) 2022-12-26 00:05:05 +08:00
github-actions[bot]
136778ae5b 📝 Update changelog 2022-12-24 02:58:56 +00:00
chrisyy2003
17538f7a66 ✏️ Plugin: 更新 gpt3 插件模块名 (#1501) 2022-12-24 10:57:34 +08:00
github-actions[bot]
d1da0be0da 📝 Update changelog 2022-12-23 15:07:19 +00:00
bridgeL
e92bc24631 🍻 publish plugin 不要复读 (#1499) 2022-12-23 23:06:04 +08:00
github-actions[bot]
73d1e5dd88 📝 Update changelog 2022-12-21 06:53:36 +00:00
nikissXI
ea83ba78ec 🍻 publish plugin JAVA MC服务器信息查询 (#1496) 2022-12-21 14:52:23 +08:00
github-actions[bot]
712f80a307 📝 Update changelog 2022-12-21 06:40:17 +00:00
Ju4tCode
5c4ef8fc00 🐛 Fix: 修复客户端请求未处理 cookies (#1491) 2022-12-21 14:39:05 +08:00
github-actions[bot]
2b0973c9f5 📝 Update changelog 2022-12-21 05:59:28 +00:00
Ikaros
767a8ecb08 ✏️ Plugin: 更新 随机禁言 插件功能描述 (#1495) 2022-12-21 13:58:11 +08:00
github-actions[bot]
b342940b69 📝 Update changelog 2022-12-21 04:46:10 +00:00
Jerry080801
7880f07db4 🍻 publish plugin 防撤回 (#1488) 2022-12-21 12:44:42 +08:00
github-actions[bot]
ac702d7eb7 📝 Update changelog 2022-12-21 03:32:39 +00:00
chrisyy2003
9f3b3b2c32 ✏️ Plugin: 更新 multi chatgpt 插件仓库地址 (#1487) 2022-12-21 11:31:35 +08:00
Ju4tCode
2d08465426 add cookies support for forward driver 2022-12-20 10:13:45 +00:00
github-actions[bot]
827d8fbc0e 📝 Update changelog 2022-12-20 07:22:48 +00:00
Ikaros-521
0320be1947 🍻 publish plugin 随机禁言 (#1485) 2022-12-20 15:21:42 +08:00
github-actions[bot]
aca65954bd 📝 Update changelog 2022-12-20 07:04:06 +00:00
RShock
1da9376fc8 🍻 publish plugin 只因进化录 (#1483) 2022-12-20 15:02:47 +08:00
github-actions[bot]
909b811f68 📝 Update changelog 2022-12-18 07:54:38 +00:00
Ju4tCode
ceecf9c692 🐛 fix on_type typing error (#1482) 2022-12-18 15:53:25 +08:00
github-actions[bot]
3de2922773 📝 Update changelog 2022-12-17 09:05:55 +00:00
Ju4tCode
a5d26b7747 add pycln to auto remove unused imports (#1481) 2022-12-17 17:04:38 +08:00
github-actions[bot]
932f50d1fb 📝 Update changelog 2022-12-17 06:25:10 +00:00
chrisyy2003
c69619f142 🍻 publish plugin GPT3 (#1479) 2022-12-17 14:24:08 +08:00
github-actions[bot]
5a49d1e0e2 📝 Update changelog 2022-12-16 05:17:35 +00:00
ReiiNoki
4f9f3f449c 🍻 publish plugin 熊老板 (#1471) 2022-12-16 13:16:23 +08:00
github-actions[bot]
60733c97be 📝 Update changelog 2022-12-15 05:39:28 +00:00
Yuelioi
7a1aa0c204 🍻 publish plugin QQ群文件备份 (#1477) 2022-12-15 13:38:27 +08:00
github-actions[bot]
6d1383a10c 📝 Update changelog 2022-12-15 05:31:38 +00:00
Ikaros-521
86006fafdc 🍻 publish plugin 戒色打卡日记 (#1474) 2022-12-15 13:30:24 +08:00
github-actions[bot]
44ca4f729a 📝 Update changelog 2022-12-14 05:01:36 +00:00
wsdtl
cf29209a55 🍻 publish plugin nonebot_plugin_idiom (#1468) 2022-12-14 13:00:22 +08:00
github-actions[bot]
5e78e2bb5d 📝 Update changelog 2022-12-13 09:49:05 +00:00
幼稚园园长
440e15e204 📝 Docs: 修复文档中部分超链接跳转到 /store.html 的问题 (#1470) 2022-12-13 17:47:45 +08:00
github-actions[bot]
6711a84cab 📝 Update changelog 2022-12-12 14:39:08 +00:00
Ikaros-521
5c2d2141e3 🍻 publish plugin 随机配色方案 (#1465) 2022-12-12 22:37:49 +08:00
github-actions[bot]
8ec1552fd6 📝 Update changelog 2022-12-11 17:09:38 +00:00
chrisyy2003
c1dca723ae 🍻 publish plugin multi-ChatGPT (#1461) 2022-12-12 01:08:28 +08:00
github-actions[bot]
b6cd0424fa 📝 Update changelog 2022-12-10 09:28:58 +00:00
ssttkkl
1beee94c1d 🍻 publish plugin 权限控制 (#1463) 2022-12-10 17:27:48 +08:00
github-actions[bot]
f2a618f663 📝 Update changelog 2022-12-09 16:13:43 +00:00
Akirami
c4286f1f39 💡 补充 params 模块的类型注解 (#1458) 2022-12-10 00:12:28 +08:00
github-actions[bot]
cd9e30bd52 📝 Update changelog 2022-12-09 06:49:49 +00:00
A-kirami
24ae0dfa15 🍻 publish plugin 汇率换算 (#1451) 2022-12-09 14:48:44 +08:00
github-actions[bot]
19a20a3407 📝 Update changelog 2022-12-09 06:44:08 +00:00
Akirami
36d7b44741 Feature: 添加正则匹配文本注入 (#1457)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-12-09 14:42:54 +08:00
dependabot[bot]
8176cd189c ⬆️ Bump nwtgck/actions-netlify from 1 to 2 (#1456)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-09 12:19:22 +08:00
github-actions[bot]
b25de93eb1 📝 Update changelog 2022-12-07 17:06:48 +00:00
A-kirami
79e636ad89 🍻 publish plugin 全群广播 (#1449) 2022-12-08 01:05:31 +08:00
github-actions[bot]
9c9973d8d8 📝 Update changelog 2022-12-07 15:49:53 +00:00
Akirami
04d4954d50 📝 Docs: 移除文档 自定义日志 中多余的符号 (#1448) 2022-12-07 23:48:48 +08:00
github-actions[bot]
2c81dc1975 📝 Update changelog 2022-12-07 13:44:39 +00:00
Akirami
850096ceaa 📝 Docs: 完善 调用平台 API 部分 (#1447)
Co-authored-by: StarHeart <starheart233@gmail.com>
2022-12-07 21:43:27 +08:00
github-actions[bot]
6bb15f6533 📝 Update changelog 2022-12-06 15:12:57 +00:00
Ikaros-521
92c6a8dd6e 🍻 publish plugin 图片背景消除 (#1445) 2022-12-06 23:11:44 +08:00
github-actions[bot]
723eef10bb 📝 Update changelog 2022-12-06 06:20:53 +00:00
Akirami
06c33ad6ea Feature: 支持主动销毁事件响应器 (#1444)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-12-06 14:19:48 +08:00
github-actions[bot]
9826bc29ca 📝 Update changelog 2022-12-05 15:22:06 +00:00
ssttkkl
f35b5b57b7 🍻 publish plugin 雀魂信息查询 (#1442) 2022-12-05 23:20:54 +08:00
github-actions[bot]
433b50c79c 📝 Update changelog 2022-12-05 08:44:04 +00:00
A-kirami
89f3496a2a 🍻 publish plugin ChatGPT (#1438) 2022-12-05 16:42:59 +08:00
github-actions[bot]
df7e8f6d83 📝 Update changelog 2022-12-04 05:23:34 +00:00
ZYKsslm
b57f17d447 🍻 publish plugin 免费快捷点歌插件 (#1435) 2022-12-04 13:22:09 +08:00
github-actions[bot]
665de1da3e 📝 Update changelog 2022-12-04 02:44:19 +00:00
Ikaros-521
091232e996 🍻 publish plugin 动画截图追溯来源 (#1433) 2022-12-04 10:43:09 +08:00
github-actions[bot]
9ee2d94f3c 📝 Update changelog 2022-12-03 06:35:43 +00:00
MingxuanGame
78bdfe65ba ✏️ Docs: 修正文档中部分配置文件示例的符号误用 (#1432)
* :typo: 使用方括号替换大括号

* ✏️ 修改 2.0.0rc2 的文档
2022-12-03 14:34:25 +08:00
github-actions[bot]
5006cf7be6 📝 Update changelog 2022-12-02 06:42:02 +00:00
jcjrobert
a818e0056e 🍻 publish plugin b站图片下载 (#1429) 2022-12-02 14:40:59 +08:00
github-actions[bot]
3efae8bfbc 📝 Update changelog 2022-12-02 05:57:28 +00:00
bridgeL
024d97b997 🍻 Plugin: 更新 ayaka_games 介绍 (#1431)
Co-authored-by: Su <wxlxy316@163.com>
2022-12-02 13:56:15 +08:00
github-actions[bot]
c90ab949d2 📝 Update changelog 2022-11-29 04:01:31 +00:00
Ju4tCode
e8ffa63b78 🐛 fix argument parser message (#1426) 2022-11-29 12:00:09 +08:00
github-actions[bot]
6c27ec7357 📝 Update changelog 2022-11-27 11:38:28 +00:00
Sena
9bf08593d7 ✏️ Plugin: 修改 novelai send magiadice 插件模块名 (#1423) 2022-11-27 19:37:15 +08:00
github-actions[bot]
b016a59a38 📝 Update changelog 2022-11-27 11:22:19 +00:00
Passerby-D
794395e737 🍻 publish plugin 记事本 (#1419) 2022-11-27 19:21:17 +08:00
github-actions[bot]
a758e6f06e 📝 Update changelog 2022-11-26 02:33:35 +00:00
monsterxcn
11feb2c0d0 🍻 publish plugin 原神前瞻直播兑换码查询 (#1421) 2022-11-26 10:32:16 +08:00
Ju4tCode
59a2ed7c2e 🎨 format code 2022-11-25 16:12:23 +00:00
github-actions[bot]
d83866f03b 🔖 Release 2.0.0rc2 2022-11-24 03:55:37 +00:00
Ju4tCode
cb83e76e16 📝 remove old doc version (#1417) 2022-11-24 11:50:20 +08:00
Ju4tCode
1644615462 🔖 bump version 2.0.0rc2 2022-11-24 03:35:31 +00:00
github-actions[bot]
89d8abf863 📝 Update changelog 2022-11-24 02:59:33 +00:00
p0ise
f8cf7c94ae 🍻 publish plugin 谁在窥屏 (#1415) 2022-11-24 10:58:29 +08:00
github-actions[bot]
bef494615f 📝 Update changelog 2022-11-24 02:58:06 +00:00
那个小白白白
6e110e725e 📝 Docs: 添加 ntchat 社区适配器 (#1414)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-11-24 10:56:53 +08:00
Ju4tCode
85390a14b6 ⬆️ upgrade dependencies (#1413) 2022-11-21 19:59:39 +08:00
github-actions[bot]
276041e314 📝 Update changelog 2022-11-21 10:46:01 +00:00
Ju4tCode
2922da7b2f Feature: 支持自定义 matchers 存储管理 (#1395) 2022-11-21 18:44:55 +08:00
github-actions[bot]
c783ab5e9b 📝 Update changelog 2022-11-20 09:42:50 +00:00
ZYKsslm
139190bff7 🍻 publish plugin 免费版NovelAI生图插件 (#1407) 2022-11-20 17:41:05 +08:00
github-actions[bot]
1524434444 📝 Update changelog 2022-11-19 16:05:11 +00:00
Ikaros
b6857d59b8 ✏️ 更新 nonebot_plugin_searchBiliInfo 插件标题和描述 (#1410) 2022-11-20 00:03:54 +08:00
github-actions[bot]
a7b0eb10a0 📝 Update changelog 2022-11-19 08:15:27 +00:00
synodriver
0eadb44e20 🐛 Fix: Bot __getattr__ 不再对 __xxx__ 方法返回 (#1398) 2022-11-19 16:14:03 +08:00
github-actions[bot]
6b43209d37 📝 Update changelog 2022-11-15 02:51:59 +00:00
Ju4tCode
a50990bef2 🐛 fix run pre/post hook not in context (#1391) 2022-11-15 10:50:52 +08:00
github-actions[bot]
f1525c1ecd 📝 Update changelog 2022-11-14 17:11:56 +00:00
Kaguya233qwq
7df9756205 🍻 publish plugin sky光遇 (#1393) 2022-11-15 01:10:50 +08:00
github-actions[bot]
376a720881 📝 Update changelog 2022-11-14 11:06:42 +00:00
Ju4tCode
c7377647fa Feature: 升级 devcontainer 配置 (#1392)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-11-14 19:05:34 +08:00
github-actions[bot]
cfbd6f1e4d 📝 Update changelog 2022-11-13 10:02:16 +00:00
EtherLeaF
cecc853f25 🍻 publish plugin Colab-NovelAI (#1389) 2022-11-13 18:01:08 +08:00
github-actions[bot]
fe5f85517e 📝 Update changelog 2022-11-13 09:38:21 +00:00
Ikaros-521
66040a7e44 🍻 publish plugin b站用户直播号、粉丝、舰团数查询 (#1384) 2022-11-13 17:37:22 +08:00
github-actions[bot]
81d2f017f6 📝 Update changelog 2022-11-12 10:23:32 +00:00
Akirami
4355025f87 Feature: 使用 importlib.metadata 替换 pkg_resources (#1388)
Co-authored-by: yanyongyu <42488585+yanyongyu@users.noreply.github.com>
2022-11-12 18:22:16 +08:00
github-actions[bot]
0bc8a39578 📝 Update changelog 2022-11-10 17:23:35 +00:00
Aziteee
7308f57776 🍻 publish plugin 投胎模拟器 (#1381) 2022-11-11 01:22:12 +08:00
github-actions[bot]
7de8912edb 📝 Update changelog 2022-11-09 15:55:14 +00:00
H-xiaoH
42a4edc3ee 🍻 publish plugin Apex API Query (#1374) 2022-11-09 23:54:00 +08:00
github-actions[bot]
4a12429a38 📝 Update changelog 2022-11-09 14:38:12 +00:00
Qianyiovo
092f6d05f5 🍻 publish bot Bread Dog Bot (#1379) 2022-11-09 22:37:07 +08:00
github-actions[bot]
2c0c05dca1 📝 Update changelog 2022-11-09 14:34:19 +00:00
Sena
31bafc832f 🍻 Plugin: Sena-nana项目拆分,地址更改 (#1378) 2022-11-09 22:33:08 +08:00
github-actions[bot]
e1720d8ea0 📝 Update changelog 2022-11-06 16:04:17 +00:00
jcjrobert
08be5724b9 🍻 publish plugin 随个人 (#1372) 2022-11-07 00:03:00 +08:00
github-actions[bot]
828714a4e3 📝 Update changelog 2022-11-06 06:58:29 +00:00
Melodyknit
a79eeb73a6 🍻 publish plugin 动漫资源获取 (#1370) 2022-11-06 14:57:18 +08:00
github-actions[bot]
c1aec637d5 📝 Update changelog 2022-11-04 07:19:58 +00:00
ssttkkl
12cc08efbd 🍻 publish plugin 日麻小工具 (#1364) 2022-11-04 15:18:35 +08:00
github-actions[bot]
f410af72dc 📝 Update changelog 2022-11-04 01:30:33 +00:00
StarHeart
113021cdf4 👷 CI: 测试环境添加 Python 3.11 (#1366)
Co-authored-by: yanyongyu <42488585+yanyongyu@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-11-04 09:29:13 +08:00
github-actions[bot]
fcc23f98f8 📝 Update changelog 2022-11-03 11:45:38 +00:00
gzy02
0b693c419e 🍻 publish bot hsbot (#1368) 2022-11-03 19:44:34 +08:00
github-actions[bot]
36e5b81510 📝 Update changelog 2022-11-02 17:05:59 +00:00
A-kirami
71f17bebaa 🍻 publish plugin 图像超分辨率增强 (#1361) 2022-11-03 01:04:38 +08:00
github-actions[bot]
2f45f25d13 📝 Update changelog 2022-11-02 16:57:54 +00:00
A-kirami
17d52446c3 🍻 publish plugin 二次元化图像 (#1359) 2022-11-03 00:56:36 +08:00
pre-commit-ci[bot]
2304aaf22b ⬆️ auto update by pre-commit hooks (#1358)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-11-01 14:12:53 +08:00
github-actions[bot]
a70c37de69 📝 Update changelog 2022-10-30 17:26:28 +00:00
ssttkkl
1cabc18277 🍻 publish plugin 日麻寄分器 (#1356) 2022-10-31 01:25:22 +08:00
github-actions[bot]
ed1235ed11 📝 Update changelog 2022-10-30 17:02:55 +00:00
MeetWq
9952b4e838 🍻 publish plugin 文本生成器 (#1354) 2022-10-31 01:01:33 +08:00
github-actions[bot]
e81390a104 📝 Update changelog 2022-10-30 16:16:02 +00:00
tkgs0
7bc22289b4 🍻 publish plugin 反嘴臭插件 (#1349) 2022-10-31 00:14:51 +08:00
github-actions[bot]
bfa4a079bf 📝 Update changelog 2022-10-28 07:06:09 +00:00
tkgs0
403850262b 🍻 publish plugin 用户&群聊黑名单 (#1347) 2022-10-28 15:05:08 +08:00
github-actions[bot]
ed25e7aa39 📝 Update changelog 2022-10-27 03:00:53 +00:00
ssttkkl
7ab480f044 🍻 publish plugin NoneBot SQLAlchemy封装 (#1344) 2022-10-27 10:59:36 +08:00
github-actions[bot]
d7d6152094 📝 Update changelog 2022-10-27 02:57:35 +00:00
bridgeL
2954e58c77 Plugin: 更新 ayaka 插件的主页链接 (#1346)
Co-authored-by: Su <wxlxy316@163.com>
2022-10-27 10:56:23 +08:00
github-actions[bot]
f684b96433 📝 Update changelog 2022-10-24 02:25:32 +00:00
jcjrobert
4388d1c9e6 🍻 publish plugin 通用抽图/语音 (#1340) 2022-10-24 10:24:15 +08:00
github-actions[bot]
876acf3e88 📝 Update changelog 2022-10-20 13:19:39 +00:00
Kaguya233qwq
963e73f517 🍻 publish plugin kfcrazy (#1338) 2022-10-20 21:18:27 +08:00
github-actions[bot]
bdc9e44142 📝 Update changelog 2022-10-20 03:14:13 +00:00
A-kirami
4f2efb7304 🍻 publish plugin 二次元图像鉴赏 (#1336) 2022-10-20 11:12:51 +08:00
github-actions[bot]
6ae891124f 📝 Update changelog 2022-10-19 11:27:42 +00:00
bridgeL
145bd4d4e1 🍻 publish plugin ayaka衍生插件 - 坏词撤回 (#1334) 2022-10-19 19:26:39 +08:00
github-actions[bot]
ab54049909 📝 Update changelog 2022-10-18 14:07:59 +00:00
Sena
7aa554f5a2 ✏️ Plugin: 补充 novelai 插件信息 (#1333) 2022-10-18 22:06:47 +08:00
github-actions[bot]
5566777374 📝 Update changelog 2022-10-18 09:57:33 +00:00
bridgeL
b2594f61de 🍻 publish plugin ayaka衍生插件 - 时区助手 (#1331) 2022-10-18 17:56:25 +08:00
github-actions[bot]
1ad1e0606c 📝 Update changelog 2022-10-18 07:07:05 +00:00
bridgeL
583d5060db 🍻 publish plugin ayaka衍生插件 - 谁是卧底 (#1329) 2022-10-18 15:05:45 +08:00
github-actions[bot]
eaa3dbdfa8 📝 Update changelog 2022-10-17 03:00:47 +00:00
bridgeL
03f378690a 🍻 publish plugin ayaka衍生插件 - 小游戏合集 (#1327) 2022-10-17 10:59:43 +08:00
github-actions[bot]
512c66ccc0 📝 Update changelog 2022-10-15 07:07:21 +00:00
lgc2333
9d20c7510a 🍻 publish plugin bnhhsh -「不能好好说话!」 (#1325) 2022-10-15 15:06:04 +08:00
github-actions[bot]
29ad8a6686 📝 Update changelog 2022-10-14 01:59:51 +00:00
Ju4tCode
db534b8824 Feature: 新增 dotenv 嵌套配置项支持 (#1324)
Co-authored-by: hemengyang <hmy0119@hotmail.com>
2022-10-14 09:58:44 +08:00
github-actions[bot]
67b96528af 📝 Update changelog 2022-10-13 04:47:18 +00:00
A-kirami
a5929f80f7 🍻 publish plugin AI绘图 (#1322) 2022-10-13 12:45:56 +08:00
github-actions[bot]
9619477a27 📝 Update changelog 2022-10-12 05:42:41 +00:00
Akirami
8377680fd7 Feature: 添加 State 响应器触发消息注入 (#1315) 2022-10-12 13:41:28 +08:00
github-actions[bot]
3e3d6f91a5 📝 Update changelog 2022-10-12 03:05:31 +00:00
sena-nana
1d3d886004 🍻 publish plugin novelai (#1318) 2022-10-12 11:04:34 +08:00
github-actions[bot]
ebc5a3cc9e 📝 Update changelog 2022-10-12 02:53:57 +00:00
Kaguyaya
1092767a51 🍻 publish plugin 游戏王小程序查价 (#1316) 2022-10-12 10:52:56 +08:00
github-actions[bot]
ab227ee64b 📝 Update changelog 2022-10-11 11:53:01 +00:00
cjladmin
00b37fb3d2 🍻 publish plugin 监测群事件 (#900) 2022-10-11 19:51:56 +08:00
github-actions[bot]
e6494dc98e 📝 Update changelog 2022-10-10 14:06:29 +00:00
JustUndertaker
c80869f952 🍻 publish adapter Ntchat (#1313) 2022-10-10 22:05:27 +08:00
github-actions[bot]
2be72eac5e 📝 Update changelog 2022-10-10 12:36:47 +00:00
HornCopper
830c4f8c6a 🍻 Bot: 修改 Inkar Suki 描述 (#1312) 2022-10-10 20:35:45 +08:00
github-actions[bot]
138fb458b7 📝 Update changelog 2022-10-08 08:18:40 +00:00
KarisAya
2c93f82ef3 🍻 publish plugin 轮盘禁言小游戏 (#1310) 2022-10-08 16:17:42 +08:00
github-actions[bot]
77aa16c2fc 📝 Update changelog 2022-10-07 02:33:18 +00:00
Ankhyty
945da7151e 🍻 publish plugin 真白萌自动签到 (#1307) 2022-10-07 10:32:15 +08:00
github-actions[bot]
41ea0df0a5 📝 Update changelog 2022-10-06 03:51:25 +00:00
Ju4tCode
cec45cf89c ⚰️ remove dead namespace (#1306) 2022-10-06 11:50:18 +08:00
Ju4tCode
2de8c66c70 ⬆️ upgrade pydantic dependency (#1305) 2022-10-05 15:13:54 +08:00
github-actions[bot]
0bcc4277e5 📝 Update changelog 2022-10-05 02:54:10 +00:00
Shadow403
d3dd93b36c 🍻 publish plugin BiliRequestAll (#1301) 2022-10-05 10:53:00 +08:00
github-actions[bot]
e9bd81d9bb 📝 Update changelog 2022-10-03 11:45:36 +00:00
17TheWord
997d4f5042 ✏️ 修改插件 MCQQ 主页地址 (#1303) 2022-10-03 19:44:21 +08:00
github-actions[bot]
3bb321c519 📝 Update changelog 2022-10-03 11:42:16 +00:00
AbCooly
fe92d29322 🍻 publish plugin 监听者 (#1298) 2022-10-03 19:41:11 +08:00
147 changed files with 5253 additions and 1880 deletions

View File

@@ -1,15 +0,0 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.238.1/containers/codespaces-linux/.devcontainer/base.Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/universal:2-focal
# ** [Optional] Uncomment this section to install additional packages. **
# USER root
#
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
USER codespace
# [Required] Poetry
RUN curl -sSL https://install.python-poetry.org | python - -y
RUN poetry config virtualenvs.in-project true

View File

@@ -1,32 +1,13 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.1/containers/codespaces-linux
{ {
"name": "GitHub Codespaces (Default)", "name": "Default Linux Universal",
"image": "mcr.microsoft.com/devcontainers/universal:2-linux",
"build": { "features": {
"dockerfile": "Dockerfile" "ghcr.io/devcontainers-contrib/features/poetry:1": {}
}, },
"postCreateCommand": "poetry config virtualenvs.in-project true && poetry install -E all && poetry run pre-commit install && yarn install",
// Configure tool-specific properties.
"customizations": { "customizations": {
// Configure properties specific to VS Code.
"vscode": { "vscode": {
// Set *default* container specific settings.json values on container create.
"settings": { "settings": {
"go.toolsManagement.checkForUpdates": "local",
"go.useLanguageServer": true,
"go.gopath": "/go",
"python.defaultInterpreterPath": "/opt/python/latest/bin/python",
"python.linting.enabled": true,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint",
"python.analysis.diagnosticMode": "workspace", "python.analysis.diagnosticMode": "workspace",
"python.analysis.typeCheckingMode": "basic", "python.analysis.typeCheckingMode": "basic",
"[python]": { "[python]": {
@@ -50,7 +31,6 @@
"[typescriptreact]": { "[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"lldb.executable": "/usr/bin/lldb",
"files.exclude": { "files.exclude": {
"**/__pycache__": true "**/__pycache__": true
}, },
@@ -59,10 +39,7 @@
"**/__pycache__": true "**/__pycache__": true
} }
}, },
// Add the IDs of extensions you want installed when the container is created.
"extensions": [ "extensions": [
"GitHub.vscode-pull-request-github",
"ms-python.python", "ms-python.python",
"ms-python.vscode-pylance", "ms-python.vscode-pylance",
"ms-python.isort", "ms-python.isort",
@@ -72,27 +49,5 @@
"bradlc.vscode-tailwindcss" "bradlc.vscode-tailwindcss"
] ]
} }
}, }
"remoteUser": "codespace",
"overrideCommand": false,
"mounts": [
"source=codespaces-linux-var-lib-docker,target=/var/lib/docker,type=volume"
],
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt",
"seccomp=unconfined",
"--privileged",
"--init"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// "oryx build" will automatically install your dependencies and attempt to build your project
"postCreateCommand": "poetry install && poetry run pre-commit install && yarn install"
} }

View File

@@ -9,10 +9,10 @@ runs:
node-version: "16" node-version: "16"
- id: yarn-cache-dir-path - id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
shell: bash shell: bash
- uses: actions/cache@v2 - uses: actions/cache@v3
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}

View File

@@ -10,22 +10,15 @@ inputs:
runs: runs:
using: "composite" using: "composite"
steps: steps:
- id: python - name: Install poetry
uses: actions/setup-python@v2 run: pipx install poetry
shell: bash
- uses: actions/setup-python@v4
with: with:
python-version: ${{ inputs.python-version }} python-version: ${{ inputs.python-version }}
architecture: "x64" architecture: "x64"
cache: "poetry"
- uses: Gr1N/setup-poetry@v7
- id: poetry-cache
run: echo "::set-output name=dir::$(poetry config virtualenvs.path)"
shell: bash
- uses: actions/cache@v2
with:
path: ${{ steps.poetry-cache.outputs.dir }}
key: ${{ runner.os }}-poetry-${{ steps.python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
- run: poetry install -E all - run: poetry install -E all
shell: bash shell: bash

View File

@@ -5,6 +5,10 @@ on:
branches: branches:
- master - master
pull_request: pull_request:
paths:
- "nonebot/**"
- "packages/**"
- "tests/**"
jobs: jobs:
test: test:
@@ -15,7 +19,7 @@ jobs:
cancel-in-progress: true cancel-in-progress: true
strategy: strategy:
matrix: matrix:
python-version: ["3.8", "3.9", "3.10"] python-version: ["3.8", "3.9", "3.10", "3.11"]
os: [ubuntu-latest, windows-latest, macos-latest] os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false fail-fast: false
env: env:

View File

@@ -5,17 +5,45 @@ on:
types: [opened, reopened, edited] types: [opened, reopened, edited]
pull_request_target: pull_request_target:
types: [closed] types: [closed]
issue_comment:
types: [created]
concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number || github.run_id }}
cancel-in-progress: true
jobs: jobs:
plugin_test:
runs-on: ubuntu-latest
name: nonebot2 plugin test
if: github.event_name != 'issue_comment' || !github.event.issue.pull_request
permissions:
issues: read
outputs:
result: ${{ steps.plugin-test.outputs.RESULT }}
output: ${{ steps.plugin-test.outputs.OUTPUT }}
steps:
- name: Install Poetry
if: ${{ !startsWith(github.event_name, 'pull_request') }}
run: pipx install poetry
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.10"
- name: Test Plugin
id: plugin-test
run: |
curl -sSL https://raw.githubusercontent.com/nonebot/nonebot2-publish-bot/main/plugin_test.py -o plugin_test.py
python plugin_test.py
publish_bot: publish_bot:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: nonebot2 publish bot name: nonebot2 publish bot
needs: plugin_test
steps: steps:
- name: Checkout code - name: Checkout Code
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
- name: NoneBot2 Publish Bot - name: NoneBot2 Publish Bot
uses: docker://ghcr.io/nonebot/nonebot2-publish-bot:main uses: docker://ghcr.io/nonebot/nonebot2-publish-bot:main
with: with:
@@ -27,3 +55,6 @@ jobs:
"bot_path": "website/static/bots.json", "bot_path": "website/static/bots.json",
"adapter_path": "website/static/adapters.json" "adapter_path": "website/static/adapters.json"
} }
env:
PLUGIN_TEST_RESULT: ${{ needs.plugin_test.outputs.result }}
PLUGIN_TEST_OUTPUT: ${{ needs.plugin_test.outputs.output }}

View File

@@ -31,7 +31,7 @@ jobs:
run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV
- name: Deploy to Netlify - name: Deploy to Netlify
uses: nwtgck/actions-netlify@v1 uses: nwtgck/actions-netlify@v2
with: with:
publish-dir: "./website/build" publish-dir: "./website/build"
production-deploy: true production-deploy: true

View File

@@ -32,7 +32,7 @@ jobs:
echo "DEPLOY_NAME=deploy-preview-${{ github.event.number }}" >> $GITHUB_ENV echo "DEPLOY_NAME=deploy-preview-${{ github.event.number }}" >> $GITHUB_ENV
- name: Deploy to Netlify - name: Deploy to Netlify
uses: nwtgck/actions-netlify@v1 uses: nwtgck/actions-netlify@v2
with: with:
publish-dir: "./website/build" publish-dir: "./website/build"
production-deploy: false production-deploy: false

View File

@@ -6,23 +6,29 @@ ci:
autoupdate_schedule: monthly autoupdate_schedule: monthly
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks" autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
repos: repos:
- repo: https://github.com/hadialqattan/pycln
rev: v2.1.2
hooks:
- id: pycln
args: [--config, pyproject.toml]
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort
rev: 5.10.1 rev: 5.11.4
hooks: hooks:
- id: isort - id: isort
stages: [commit] stages: [commit]
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 22.8.0 rev: 22.12.0
hooks: hooks:
- id: black - id: black
stages: [commit] stages: [commit]
- repo: https://github.com/pre-commit/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0-alpha.0 rev: v3.0.0-alpha.4
hooks: hooks:
- id: prettier - id: prettier
types_or: [javascript, jsx, ts, tsx, markdown, yaml] types_or: [javascript, jsx, ts, tsx, markdown, yaml, json]
stages: [commit] stages: [commit]
- repo: https://github.com/nonebot/nonemoji - repo: https://github.com/nonebot/nonemoji

View File

@@ -33,7 +33,7 @@ pre-commit install
### 使用 GitHub CodespacesDev Container ### 使用 GitHub CodespacesDev Container
使用 GitHub Codespaces 选择 `NoneBot2` 项目,然后选择 `.devcontainer/devcontainer.json` 配置即可。 [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=master&repo=289605524)
### Commit 规范 ### Commit 规范

View File

@@ -97,7 +97,7 @@ NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架
- 海纳百川:一个框架,支持多个聊天软件平台,可自定义通信协议 - 海纳百川:一个框架,支持多个聊天软件平台,可自定义通信协议
| 协议名称 | 状态 | 注释 | | 协议名称 | 状态 | 注释 |
| :---------------------------------------------------: | :--: | :----------------------------------------------------------------: | | :-----------------------------------------------------------------------: | :--: | :----------------------------------------------------------------: |
| [OneBot 协议](https://onebot.dev/) | ✅ | 支持 QQ、TG、微信公众号等[平台](https://onebot.dev/ecosystem.html) | | [OneBot 协议](https://onebot.dev/) | ✅ | 支持 QQ、TG、微信公众号等[平台](https://onebot.dev/ecosystem.html) |
| [Telegram](https://core.telegram.org/bots/api) | ✅ | | | [Telegram](https://core.telegram.org/bots/api) | ✅ | |
| [飞书](https://open.feishu.cn/document/home/index) | ✅ | | | [飞书](https://open.feishu.cn/document/home/index) | ✅ | |
@@ -107,6 +107,9 @@ NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架
| Console | ✅ | 控制台交互 | | Console | ✅ | 控制台交互 |
| [开黑啦](https://developer.kookapp.cn/) | ↗️ | 由社区贡献 | | [开黑啦](https://developer.kookapp.cn/) | ↗️ | 由社区贡献 |
| [Mirai](https://docs.mirai.mamoe.net/mirai-api-http/) | ↗️ | 由社区贡献 | | [Mirai](https://docs.mirai.mamoe.net/mirai-api-http/) | ↗️ | 由社区贡献 |
| [Ntchat](https://github.com/JustUndertaker/adapter-ntchat) | ↗️ | 由社区贡献 |
| [MineCraft (Spigot)](https://github.com/17TheWord/nonebot-adapter-spigot) | ↗️ | 由社区贡献 |
| [BiliBili Live](https://github.com/wwweww/adapter-bilibili) | ↗️ | 由社区贡献 |
- 坚实后盾:支持多种 web 框架,可自定义替换、组合 - 坚实后盾:支持多种 web 框架,可自定义替换、组合
@@ -134,12 +137,17 @@ NoneBot2 不是 NoneBot1 的替代品。事实上,它们都在被积极的维
懒得看文档?下面是快速安装指南: 懒得看文档?下面是快速安装指南:
1. (**强烈建议**)使用你喜欢的 Python 环境管理工具创建新的虚拟环境。 1. 安装 [pipx](https://pypa.github.io/pipx/)
2. 使用 `pip` (或其他) 安装 NoneBot 脚手架。
```bash ```bash
pip install nb-cli python -m pip install --user pipx
python -m pipx ensurepath
```
2. 安装脚手架
```bash
pipx install nb-cli
``` ```
3. 使用脚手架创建项目 3. 使用脚手架创建项目
@@ -148,6 +156,12 @@ NoneBot2 不是 NoneBot1 的替代品。事实上,它们都在被积极的维
nb create nb create
``` ```
4. 运行项目
```bash
nb run
```
## 社区资源 ## 社区资源
### 常见问题 ### 常见问题

View File

@@ -37,26 +37,24 @@ FrontMatter:
description: nonebot 模块 description: nonebot 模块
""" """
import os
import importlib import importlib
from importlib.metadata import version
from typing import Any, Dict, Type, Optional from typing import Any, Dict, Type, Optional
import loguru import loguru
from pydantic.env_settings import DotenvType
from nonebot.log import logger
from nonebot.adapters import Bot from nonebot.adapters import Bot
from nonebot.utils import escape_tag from nonebot.utils import escape_tag
from nonebot.config import Env, Config from nonebot.config import Env, Config
from nonebot.log import logger as logger
from nonebot.drivers import Driver, ReverseDriver, combine_driver from nonebot.drivers import Driver, ReverseDriver, combine_driver
try: try:
import pkg_resources __version__ = version("nonebot2")
_dist: pkg_resources.Distribution = pkg_resources.get_distribution("nonebot2")
__version__ = _dist.version
VERSION = _dist.parsed_version
except Exception: # pragma: no cover except Exception: # pragma: no cover
__version__ = None __version__ = None
VERSION = None
_driver: Optional[Driver] = None _driver: Optional[Driver] = None
@@ -217,7 +215,7 @@ def _log_patcher(record: "loguru.Record"):
) )
def init(*, _env_file: Optional[str] = None, **kwargs: Any) -> None: def init(*, _env_file: Optional[DotenvType] = None, **kwargs: Any) -> None:
"""初始化 NoneBot 以及 全局 {ref}`nonebot.drivers.Driver` 对象。 """初始化 NoneBot 以及 全局 {ref}`nonebot.drivers.Driver` 对象。
NoneBot 将会从 .env 文件中读取环境信息,并使用相应的 env 文件配置。 NoneBot 将会从 .env 文件中读取环境信息,并使用相应的 env 文件配置。
@@ -237,10 +235,12 @@ def init(*, _env_file: Optional[str] = None, **kwargs: Any) -> None:
if not _driver: if not _driver:
logger.success("NoneBot is initializing...") logger.success("NoneBot is initializing...")
env = Env() env = Env()
_env_file = _env_file or f".env.{env.environment}"
config = Config( config = Config(
**kwargs, **kwargs,
_common_config=env.dict(), _env_file=(".env", _env_file)
_env_file=_env_file or f".env.{env.environment}", if isinstance(_env_file, (str, os.PathLike))
else _env_file,
) )
logger.configure( logger.configure(

View File

@@ -7,21 +7,6 @@ FrontMatter:
description: nonebot.adapters 模块 description: nonebot.adapters 模块
""" """
from typing import Iterable
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
del pkg_resources
except ImportError:
import pkgutil
__path__: Iterable[str] = pkgutil.extend_path(__path__, __name__) # type: ignore
del pkgutil
except Exception:
pass
from nonebot.internal.adapter import Bot as Bot from nonebot.internal.adapter import Bot as Bot
from nonebot.internal.adapter import Event as Event from nonebot.internal.adapter import Event as Event
from nonebot.internal.adapter import Adapter as Adapter from nonebot.internal.adapter import Adapter as Adapter

View File

@@ -9,19 +9,18 @@ FrontMatter:
description: nonebot.config 模块 description: nonebot.config 模块
""" """
import os import os
from pathlib import Path
from datetime import timedelta from datetime import timedelta
from ipaddress import IPv4Address from ipaddress import IPv4Address
from typing import TYPE_CHECKING, Any, Set, Dict, Tuple, Union, Mapping, Optional from typing import TYPE_CHECKING, Any, Set, Dict, Tuple, Union, Mapping, Optional
from pydantic import BaseSettings, IPvAnyAddress from pydantic.utils import deep_update
from pydantic import Extra, BaseSettings, IPvAnyAddress
from pydantic.env_settings import ( from pydantic.env_settings import (
DotenvType,
SettingsError, SettingsError,
EnvSettingsSource, EnvSettingsSource,
InitSettingsSource, InitSettingsSource,
SettingsSourceCallable, SettingsSourceCallable,
read_env_file,
env_file_sentinel,
) )
from nonebot.log import logger from nonebot.log import logger
@@ -32,32 +31,14 @@ class CustomEnvSettings(EnvSettingsSource):
""" """
Build environment variables suitable for passing to the Model. Build environment variables suitable for passing to the Model.
""" """
d: Dict[str, Optional[str]] = {} d: Dict[str, Any] = {}
if settings.__config__.case_sensitive: if settings.__config__.case_sensitive:
env_vars: Mapping[str, Optional[str]] = os.environ # pragma: no cover env_vars: Mapping[str, Optional[str]] = os.environ # pragma: no cover
else: else:
env_vars = {k.lower(): v for k, v in os.environ.items()} env_vars = {k.lower(): v for k, v in os.environ.items()}
env_file_vars: Dict[str, Optional[str]] = {} env_file_vars = self._read_env_files(settings.__config__.case_sensitive)
env_file = (
self.env_file
if self.env_file != env_file_sentinel
else settings.__config__.env_file
)
env_file_encoding = (
self.env_file_encoding
if self.env_file_encoding is not None
else settings.__config__.env_file_encoding
)
if env_file is not None:
env_path = Path(env_file)
if env_path.is_file():
env_file_vars = read_env_file(
env_path,
encoding=env_file_encoding, # type: ignore
case_sensitive=settings.__config__.case_sensitive,
)
env_vars = {**env_file_vars, **env_vars} env_vars = {**env_file_vars, **env_vars}
for field in settings.__fields__.values(): for field in settings.__fields__.values():
@@ -69,24 +50,38 @@ class CustomEnvSettings(EnvSettingsSource):
if env_val is not None: if env_val is not None:
break break
is_complex, allow_parse_failure = self.field_is_complex(field)
if is_complex:
if env_val is None: if env_val is None:
continue if env_val_built := self.explode_env_vars(field, env_vars):
d[field.alias] = env_val_built
if field.is_complex(): else:
# field is complex and there's a value, decode that as JSON, then add explode_env_vars
try: try:
env_val = settings.__config__.json_loads(env_val) env_val = settings.__config__.parse_env_var(field.name, env_val)
except ValueError as e: # pragma: no cover except ValueError as e:
if not allow_parse_failure:
raise SettingsError( raise SettingsError(
f'error parsing JSON for "{env_name}"' # type: ignore f'error parsing env var "{env_name}"' # type: ignore
) from e ) from e
if isinstance(env_val, dict):
d[field.alias] = deep_update(
env_val, self.explode_env_vars(field, env_vars)
)
else:
d[field.alias] = env_val
elif env_val is not None:
# simplest case, field is not complex, we only need to add the value if it was found
d[field.alias] = env_val d[field.alias] = env_val
if env_file_vars: # remain user custom config
for env_name in env_file_vars.keys(): for env_name in env_file_vars:
env_val = env_vars[env_name] env_val = env_vars[env_name]
if env_val and (val_striped := env_val.strip()): if env_val and (val_striped := env_val.strip()):
# there's a value, decode that as JSON
try: try:
env_val = settings.__config__.json_loads(val_striped) env_val = settings.__config__.parse_env_var(env_name, val_striped)
except ValueError as e: except ValueError as e:
logger.trace( logger.trace(
"Error while parsing JSON for " "Error while parsing JSON for "
@@ -94,6 +89,17 @@ class CustomEnvSettings(EnvSettingsSource):
"Assumed as string." "Assumed as string."
) )
# explode value when it's a nested dict
env_name, *nested_keys = env_name.split(self.env_nested_delimiter)
if nested_keys and (env_name not in d or isinstance(d[env_name], dict)):
result = {}
*keys, last_key = nested_keys
_tmp = result
for key in keys:
_tmp = _tmp.setdefault(key, {})
_tmp[last_key] = env_val
d[env_name] = deep_update(d.get(env_name, {}), result)
elif not nested_keys:
d[env_name] = env_val d[env_name] = env_val
return d return d
@@ -106,6 +112,9 @@ class BaseConfig(BaseSettings):
return self.__dict__.get(name) return self.__dict__.get(name)
class Config: class Config:
extra = Extra.allow
env_nested_delimiter = "__"
@classmethod @classmethod
def customise_sources( def customise_sources(
cls, cls,
@@ -117,7 +126,10 @@ class BaseConfig(BaseSettings):
return ( return (
init_settings, init_settings,
CustomEnvSettings( CustomEnvSettings(
env_settings.env_file, env_settings.env_file_encoding env_settings.env_file,
env_settings.env_file_encoding,
env_settings.env_nested_delimiter,
env_settings.env_prefix_len,
), ),
InitSettingsSource(common_config), InitSettingsSource(common_config),
file_secret_settings, file_secret_settings,
@@ -137,7 +149,6 @@ class Env(BaseConfig):
""" """
class Config: class Config:
extra = "allow"
env_file = ".env" env_file = ".env"
@@ -150,8 +161,7 @@ class Config(BaseConfig):
配置方法参考: [配置](https://v2.nonebot.dev/docs/tutorial/configuration) 配置方法参考: [配置](https://v2.nonebot.dev/docs/tutorial/configuration)
""" """
_env_file: str = ".env" _env_file: DotenvType = ".env", ".env.prod"
_common_config: Dict[str, Any] = {}
# nonebot configs # nonebot configs
driver: str = "~fastapi" driver: str = "~fastapi"
@@ -231,8 +241,7 @@ class Config(BaseConfig):
# or from env file using json loads # or from env file using json loads
class Config: class Config:
extra = "allow" env_file = ".env", ".env.prod"
env_file = ".env.prod"
__autodoc__ = { __autodoc__ = {

View File

@@ -38,7 +38,17 @@ SHELL_ARGV: Literal["_argv"] = "_argv"
REGEX_MATCHED: Literal["_matched"] = "_matched" REGEX_MATCHED: Literal["_matched"] = "_matched"
"""正则匹配结果存储 key""" """正则匹配结果存储 key"""
REGEX_STR: Literal["_matched_str"] = "_matched_str"
"""正则匹配文本存储 key"""
REGEX_GROUP: Literal["_matched_groups"] = "_matched_groups" REGEX_GROUP: Literal["_matched_groups"] = "_matched_groups"
"""正则匹配 group 元组存储 key""" """正则匹配 group 元组存储 key"""
REGEX_DICT: Literal["_matched_dict"] = "_matched_dict" REGEX_DICT: Literal["_matched_dict"] = "_matched_dict"
"""正则匹配 group 字典存储 key""" """正则匹配 group 字典存储 key"""
STARTSWITH_KEY: Literal["_startswith"] = "_startswith"
"""响应触发前缀 key"""
ENDSWITH_KEY: Literal["_endswith"] = "_endswith"
"""响应触发后缀 key"""
FULLMATCH_KEY: Literal["_fullmatch"] = "_fullmatch"
"""响应触发完整消息 key"""
KEYWORD_KEY: Literal["_keyword"] = "_keyword"
"""响应触发关键字 key"""

View File

@@ -4,11 +4,11 @@ FrontMatter:
description: nonebot.dependencies.utils 模块 description: nonebot.dependencies.utils 模块
""" """
import inspect import inspect
from typing import Any, Dict, TypeVar, Callable from typing import Any, Dict, TypeVar, Callable, ForwardRef
from loguru import logger from loguru import logger
from pydantic.fields import ModelField from pydantic.fields import ModelField
from pydantic.typing import ForwardRef, evaluate_forwardref from pydantic.typing import evaluate_forwardref
from nonebot.exception import TypeMisMatch from nonebot.exception import TypeMisMatch

View File

@@ -21,7 +21,7 @@ from contextlib import asynccontextmanager
from nonebot.typing import overrides from nonebot.typing import overrides
from nonebot.drivers import Request, Response from nonebot.drivers import Request, Response
from nonebot.exception import WebSocketClosed from nonebot.exception import WebSocketClosed
from nonebot.drivers._block_driver import BlockDriver from nonebot.drivers.none import Driver as NoneDriver
from nonebot.drivers import WebSocket as BaseWebSocket from nonebot.drivers import WebSocket as BaseWebSocket
from nonebot.drivers import HTTPVersion, ForwardMixin, ForwardDriver, combine_driver from nonebot.drivers import HTTPVersion, ForwardMixin, ForwardDriver, combine_driver
@@ -56,7 +56,13 @@ class Mixin(ForwardMixin):
files = aiohttp.FormData() files = aiohttp.FormData()
for name, file in setup.files: for name, file in setup.files:
files.add_field(name, file[1], content_type=file[2], filename=file[0]) files.add_field(name, file[1], content_type=file[2], filename=file[0])
async with aiohttp.ClientSession(version=version, trust_env=True) as session:
cookies = {
cookie.name: cookie.value for cookie in setup.cookies if cookie.value
}
async with aiohttp.ClientSession(
cookies=cookies, version=version, trust_env=True
) as session:
async with session.request( async with session.request(
setup.method, setup.method,
setup.url, setup.url,
@@ -66,13 +72,12 @@ class Mixin(ForwardMixin):
timeout=timeout, timeout=timeout,
proxy=setup.proxy, proxy=setup.proxy,
) as response: ) as response:
res = Response( return Response(
response.status, response.status,
headers=response.headers.copy(), headers=response.headers.copy(),
content=await response.read(), content=await response.read(),
request=setup, request=setup,
) )
return res
@overrides(ForwardMixin) @overrides(ForwardMixin)
@asynccontextmanager @asynccontextmanager
@@ -92,8 +97,7 @@ class Mixin(ForwardMixin):
headers=setup.headers, headers=setup.headers,
proxy=setup.proxy, proxy=setup.proxy,
) as ws: ) as ws:
websocket = WebSocket(request=setup, session=session, websocket=ws) yield WebSocket(request=setup, session=session, websocket=ws)
yield websocket
class WebSocket(BaseWebSocket): class WebSocket(BaseWebSocket):
@@ -166,5 +170,5 @@ class WebSocket(BaseWebSocket):
await self.websocket.send_bytes(data) await self.websocket.send_bytes(data)
Driver: Type[ForwardDriver] = combine_driver(BlockDriver, Mixin) # type: ignore Driver: Type[ForwardDriver] = combine_driver(NoneDriver, Mixin) # type: ignore
"""AIOHTTP Driver""" """AIOHTTP Driver"""

View File

@@ -1,5 +1,11 @@
"""[FastAPI](https://fastapi.tiangolo.com/) 驱动适配 """[FastAPI](https://fastapi.tiangolo.com/) 驱动适配
```bash
nb driver install fastapi
# 或者
pip install nonebot2[fastapi]
```
:::tip 提示 :::tip 提示
本驱动仅支持服务端连接 本驱动仅支持服务端连接
::: :::
@@ -13,13 +19,9 @@ FrontMatter:
import logging import logging
import contextlib import contextlib
from functools import wraps from functools import wraps
from typing import Any, List, Tuple, Union, Callable, Optional from typing import Any, Dict, List, Tuple, Union, Callable, Optional
import uvicorn
from pydantic import BaseSettings from pydantic import BaseSettings
from fastapi.responses import Response
from fastapi import FastAPI, Request, UploadFile, status
from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect
from nonebot.config import Env from nonebot.config import Env
from nonebot.typing import overrides from nonebot.typing import overrides
@@ -30,6 +32,16 @@ from nonebot.drivers import Request as BaseRequest
from nonebot.drivers import WebSocket as BaseWebSocket from nonebot.drivers import WebSocket as BaseWebSocket
from nonebot.drivers import ReverseDriver, HTTPServerSetup, WebSocketServerSetup from nonebot.drivers import ReverseDriver, HTTPServerSetup, WebSocketServerSetup
try:
import uvicorn
from fastapi.responses import Response
from fastapi import FastAPI, Request, UploadFile, status
from starlette.websockets import WebSocket, WebSocketState, WebSocketDisconnect
except ImportError: # pragma: no cover
raise ImportError(
"Please install FastAPI by using `pip install nonebot2[fastapi]`"
) from None
def catch_closed(func): def catch_closed(func):
@wraps(func) @wraps(func)
@@ -65,6 +77,8 @@ class Config(BaseSettings):
"""要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值""" """要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
fastapi_reload_excludes: Optional[List[str]] = None fastapi_reload_excludes: Optional[List[str]] = None
"""不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值""" """不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
fastapi_extra: Dict[str, Any] = {}
"""传递给 `FastAPI` 的其他参数。"""
class Config: class Config:
extra = "ignore" extra = "ignore"
@@ -82,6 +96,7 @@ class Driver(ReverseDriver):
openapi_url=self.fastapi_config.fastapi_openapi_url, openapi_url=self.fastapi_config.fastapi_openapi_url,
docs_url=self.fastapi_config.fastapi_docs_url, docs_url=self.fastapi_config.fastapi_docs_url,
redoc_url=self.fastapi_config.fastapi_redoc_url, redoc_url=self.fastapi_config.fastapi_redoc_url,
**self.fastapi_config.fastapi_extra,
) )
@property @property
@@ -262,7 +277,7 @@ class FastAPIWebSocket(BaseWebSocket):
async def close( async def close(
self, code: int = status.WS_1000_NORMAL_CLOSURE, reason: str = "" self, code: int = status.WS_1000_NORMAL_CLOSURE, reason: str = ""
) -> None: ) -> None:
await self.websocket.close(code) await self.websocket.close(code, reason)
@overrides(BaseWebSocket) @overrides(BaseWebSocket)
async def receive(self) -> Union[str, bytes]: async def receive(self) -> Union[str, bytes]:

View File

@@ -18,7 +18,7 @@ from typing import Type, AsyncGenerator
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from nonebot.typing import overrides from nonebot.typing import overrides
from nonebot.drivers._block_driver import BlockDriver from nonebot.drivers.none import Driver as NoneDriver
from nonebot.drivers import ( from nonebot.drivers import (
Request, Request,
Response, Response,
@@ -48,17 +48,18 @@ class Mixin(ForwardMixin):
@overrides(ForwardMixin) @overrides(ForwardMixin)
async def request(self, setup: Request) -> Response: async def request(self, setup: Request) -> Response:
async with httpx.AsyncClient( async with httpx.AsyncClient(
cookies=setup.cookies.jar,
http2=setup.version == HTTPVersion.H2, http2=setup.version == HTTPVersion.H2,
proxies=setup.proxy, # type: ignore proxies=setup.proxy,
follow_redirects=True, follow_redirects=True,
) as client: ) as client:
response = await client.request( response = await client.request(
setup.method, setup.method,
str(setup.url), str(setup.url),
content=setup.content, # type: ignore content=setup.content,
data=setup.data, # type: ignore data=setup.data,
json=setup.json, json=setup.json,
files=setup.files, # type: ignore files=setup.files,
headers=tuple(setup.headers.items()), headers=tuple(setup.headers.items()),
timeout=setup.timeout, timeout=setup.timeout,
) )
@@ -76,5 +77,5 @@ class Mixin(ForwardMixin):
yield ws yield ws
Driver: Type[ForwardDriver] = combine_driver(BlockDriver, Mixin) # type: ignore Driver: Type[ForwardDriver] = combine_driver(NoneDriver, Mixin) # type: ignore
"""HTTPX Driver""" """HTTPX Driver"""

View File

@@ -1,12 +1,24 @@
"""None 驱动适配
:::tip 提示
本驱动不支持任何服务器或客户端连接
:::
FrontMatter:
sidebar_position: 6
description: nonebot.drivers.none 模块
"""
import signal import signal
import asyncio import asyncio
import threading import threading
from typing import Set, Union, Callable, Awaitable, cast from typing import Set, Union, Callable, Awaitable, cast
from nonebot.log import logger from nonebot.log import logger
from nonebot.drivers import Driver
from nonebot.typing import overrides from nonebot.typing import overrides
from nonebot.config import Env, Config from nonebot.config import Env, Config
from nonebot.drivers import Driver as BaseDriver
from nonebot.utils import run_sync, is_coroutine_callable from nonebot.utils import run_sync, is_coroutine_callable
HOOK_FUNC = Union[Callable[[], None], Callable[[], Awaitable[None]]] HOOK_FUNC = Union[Callable[[], None], Callable[[], Awaitable[None]]]
@@ -16,7 +28,9 @@ HANDLED_SIGNALS = (
) )
class BlockDriver(Driver): class Driver(BaseDriver):
"""None 驱动框架"""
def __init__(self, env: Env, config: Config): def __init__(self, env: Env, config: Config):
super().__init__(env, config) super().__init__(env, config)
self.startup_funcs: Set[HOOK_FUNC] = set() self.startup_funcs: Set[HOOK_FUNC] = set()
@@ -25,18 +39,18 @@ class BlockDriver(Driver):
self.force_exit: bool = False self.force_exit: bool = False
@property @property
@overrides(Driver) @overrides(BaseDriver)
def type(self) -> str: def type(self) -> str:
"""驱动名称: `block_driver`""" """驱动名称: `none`"""
return "block_driver" return "none"
@property @property
@overrides(Driver) @overrides(BaseDriver)
def logger(self): def logger(self):
"""block driver 使用的 logger""" """none driver 使用的 logger"""
return logger return logger
@overrides(Driver) @overrides(BaseDriver)
def on_startup(self, func: HOOK_FUNC) -> HOOK_FUNC: def on_startup(self, func: HOOK_FUNC) -> HOOK_FUNC:
""" """
注册一个启动时执行的函数 注册一个启动时执行的函数
@@ -44,7 +58,7 @@ class BlockDriver(Driver):
self.startup_funcs.add(func) self.startup_funcs.add(func)
return func return func
@overrides(Driver) @overrides(BaseDriver)
def on_shutdown(self, func: HOOK_FUNC) -> HOOK_FUNC: def on_shutdown(self, func: HOOK_FUNC) -> HOOK_FUNC:
""" """
注册一个停止时执行的函数 注册一个停止时执行的函数
@@ -52,22 +66,22 @@ class BlockDriver(Driver):
self.shutdown_funcs.add(func) self.shutdown_funcs.add(func)
return func return func
@overrides(Driver) @overrides(BaseDriver)
def run(self, *args, **kwargs): def run(self, *args, **kwargs):
"""启动 block driver""" """启动 none driver"""
super().run(*args, **kwargs) super().run(*args, **kwargs)
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
loop.run_until_complete(self.serve()) loop.run_until_complete(self._serve())
async def serve(self): async def _serve(self):
self.install_signal_handlers() self._install_signal_handlers()
await self.startup() await self._startup()
if self.should_exit.is_set(): if self.should_exit.is_set():
return return
await self.main_loop() await self._main_loop()
await self.shutdown() await self._shutdown()
async def startup(self): async def _startup(self):
# run startup # run startup
cors = [ cors = [
cast(Callable[..., Awaitable[None]], startup)() cast(Callable[..., Awaitable[None]], startup)()
@@ -86,10 +100,10 @@ class BlockDriver(Driver):
logger.info("Application startup completed.") logger.info("Application startup completed.")
async def main_loop(self): async def _main_loop(self):
await self.should_exit.wait() await self.should_exit.wait()
async def shutdown(self): async def _shutdown(self):
logger.info("Shutting down") logger.info("Shutting down")
logger.info("Waiting for application shutdown.") logger.info("Waiting for application shutdown.")
@@ -130,7 +144,7 @@ class BlockDriver(Driver):
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
loop.stop() loop.stop()
def install_signal_handlers(self) -> None: def _install_signal_handlers(self) -> None:
if threading.current_thread() is not threading.main_thread(): if threading.current_thread() is not threading.main_thread():
# Signals can only be listened to from the main thread. # Signals can only be listened to from the main thread.
return return
@@ -139,13 +153,13 @@ class BlockDriver(Driver):
try: try:
for sig in HANDLED_SIGNALS: for sig in HANDLED_SIGNALS:
loop.add_signal_handler(sig, self.handle_exit, sig, None) loop.add_signal_handler(sig, self._handle_exit, sig, None)
except NotImplementedError: except NotImplementedError:
# Windows # Windows
for sig in HANDLED_SIGNALS: for sig in HANDLED_SIGNALS:
signal.signal(sig, self.handle_exit) signal.signal(sig, self._handle_exit)
def handle_exit(self, sig, frame): def _handle_exit(self, sig, frame):
if self.should_exit.is_set(): if self.should_exit.is_set():
self.force_exit = True self.force_exit = True
else: else:

View File

@@ -17,9 +17,8 @@ FrontMatter:
import asyncio import asyncio
from functools import wraps from functools import wraps
from typing import List, Tuple, Union, TypeVar, Callable, Optional, Coroutine from typing import Any, Dict, List, Tuple, Union, TypeVar, Callable, Optional, Coroutine
import uvicorn
from pydantic import BaseSettings from pydantic import BaseSettings
from nonebot.config import Env from nonebot.config import Env
@@ -32,6 +31,7 @@ from nonebot.drivers import WebSocket as BaseWebSocket
from nonebot.drivers import ReverseDriver, HTTPServerSetup, WebSocketServerSetup from nonebot.drivers import ReverseDriver, HTTPServerSetup, WebSocketServerSetup
try: try:
import uvicorn
from quart import request as _request from quart import request as _request
from quart import websocket as _websocket from quart import websocket as _websocket
from quart import Quart, Request, Response from quart import Quart, Request, Response
@@ -69,6 +69,8 @@ class Config(BaseSettings):
"""要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值""" """要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
quart_reload_excludes: Optional[List[str]] = None quart_reload_excludes: Optional[List[str]] = None
"""不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值""" """不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
quart_extra: Dict[str, Any] = {}
"""传递给 `Quart` 的其他参数。"""
class Config: class Config:
extra = "ignore" extra = "ignore"
@@ -82,7 +84,9 @@ class Driver(ReverseDriver):
self.quart_config = Config(**config.dict()) self.quart_config = Config(**config.dict())
self._server_app = Quart(self.__class__.__qualname__) self._server_app = Quart(
self.__class__.__qualname__, **self.quart_config.quart_extra
)
@property @property
@overrides(ReverseDriver) @overrides(ReverseDriver)

View File

@@ -23,7 +23,7 @@ from nonebot.typing import overrides
from nonebot.log import LoguruHandler from nonebot.log import LoguruHandler
from nonebot.drivers import Request, Response from nonebot.drivers import Request, Response
from nonebot.exception import WebSocketClosed from nonebot.exception import WebSocketClosed
from nonebot.drivers._block_driver import BlockDriver from nonebot.drivers.none import Driver as NoneDriver
from nonebot.drivers import WebSocket as BaseWebSocket from nonebot.drivers import WebSocket as BaseWebSocket
from nonebot.drivers import ForwardMixin, ForwardDriver, combine_driver from nonebot.drivers import ForwardMixin, ForwardDriver, combine_driver
@@ -70,7 +70,7 @@ class Mixin(ForwardMixin):
async def websocket(self, setup: Request) -> AsyncGenerator["WebSocket", None]: async def websocket(self, setup: Request) -> AsyncGenerator["WebSocket", None]:
connection = Connect( connection = Connect(
str(setup.url), str(setup.url),
extra_headers=setup.headers.items(), extra_headers={**setup.headers, **setup.cookies.as_header(setup)},
open_timeout=setup.timeout, open_timeout=setup.timeout,
) )
async with connection as ws: async with connection as ws:
@@ -101,8 +101,7 @@ class WebSocket(BaseWebSocket):
@overrides(BaseWebSocket) @overrides(BaseWebSocket)
@catch_closed @catch_closed
async def receive(self) -> Union[str, bytes]: async def receive(self) -> Union[str, bytes]:
msg = await self.websocket.recv() return await self.websocket.recv()
return msg
@overrides(BaseWebSocket) @overrides(BaseWebSocket)
@catch_closed @catch_closed
@@ -129,5 +128,5 @@ class WebSocket(BaseWebSocket):
await self.websocket.send(data) await self.websocket.send(data)
Driver: Type[ForwardDriver] = combine_driver(BlockDriver, Mixin) # type: ignore Driver: Type[ForwardDriver] = combine_driver(NoneDriver, Mixin) # type: ignore
"""Websockets Driver""" """Websockets Driver"""

View File

@@ -37,6 +37,9 @@ from pydantic.fields import ModelField
class NoneBotException(Exception): class NoneBotException(Exception):
"""所有 NoneBot 发生的异常基类。""" """所有 NoneBot 发生的异常基类。"""
def __str__(self) -> str:
return self.__repr__()
# Rule Exception # Rule Exception
class ParserExit(NoneBotException): class ParserExit(NoneBotException):
@@ -53,9 +56,6 @@ class ParserExit(NoneBotException):
+ ")" + ")"
) )
def __str__(self) -> str:
return self.__repr__()
# Processor Exception # Processor Exception
class ProcessException(NoneBotException): class ProcessException(NoneBotException):
@@ -75,9 +75,6 @@ class IgnoredException(ProcessException):
def __repr__(self) -> str: def __repr__(self) -> str:
return f"IgnoredException(reason={self.reason!r})" return f"IgnoredException(reason={self.reason!r})"
def __str__(self) -> str:
return self.__repr__()
class SkippedException(ProcessException): class SkippedException(ProcessException):
"""指示 NoneBot 立即结束当前 `Dependent` 的运行。 """指示 NoneBot 立即结束当前 `Dependent` 的运行。
@@ -109,9 +106,6 @@ class TypeMisMatch(SkippedException):
f"type={self.param._type_display()}, value={self.value!r}>" f"type={self.param._type_display()}, value={self.value!r}>"
) )
def __str__(self) -> str:
return self.__repr__()
class MockApiException(ProcessException): class MockApiException(ProcessException):
"""指示 NoneBot 阻止本次 API 调用或修改本次调用返回值,并返回自定义内容。可由 api hook 抛出。 """指示 NoneBot 阻止本次 API 调用或修改本次调用返回值,并返回自定义内容。可由 api hook 抛出。
@@ -126,9 +120,6 @@ class MockApiException(ProcessException):
def __repr__(self) -> str: def __repr__(self) -> str:
return f"MockApiException(result={self.result!r})" return f"MockApiException(result={self.result!r})"
def __str__(self) -> str:
return self.__repr__()
class StopPropagation(ProcessException): class StopPropagation(ProcessException):
"""指示 NoneBot 终止事件向下层传播。 """指示 NoneBot 终止事件向下层传播。
@@ -244,6 +235,3 @@ class WebSocketClosed(DriverException):
+ (f", reason={self.reason!r}" if self.reason else "") + (f", reason={self.reason!r}" if self.reason else "")
+ ")" + ")"
) )
def __str__(self) -> str:
return self.__repr__()

View File

@@ -43,6 +43,10 @@ class Bot(abc.ABC):
return f"Bot(type={self.type!r}, self_id={self.self_id!r})" return f"Bot(type={self.type!r}, self_id={self.self_id!r})"
def __getattr__(self, name: str) -> "_ApiCall": def __getattr__(self, name: str) -> "_ApiCall":
if name.startswith("__") and name.endswith("__"):
raise AttributeError(
f"'{self.__class__.__name__}' object has no attribute '{name}'"
)
return partial(self.call_api, name) return partial(self.call_api, name)
@property @property

View File

@@ -49,11 +49,11 @@ class MessageTemplate(Formatter, Generic[TF]):
) -> None: ) -> None:
... ...
def __init__( # type:ignore def __init__(
self, template, factory=str self, template: Union[str, TM], factory: Union[Type[str], Type[TM]] = str
) -> None: # TODO: fix type hint here ) -> None:
self.template: TF = template self.template: TF = template # type: ignore
self.factory: Type[TF] = factory self.factory: Type[TF] = factory # type: ignore
self.format_specs: Dict[str, FormatSpecFunc] = {} self.format_specs: Dict[str, FormatSpecFunc] = {}
def __repr__(self) -> str: def __repr__(self) -> str:
@@ -98,7 +98,7 @@ class MessageTemplate(Formatter, Generic[TF]):
else: else:
raise TypeError("template must be a string or instance of Message!") raise TypeError("template must be a string or instance of Message!")
self.check_unused_args(list(used_args), args, kwargs) self.check_unused_args(used_args, args, kwargs)
return cast(TF, full_message) return cast(TF, full_message)
def vformat( def vformat(

View File

@@ -1,4 +1,5 @@
import abc import abc
import urllib.request
from enum import Enum from enum import Enum
from dataclasses import dataclass from dataclasses import dataclass
from http.cookiejar import Cookie, CookieJar from http.cookiejar import Cookie, CookieJar
@@ -105,12 +106,9 @@ class Request:
self.url: URL = url self.url: URL = url
# headers # headers
self.headers: CIMultiDict[str] self.headers: CIMultiDict[str] = (
if headers is not None: CIMultiDict(headers) if headers is not None else CIMultiDict()
self.headers = CIMultiDict(headers) )
else:
self.headers = CIMultiDict()
# cookies # cookies
self.cookies = Cookies(cookies) self.cookies = Cookies(cookies)
@@ -147,12 +145,9 @@ class Response:
self.status_code: int = status_code self.status_code: int = status_code
# headers # headers
self.headers: CIMultiDict[str] self.headers: CIMultiDict[str] = (
if headers is not None: CIMultiDict(headers) if headers is not None else CIMultiDict()
self.headers = CIMultiDict(headers) )
else:
self.headers = CIMultiDict()
# body # body
self.content: ContentTypes = content self.content: ContentTypes = content
@@ -308,6 +303,11 @@ class Cookies(MutableMapping):
for cookie in cookies.jar: for cookie in cookies.jar:
self.jar.set_cookie(cookie) self.jar.set_cookie(cookie)
def as_header(self, request: Request) -> Dict[str, str]:
urllib_request = self._CookieCompatRequest(request)
self.jar.add_cookie_header(urllib_request)
return urllib_request.added_headers
def __setitem__(self, name: str, value: str) -> None: def __setitem__(self, name: str, value: str) -> None:
return self.set(name, value) return self.set(name, value)
@@ -333,6 +333,20 @@ class Cookies(MutableMapping):
) )
return f"{self.__class__.__name__}({cookies_repr})" return f"{self.__class__.__name__}({cookies_repr})"
class _CookieCompatRequest(urllib.request.Request):
def __init__(self, request: Request) -> None:
super().__init__(
url=str(request.url),
headers=dict(request.headers),
method=request.method,
)
self.request = request
self.added_headers: Dict[str, str] = {}
def add_unredirected_header(self, key: str, value: str) -> None:
super().add_unredirected_header(key, value)
self.added_headers[key] = value
@dataclass @dataclass
class HTTPServerSetup: class HTTPServerSetup:

View File

@@ -0,0 +1,11 @@
from .manager import MatcherManager as MatcherManager
from .provider import MatcherProvider as MatcherProvider
from .provider import DEFAULT_PROVIDER_CLASS as DEFAULT_PROVIDER_CLASS
matchers = MatcherManager()
from .matcher import Matcher as Matcher
from .matcher import current_bot as current_bot
from .matcher import current_event as current_event
from .matcher import current_handler as current_handler
from .matcher import current_matcher as current_matcher

View File

@@ -0,0 +1,104 @@
from typing import (
TYPE_CHECKING,
Any,
List,
Type,
Tuple,
Union,
TypeVar,
Iterator,
KeysView,
Optional,
ItemsView,
ValuesView,
MutableMapping,
overload,
)
from .provider import DEFAULT_PROVIDER_CLASS, MatcherProvider
if TYPE_CHECKING:
from .matcher import Matcher
T = TypeVar("T")
class MatcherManager(MutableMapping[int, List[Type["Matcher"]]]):
"""事件响应器管理器
实现了常用字典操作,用于管理事件响应器。
"""
def __init__(self):
self.provider: MatcherProvider = DEFAULT_PROVIDER_CLASS({})
def __repr__(self) -> str:
return f"MatcherManager(provider={self.provider!r})"
def __contains__(self, o: object) -> bool:
return o in self.provider
def __iter__(self) -> Iterator[int]:
return iter(self.provider)
def __len__(self) -> int:
return len(self.provider)
def __getitem__(self, key: int) -> List[Type["Matcher"]]:
return self.provider[key]
def __setitem__(self, key: int, value: List[Type["Matcher"]]) -> None:
self.provider[key] = value
def __delitem__(self, key: int) -> None:
del self.provider[key]
def __eq__(self, other: Any) -> bool:
return isinstance(other, MatcherManager) and self.provider == other.provider
def keys(self) -> KeysView[int]:
return self.provider.keys()
def values(self) -> ValuesView[List[Type["Matcher"]]]:
return self.provider.values()
def items(self) -> ItemsView[int, List[Type["Matcher"]]]:
return self.provider.items()
@overload
def get(self, key: int) -> Optional[List[Type["Matcher"]]]:
...
@overload
def get(self, key: int, default: T) -> Union[List[Type["Matcher"]], T]:
...
def get(
self, key: int, default: Optional[T] = None
) -> Optional[Union[List[Type["Matcher"]], T]]:
return self.provider.get(key, default)
def pop(self, key: int) -> List[Type["Matcher"]]:
return self.provider.pop(key)
def popitem(self) -> Tuple[int, List[Type["Matcher"]]]:
return self.provider.popitem()
def clear(self) -> None:
self.provider.clear()
def update(self, __m: MutableMapping[int, List[Type["Matcher"]]]) -> None:
self.provider.update(__m)
def setdefault(
self, key: int, default: List[Type["Matcher"]]
) -> List[Type["Matcher"]]:
return self.provider.setdefault(key, default)
def set_provider(self, provider_class: Type[MatcherProvider]) -> None:
"""设置事件响应器存储器
参数:
provider_class: 事件响应器存储器类
"""
self.provider = provider_class(self.provider)

View File

@@ -1,12 +1,10 @@
from types import ModuleType from types import ModuleType
from contextvars import ContextVar from contextvars import ContextVar
from collections import defaultdict
from contextlib import AsyncExitStack
from datetime import datetime, timedelta from datetime import datetime, timedelta
from contextlib import AsyncExitStack, contextmanager
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
Dict,
List, List,
Type, Type,
Union, Union,
@@ -19,7 +17,16 @@ from typing import (
) )
from nonebot.log import logger from nonebot.log import logger
from nonebot.internal.rule import Rule
from nonebot.dependencies import Dependent from nonebot.dependencies import Dependent
from nonebot.internal.permission import USER, User, Permission
from nonebot.internal.adapter import (
Bot,
Event,
Message,
MessageSegment,
MessageTemplate,
)
from nonebot.consts import ( from nonebot.consts import (
ARG_KEY, ARG_KEY,
RECEIVE_KEY, RECEIVE_KEY,
@@ -42,11 +49,7 @@ from nonebot.exception import (
FinishedException, FinishedException,
RejectedException, RejectedException,
) )
from nonebot.internal.params import (
from .rule import Rule
from .permission import USER, User, Permission
from .adapter import Bot, Event, Message, MessageSegment, MessageTemplate
from .params import (
Depends, Depends,
ArgParam, ArgParam,
BotParam, BotParam,
@@ -57,13 +60,13 @@ from .params import (
MatcherParam, MatcherParam,
) )
from . import matchers
if TYPE_CHECKING: if TYPE_CHECKING:
from nonebot.plugin import Plugin from nonebot.plugin import Plugin
T = TypeVar("T") T = TypeVar("T")
matchers: Dict[int, List[Type["Matcher"]]] = defaultdict(list)
"""用于存储当前所有的事件响应器"""
current_bot: ContextVar[Bot] = ContextVar("current_bot") current_bot: ContextVar[Bot] = ContextVar("current_bot")
current_event: ContextVar[Event] = ContextVar("current_event") current_event: ContextVar[Event] = ContextVar("current_event")
current_matcher: ContextVar["Matcher"] = ContextVar("current_matcher") current_matcher: ContextVar["Matcher"] = ContextVar("current_matcher")
@@ -244,6 +247,11 @@ class Matcher(metaclass=MatcherMeta):
return NewMatcher return NewMatcher
@classmethod
def destroy(cls) -> None:
"""销毁当前的事件响应器"""
matchers[cls.priority].remove(cls)
@classmethod @classmethod
async def check_perm( async def check_perm(
cls, cls,
@@ -661,6 +669,18 @@ class Matcher(metaclass=MatcherMeta):
if REJECT_CACHE_TARGET in self.state: if REJECT_CACHE_TARGET in self.state:
self.state[REJECT_TARGET] = self.state[REJECT_CACHE_TARGET] self.state[REJECT_TARGET] = self.state[REJECT_CACHE_TARGET]
@contextmanager
def ensure_context(self, bot: Bot, event: Event):
b_t = current_bot.set(bot)
e_t = current_event.set(event)
m_t = current_matcher.set(self)
try:
yield
finally:
current_bot.reset(b_t)
current_event.reset(e_t)
current_matcher.reset(m_t)
async def simple_run( async def simple_run(
self, self,
bot: Bot, bot: Bot,
@@ -673,9 +693,8 @@ class Matcher(metaclass=MatcherMeta):
f"{self} run with incoming args: " f"{self} run with incoming args: "
f"bot={bot}, event={event!r}, state={state!r}" f"bot={bot}, event={event!r}, state={state!r}"
) )
b_t = current_bot.set(bot)
e_t = current_event.set(event) with self.ensure_context(bot, event):
m_t = current_matcher.set(self)
try: try:
# Refresh preprocess state # Refresh preprocess state
self.state.update(state) self.state.update(state)
@@ -699,9 +718,6 @@ class Matcher(metaclass=MatcherMeta):
self.block = True self.block = True
finally: finally:
logger.info(f"{self} running complete") logger.info(f"{self} running complete")
current_bot.reset(b_t)
current_event.reset(e_t)
current_matcher.reset(m_t)
# 运行handlers # 运行handlers
async def run( async def run(
@@ -756,14 +772,3 @@ class Matcher(metaclass=MatcherMeta):
) )
except FinishedException: except FinishedException:
pass pass
__autodoc__ = {
"MatcherMeta": False,
"Matcher.get_target": False,
"Matcher.set_target": False,
"Matcher.update_type": False,
"Matcher.update_permission": False,
"Matcher.resolve_reject": False,
"Matcher.simple_run": False,
}

View File

@@ -0,0 +1,27 @@
import abc
from collections import defaultdict
from typing import TYPE_CHECKING, List, Type, Mapping, MutableMapping
if TYPE_CHECKING:
from .matcher import Matcher
class MatcherProvider(abc.ABC, MutableMapping[int, List[Type["Matcher"]]]):
"""事件响应器存储器基类
参数:
matchers: 当前存储器中已有的事件响应器
"""
@abc.abstractmethod
def __init__(self, matchers: Mapping[int, List[Type["Matcher"]]]):
raise NotImplementedError
class _DictProvider(defaultdict, MatcherProvider):
def __init__(self, matchers: Mapping[int, List[Type["Matcher"]]]):
super().__init__(list, matchers)
DEFAULT_PROVIDER_CLASS = _DictProvider
"""默认存储器类型"""

View File

@@ -9,10 +9,16 @@ from nonebot.internal.matcher import Matcher as Matcher
from nonebot.internal.matcher import matchers as matchers from nonebot.internal.matcher import matchers as matchers
from nonebot.internal.matcher import current_bot as current_bot from nonebot.internal.matcher import current_bot as current_bot
from nonebot.internal.matcher import current_event as current_event from nonebot.internal.matcher import current_event as current_event
from nonebot.internal.matcher import MatcherManager as MatcherManager
from nonebot.internal.matcher import MatcherProvider as MatcherProvider
from nonebot.internal.matcher import current_handler as current_handler from nonebot.internal.matcher import current_handler as current_handler
from nonebot.internal.matcher import current_matcher as current_matcher from nonebot.internal.matcher import current_matcher as current_matcher
from nonebot.internal.matcher import DEFAULT_PROVIDER_CLASS as DEFAULT_PROVIDER_CLASS
__autodoc__ = { __autodoc__ = {
"Matcher": True, "Matcher": True,
"matchers": True, "matchers": True,
"MatcherManager": True,
"MatcherProvider": True,
"DEFAULT_PROVIDER_CLASS": True,
} }

View File

@@ -112,7 +112,6 @@ def run_postprocessor(func: T_RunPostProcessor) -> T_RunPostProcessor:
async def _check_matcher( async def _check_matcher(
priority: int,
Matcher: Type[Matcher], Matcher: Type[Matcher],
bot: "Bot", bot: "Bot",
event: "Event", event: "Event",
@@ -122,7 +121,7 @@ async def _check_matcher(
) -> None: ) -> None:
if Matcher.expire_time and datetime.now() > Matcher.expire_time: if Matcher.expire_time and datetime.now() > Matcher.expire_time:
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
matchers[priority].remove(Matcher) Matcher.destroy()
return return
try: try:
@@ -138,7 +137,7 @@ async def _check_matcher(
if Matcher.temp: if Matcher.temp:
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
matchers[priority].remove(Matcher) Matcher.destroy()
await _run_matcher(Matcher, bot, event, state, stack, dependency_cache) await _run_matcher(Matcher, bot, event, state, stack, dependency_cache)
@@ -167,6 +166,8 @@ async def _run_matcher(
) )
for proc in _run_preprocessors for proc in _run_preprocessors
]: ]:
# ensure matcher function can be correctly called
with matcher.ensure_context(bot, event):
try: try:
await asyncio.gather(*coros) await asyncio.gather(*coros)
except IgnoredException: except IgnoredException:
@@ -205,6 +206,8 @@ async def _run_matcher(
) )
for proc in _run_postprocessors for proc in _run_postprocessors
]: ]:
# ensure matcher function can be correctly called
with matcher.ensure_context(bot, event):
try: try:
await asyncio.gather(*coros) await asyncio.gather(*coros)
except Exception as e: except Exception as e:
@@ -290,7 +293,7 @@ async def handle_event(bot: "Bot", event: "Event") -> None:
pending_tasks = [ pending_tasks = [
_check_matcher( _check_matcher(
priority, matcher, bot, event, state.copy(), stack, dependency_cache matcher, bot, event, state.copy(), stack, dependency_cache
) )
for matcher in matchers[priority] for matcher in matchers[priority]
] ]
@@ -308,6 +311,9 @@ async def handle_event(bot: "Bot", event: "Event") -> None:
"<r><bg #f8bbd0>Error when checking Matcher.</bg #f8bbd0></r>" "<r><bg #f8bbd0>Error when checking Matcher.</bg #f8bbd0></r>"
) )
if show_log:
logger.debug("Checking for matchers completed")
if coros := [ if coros := [
run_coro_with_catch( run_coro_with_catch(
proc( proc(

View File

@@ -5,6 +5,7 @@ FrontMatter:
description: nonebot.params 模块 description: nonebot.params 模块
""" """
import warnings
from typing import Any, Dict, List, Tuple, Union, Optional from typing import Any, Dict, List, Tuple, Union, Optional
from nonebot.typing import T_State from nonebot.typing import T_State
@@ -24,15 +25,20 @@ from nonebot.internal.params import MatcherParam as MatcherParam
from nonebot.internal.params import ExceptionParam as ExceptionParam from nonebot.internal.params import ExceptionParam as ExceptionParam
from nonebot.consts import ( from nonebot.consts import (
CMD_KEY, CMD_KEY,
REGEX_STR,
PREFIX_KEY, PREFIX_KEY,
REGEX_DICT, REGEX_DICT,
SHELL_ARGS, SHELL_ARGS,
SHELL_ARGV, SHELL_ARGV,
CMD_ARG_KEY, CMD_ARG_KEY,
KEYWORD_KEY,
RAW_CMD_KEY, RAW_CMD_KEY,
REGEX_GROUP, REGEX_GROUP,
ENDSWITH_KEY,
CMD_START_KEY, CMD_START_KEY,
FULLMATCH_KEY,
REGEX_MATCHED, REGEX_MATCHED,
STARTSWITH_KEY,
) )
@@ -132,10 +138,25 @@ def _regex_matched(state: T_State) -> str:
def RegexMatched() -> str: def RegexMatched() -> str:
"""正则匹配结果""" """正则匹配结果"""
warnings.warn(
'"RegexMatched()" will be changed to "re.Match" object, '
'use "RegexStr()" instead. '
"See https://github.com/nonebot/nonebot2/pull/1453 .",
DeprecationWarning,
)
return Depends(_regex_matched, use_cache=False) return Depends(_regex_matched, use_cache=False)
def _regex_group(state: T_State): def _regex_str(state: T_State) -> str:
return state[REGEX_STR]
def RegexStr() -> str:
"""正则匹配结果文本"""
return Depends(_regex_str, use_cache=False)
def _regex_group(state: T_State) -> Tuple[Any, ...]:
return state[REGEX_GROUP] return state[REGEX_GROUP]
@@ -144,7 +165,7 @@ def RegexGroup() -> Tuple[Any, ...]:
return Depends(_regex_group, use_cache=False) return Depends(_regex_group, use_cache=False)
def _regex_dict(state: T_State): def _regex_dict(state: T_State) -> Dict[str, Any]:
return state[REGEX_DICT] return state[REGEX_DICT]
@@ -153,10 +174,46 @@ def RegexDict() -> Dict[str, Any]:
return Depends(_regex_dict, use_cache=False) return Depends(_regex_dict, use_cache=False)
def _startswith(state: T_State) -> str:
return state[STARTSWITH_KEY]
def Startswith() -> str:
"""响应触发前缀"""
return Depends(_startswith, use_cache=False)
def _endswith(state: T_State) -> str:
return state[ENDSWITH_KEY]
def Endswith() -> str:
"""响应触发后缀"""
return Depends(_endswith, use_cache=False)
def _fullmatch(state: T_State) -> str:
return state[FULLMATCH_KEY]
def Fullmatch() -> str:
"""响应触发完整消息"""
return Depends(_fullmatch, use_cache=False)
def _keyword(state: T_State) -> str:
return state[KEYWORD_KEY]
def Keyword() -> str:
"""响应触发关键字"""
return Depends(_keyword, use_cache=False)
def Received(id: Optional[str] = None, default: Any = None) -> Any: def Received(id: Optional[str] = None, default: Any = None) -> Any:
"""`receive` 事件参数""" """`receive` 事件参数"""
def _received(matcher: "Matcher"): def _received(matcher: "Matcher") -> Any:
return matcher.get_receive(id or "", default) return matcher.get_receive(id or "", default)
return Depends(_received, use_cache=False) return Depends(_received, use_cache=False)

View File

@@ -65,6 +65,8 @@ def _revert_plugin(plugin: "Plugin") -> None:
if plugin.name not in _plugins: if plugin.name not in _plugins:
raise RuntimeError("Plugin not found!") raise RuntimeError("Plugin not found!")
del _plugins[plugin.name] del _plugins[plugin.name]
if parent_plugin := plugin.parent_plugin:
parent_plugin.sub_plugins.remove(plugin)
def get_plugin(name: str) -> Optional["Plugin"]: def get_plugin(name: str) -> Optional["Plugin"]:

View File

@@ -14,7 +14,7 @@ from itertools import chain
from types import ModuleType from types import ModuleType
from importlib.abc import MetaPathFinder from importlib.abc import MetaPathFinder
from importlib.machinery import PathFinder, SourceFileLoader from importlib.machinery import PathFinder, SourceFileLoader
from typing import Set, Dict, List, Union, Iterable, Optional, Sequence from typing import Set, Dict, List, Iterable, Optional, Sequence
from nonebot.log import logger from nonebot.log import logger
from nonebot.utils import escape_tag, path_to_module_name from nonebot.utils import escape_tag, path_to_module_name
@@ -174,7 +174,7 @@ class PluginFinder(MetaPathFinder):
def find_spec( def find_spec(
self, self,
fullname: str, fullname: str,
path: Optional[Sequence[Union[bytes, str]]], path: Optional[Sequence[str]],
target: Optional[ModuleType] = None, target: Optional[ModuleType] = None,
): ):
if _managers: if _managers:

View File

@@ -439,7 +439,7 @@ def on_regex(
def on_type( def on_type(
types: Union[Type[Event], Tuple[Type[Event]]], types: Union[Type[Event], Tuple[Type[Event], ...]],
rule: Optional[Union[Rule, T_RuleChecker]] = None, rule: Optional[Union[Rule, T_RuleChecker]] = None,
*, *,
_depth: int = 0, _depth: int = 0,

View File

@@ -154,7 +154,7 @@ def on_regex(
state: Optional[T_State] = ..., state: Optional[T_State] = ...,
) -> Type[Matcher]: ... ) -> Type[Matcher]: ...
def on_type( def on_type(
types: Union[Type[Event], Tuple[Type[Event]]], types: Union[Type[Event], Tuple[Type[Event], ...]],
rule: Optional[Union[Rule, T_RuleChecker]] = ..., rule: Optional[Union[Rule, T_RuleChecker]] = ...,
*, *,
permission: Optional[Union[Permission, T_PermissionChecker]] = ..., permission: Optional[Union[Permission, T_PermissionChecker]] = ...,

View File

@@ -12,7 +12,8 @@ from pydantic import BaseModel
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from . import _plugins as plugins # FIXME: backport for nonebug # FIXME: backport for nonebug
from . import _plugins as plugins # nopycln: import
if TYPE_CHECKING: if TYPE_CHECKING:
from .manager import PluginManager from .manager import PluginManager

View File

@@ -12,13 +12,13 @@ import re
import shlex import shlex
from argparse import Action from argparse import Action
from argparse import ArgumentError from argparse import ArgumentError
from contextvars import ContextVar
from itertools import chain, product from itertools import chain, product
from argparse import Namespace as Namespace from argparse import Namespace as Namespace
from argparse import ArgumentParser as ArgParser from argparse import ArgumentParser as ArgParser
from typing import ( from typing import (
IO, IO,
TYPE_CHECKING, TYPE_CHECKING,
Any,
List, List,
Type, Type,
Tuple, Tuple,
@@ -43,15 +43,20 @@ from nonebot.params import Command, EventToMe, CommandArg
from nonebot.adapters import Bot, Event, Message, MessageSegment from nonebot.adapters import Bot, Event, Message, MessageSegment
from nonebot.consts import ( from nonebot.consts import (
CMD_KEY, CMD_KEY,
REGEX_STR,
PREFIX_KEY, PREFIX_KEY,
REGEX_DICT, REGEX_DICT,
SHELL_ARGS, SHELL_ARGS,
SHELL_ARGV, SHELL_ARGV,
CMD_ARG_KEY, CMD_ARG_KEY,
KEYWORD_KEY,
RAW_CMD_KEY, RAW_CMD_KEY,
REGEX_GROUP, REGEX_GROUP,
ENDSWITH_KEY,
CMD_START_KEY, CMD_START_KEY,
FULLMATCH_KEY,
REGEX_MATCHED, REGEX_MATCHED,
STARTSWITH_KEY,
) )
T = TypeVar("T") T = TypeVar("T")
@@ -70,6 +75,8 @@ TRIE_VALUE = NamedTuple(
"TRIE_VALUE", [("command_start", str), ("command", Tuple[str, ...])] "TRIE_VALUE", [("command_start", str), ("command", Tuple[str, ...])]
) )
parser_message: ContextVar[str] = ContextVar("parser_message")
class TrieRule: class TrieRule:
prefix: CharTrie = CharTrie() prefix: CharTrie = CharTrie()
@@ -136,20 +143,21 @@ class StartswithRule:
def __hash__(self) -> int: def __hash__(self) -> int:
return hash((frozenset(self.msg), self.ignorecase)) return hash((frozenset(self.msg), self.ignorecase))
async def __call__(self, event: Event) -> bool: async def __call__(self, event: Event, state: T_State) -> bool:
if event.get_type() != "message": if event.get_type() != "message":
return False return False
try: try:
text = event.get_plaintext() text = event.get_plaintext()
except Exception: except Exception:
return False return False
return bool( if match := re.match(
re.match(
f"^(?:{'|'.join(re.escape(prefix) for prefix in self.msg)})", f"^(?:{'|'.join(re.escape(prefix) for prefix in self.msg)})",
text, text,
re.IGNORECASE if self.ignorecase else 0, re.IGNORECASE if self.ignorecase else 0,
) ):
) state[STARTSWITH_KEY] = match.group()
return True
return False
def startswith(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule: def startswith(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule:
@@ -192,20 +200,21 @@ class EndswithRule:
def __hash__(self) -> int: def __hash__(self) -> int:
return hash((frozenset(self.msg), self.ignorecase)) return hash((frozenset(self.msg), self.ignorecase))
async def __call__(self, event: Event) -> bool: async def __call__(self, event: Event, state: T_State) -> bool:
if event.get_type() != "message": if event.get_type() != "message":
return False return False
try: try:
text = event.get_plaintext() text = event.get_plaintext()
except Exception: except Exception:
return False return False
return bool( if match := re.search(
re.search( f"(?:{'|'.join(re.escape(suffix) for suffix in self.msg)})$",
f"(?:{'|'.join(re.escape(prefix) for prefix in self.msg)})$",
text, text,
re.IGNORECASE if self.ignorecase else 0, re.IGNORECASE if self.ignorecase else 0,
) ):
) state[ENDSWITH_KEY] = match.group()
return True
return False
def endswith(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule: def endswith(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule:
@@ -248,14 +257,20 @@ class FullmatchRule:
def __hash__(self) -> int: def __hash__(self) -> int:
return hash((frozenset(self.msg), self.ignorecase)) return hash((frozenset(self.msg), self.ignorecase))
async def __call__(self, event: Event) -> bool: async def __call__(self, event: Event, state: T_State) -> bool:
if event.get_type() != "message": if event.get_type() != "message":
return False return False
try: try:
text = event.get_plaintext() text = event.get_plaintext()
except Exception: except Exception:
return False return False
return (text.casefold() if self.ignorecase else text) in self.msg if not text:
return False
text = text.casefold() if self.ignorecase else text
if text in self.msg:
state[FULLMATCH_KEY] = text
return True
return False
def fullmatch(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule: def fullmatch(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule:
@@ -294,14 +309,19 @@ class KeywordsRule:
def __hash__(self) -> int: def __hash__(self) -> int:
return hash(frozenset(self.keywords)) return hash(frozenset(self.keywords))
async def __call__(self, event: Event) -> bool: async def __call__(self, event: Event, state: T_State) -> bool:
if event.get_type() != "message": if event.get_type() != "message":
return False return False
try: try:
text = event.get_plaintext() text = event.get_plaintext()
except Exception: except Exception:
return False return False
return bool(text and any(keyword in text for keyword in self.keywords)) if not text:
return False
if key := next((k for k in self.keywords if k in text), None):
state[KEYWORD_KEY] = key
return True
return False
def keyword(*keywords: str) -> Rule: def keyword(*keywords: str) -> Rule:
@@ -430,13 +450,15 @@ class ArgumentParser(ArgParser):
) )
def _print_message(self, message: str, file: Optional[IO[str]] = None): def _print_message(self, message: str, file: Optional[IO[str]] = None):
if message: if (msg := parser_message.get(None)) is not None:
setattr(self, "_message", getattr(self, "_message", "") + message) parser_message.set(msg + message)
else:
super()._print_message(message, file)
def exit(self, status: int = 0, message: Optional[str] = None): def exit(self, status: int = 0, message: Optional[str] = None):
if message: if message:
self._print_message(message) self._print_message(message)
raise ParserExit(status=status, message=getattr(self, "_message", None)) raise ParserExit(status=status, message=parser_message.get(None))
class ShellCommandRule: class ShellCommandRule:
@@ -483,6 +505,7 @@ class ShellCommandRule:
) )
if self.parser: if self.parser:
t = parser_message.set("")
try: try:
args = self.parser.parse_args(state[SHELL_ARGV]) args = self.parser.parse_args(state[SHELL_ARGV])
state[SHELL_ARGS] = args state[SHELL_ARGS] = args
@@ -490,6 +513,8 @@ class ShellCommandRule:
state[SHELL_ARGS] = ParserExit(status=2, message=str(e)) state[SHELL_ARGS] = ParserExit(status=2, message=str(e))
except ParserExit as e: except ParserExit as e:
state[SHELL_ARGS] = e state[SHELL_ARGS] = e
finally:
parser_message.reset(t)
return True return True
@@ -592,6 +617,7 @@ class RegexRule:
return False return False
if matched := re.search(self.regex, str(msg), self.flags): if matched := re.search(self.regex, str(msg), self.flags):
state[REGEX_MATCHED] = matched.group() state[REGEX_MATCHED] = matched.group()
state[REGEX_STR] = matched.group()
state[REGEX_GROUP] = matched.groups() state[REGEX_GROUP] = matched.groups()
state[REGEX_DICT] = matched.groupdict() state[REGEX_DICT] = matched.groupdict()
return True return True
@@ -602,7 +628,7 @@ class RegexRule:
def regex(regex: str, flags: Union[int, re.RegexFlag] = 0) -> Rule: def regex(regex: str, flags: Union[int, re.RegexFlag] = 0) -> Rule:
"""匹配符合正则表达式的消息字符串。 """匹配符合正则表达式的消息字符串。
可以通过 {ref}`nonebot.params.RegexMatched` 获取匹配成功的字符串, 可以通过 {ref}`nonebot.params.RegexStr` 获取匹配成功的字符串,
通过 {ref}`nonebot.params.RegexGroup` 获取匹配成功的 group 元组, 通过 {ref}`nonebot.params.RegexGroup` 获取匹配成功的 group 元组,
通过 {ref}`nonebot.params.RegexDict` 获取匹配成功的 group 字典。 通过 {ref}`nonebot.params.RegexDict` 获取匹配成功的 group 字典。

View File

@@ -11,7 +11,7 @@
"start": "yarn workspace nonebot start", "start": "yarn workspace nonebot start",
"serve": "yarn workspace nonebot serve", "serve": "yarn workspace nonebot serve",
"clear": "yarn workspace nonebot clear", "clear": "yarn workspace nonebot clear",
"prettier": "prettier --config ./.prettierrc --write \"./website/**/*.md\"" "prettier": "prettier --config ./.prettierrc --write \"./website/\""
}, },
"devDependencies": { "devDependencies": {
"cross-env": "^7.0.3", "cross-env": "^7.0.3",

3240
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "nonebot2" name = "nonebot2"
version = "2.0.0-rc.1" version = "2.0.0rc3"
description = "An asynchronous python bot framework." description = "An asynchronous python bot framework."
authors = ["yanyongyu <yyy@nonebot.dev>"] authors = ["yanyongyu <yyy@nonebot.dev>"]
license = "MIT" license = "MIT"
@@ -26,33 +26,40 @@ python = "^3.8"
yarl = "^1.7.2" yarl = "^1.7.2"
loguru = "^0.6.0" loguru = "^0.6.0"
pygtrie = "^2.4.1" pygtrie = "^2.4.1"
fastapi = "^0.79.0"
tomlkit = ">=0.10.0,<1.0.0" tomlkit = ">=0.10.0,<1.0.0"
typing-extensions = ">=3.10.0,<5.0.0" typing-extensions = ">=3.10.0,<5.0.0"
Quart = { version = "^0.17.0", optional = true } pydantic = { version = "^1.10.0", extras = ["dotenv"] }
websockets = { version="^10.0", optional = true }
pydantic = { version = "~1.9.0", extras = ["dotenv"] }
uvicorn = { version = "^0.18.0", extras = ["standard"] }
aiohttp = { version = "^3.7.4", extras = ["speedups"], optional = true }
httpx = { version = ">=0.20.0, <1.0.0", extras = ["http2"], optional = true }
[tool.poetry.dev-dependencies] websockets = { version = "^10.0", optional = true }
Quart = { version = ">=0.18.0,<1.0.0", optional = true }
fastapi = { version = ">=0.87.0,!=0.89.0,<1.0.0", optional = true }
aiohttp = { version = "^3.7.4", extras = ["speedups"], optional = true }
httpx = { version = ">=0.20.0,<1.0.0", extras = ["http2"], optional = true }
uvicorn = { version = ">=0.20.0,<1.0.0", extras = ["standard"], optional = true }
[tool.poetry.group.dev.dependencies]
pycln = "^2.1.2"
isort = "^5.10.1" isort = "^5.10.1"
black = "^22.1.0" black = "^22.1.0"
nonemoji = "^0.1.2" nonemoji = "^0.1.2"
pytest-cov = "^3.0.0"
pre-commit = "^2.16.0" pre-commit = "^2.16.0"
pytest-xdist = "^2.5.0"
pytest-asyncio = "^0.19.0" [tool.poetry.group.test.dependencies]
pytest-cov = "^4.0.0"
pytest-xdist = "^3.0.2"
pytest-asyncio = "^0.20.0"
nonebug = { git = "https://github.com/nonebot/nonebug.git" } nonebug = { git = "https://github.com/nonebot/nonebug.git" }
[tool.poetry.group.docs.dependencies]
nb-autodoc = { git = "https://github.com/nonebot/nb-autodoc.git" } nb-autodoc = { git = "https://github.com/nonebot/nb-autodoc.git" }
[tool.poetry.extras] [tool.poetry.extras]
quart = ["quart"]
httpx = ["httpx"] httpx = ["httpx"]
aiohttp = ["aiohttp"] aiohttp = ["aiohttp"]
websockets = ["websockets"] websockets = ["websockets"]
all = ["quart", "aiohttp", "httpx", "websockets"] quart = ["quart", "uvicorn"]
fastapi = ["fastapi", "uvicorn"]
all = ["fastapi", "quart", "aiohttp", "httpx", "websockets", "uvicorn"]
[tool.pytest.ini_options] [tool.pytest.ini_options]
asyncio_mode = "auto" asyncio_mode = "auto"
@@ -64,7 +71,7 @@ filterwarnings = [
[tool.black] [tool.black]
line-length = 88 line-length = 88
target-version = ["py37", "py38", "py39", "py310"] target-version = ["py38", "py39", "py310", "py311"]
include = '\.pyi?$' include = '\.pyi?$'
extend-exclude = ''' extend-exclude = '''
''' '''
@@ -78,6 +85,20 @@ force_sort_within_sections = true
src_paths = ["nonebot", "tests"] src_paths = ["nonebot", "tests"]
extra_standard_library = ["typing_extensions"] extra_standard_library = ["typing_extensions"]
[tool.pycln]
path = "."
all = false
[tool.pyright]
reportShadowedImports = false
pythonVersion = "3.8"
pythonPlatform = "All"
executionEnvironments = [
{ root = "./tests", extraPaths = ["./"] },
{ root = "./" },
]
[build-system] [build-system]
requires = ["poetry_core>=1.0.0"] requires = ["poetry_core>=1.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"

View File

@@ -1,2 +1,3 @@
ENVIRONMENT=test ENVIRONMENT=test
COMMON_CONFIG=common COMMON_CONFIG=common
COMMON_OVERRIDE=old

View File

@@ -1,5 +1,13 @@
LOG_LEVEL=TRACE LOG_LEVEL=TRACE
NICKNAME=["test"] NICKNAME=["test"]
SUPERUSERS=["test", "fake:faketest"] SUPERUSERS=["test", "fake:faketest"]
COMMON_OVERRIDE=new
CONFIG_FROM_ENV= CONFIG_FROM_ENV=
CONFIG_OVERRIDE=old CONFIG_OVERRIDE=old
NESTED_DICT={"a": 1}
NESTED_DICT__B=2
NESTED_DICT__C__D=3
NESTED_MISSING_DICT__A=1
NESTED_MISSING_DICT__B__C=2
NOT_NESTED=some string
NOT_NESTED__A=1

View File

@@ -1 +1 @@
from .nested_subplugin2 import a from .nested_subplugin2 import a # nopycln: import

View File

@@ -4,10 +4,15 @@ from nonebot.typing import T_State
from nonebot.adapters import Message from nonebot.adapters import Message
from nonebot.params import ( from nonebot.params import (
Command, Command,
Keyword,
Endswith,
RegexStr,
Fullmatch,
RegexDict, RegexDict,
CommandArg, CommandArg,
RawCommand, RawCommand,
RegexGroup, RegexGroup,
Startswith,
CommandStart, CommandStart,
RegexMatched, RegexMatched,
ShellCommandArgs, ShellCommandArgs,
@@ -65,3 +70,23 @@ async def regex_group(regex_group: Tuple = RegexGroup()) -> Tuple:
async def regex_matched(regex_matched: str = RegexMatched()) -> str: async def regex_matched(regex_matched: str = RegexMatched()) -> str:
return regex_matched return regex_matched
async def regex_str(regex_matched: str = RegexStr()) -> str:
return regex_matched
async def startswith(startswith: str = Startswith()) -> str:
return startswith
async def endswith(endswith: str = Endswith()) -> str:
return endswith
async def fullmatch(fullmatch: str = Fullmatch()) -> str:
return fullmatch
async def keyword(keyword: str = Keyword()) -> str:
return keyword

View File

@@ -1,3 +1,5 @@
import json
import asyncio
from typing import cast from typing import cast
import pytest import pytest
@@ -79,6 +81,62 @@ async def test_reverse_driver(app: App):
await ws.close() await ws.close()
await asyncio.sleep(1)
@pytest.mark.asyncio
@pytest.mark.parametrize(
"nonebug_init",
[
pytest.param({"driver": "nonebot.drivers.httpx:Driver"}, id="httpx"),
pytest.param({"driver": "nonebot.drivers.aiohttp:Driver"}, id="aiohttp"),
],
indirect=True,
)
async def test_http_driver(app: App):
import nonebot
from nonebot.drivers import Request, ForwardDriver
driver = cast(ForwardDriver, nonebot.get_driver())
request = Request(
"POST",
"https://httpbin.org/post",
params={"param": "test"},
headers={"X-Test": "test"},
cookies={"session": "test"},
content="test",
)
response = await driver.request(request)
assert response.status_code == 200 and response.content
data = json.loads(response.content)
assert data["args"] == {"param": "test"}
assert data["headers"].get("X-Test") == "test"
assert data["headers"].get("Cookie") == "session=test"
assert data["data"] == "test"
request = Request("POST", "https://httpbin.org/post", data={"form": "test"})
response = await driver.request(request)
assert response.status_code == 200 and response.content
data = json.loads(response.content)
assert data["form"] == {"form": "test"}
request = Request("POST", "https://httpbin.org/post", json={"json": "test"})
response = await driver.request(request)
assert response.status_code == 200 and response.content
data = json.loads(response.content)
assert data["json"] == {"json": "test"}
request = Request(
"POST", "https://httpbin.org/post", files={"test": ("test.txt", b"test")}
)
response = await driver.request(request)
assert response.status_code == 200 and response.content
data = json.loads(response.content)
assert data["files"] == {"test": "test"}
await asyncio.sleep(1)
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.parametrize( @pytest.mark.parametrize(
@@ -91,7 +149,7 @@ async def test_reverse_driver(app: App):
), ),
pytest.param( pytest.param(
{"driver": "~httpx:Driver+~websockets"}, {"driver": "~httpx:Driver+~websockets"},
"block_driver+httpx+websockets", "none+httpx+websockets",
id="httpx+websockets", id="httpx+websockets",
), ),
], ],

View File

@@ -29,6 +29,10 @@ async def test_init(nonebug_init):
assert config.config_override == "new" assert config.config_override == "new"
assert config.config_from_init == "init" assert config.config_from_init == "init"
assert config.common_config == "common" assert config.common_config == "common"
assert config.common_override == "new"
assert config.nested_dict == {"a": 1, "b": 2, "c": {"d": 3}}
assert config.nested_missing_dict == {"a": 1, "b": {"c": 2}}
assert config.not_nested == "some string"
@pytest.mark.asyncio @pytest.mark.asyncio

View File

@@ -187,25 +187,19 @@ async def test_expire(app: App, load_plugin):
async with app.test_api() as ctx: async with app.test_api() as ctx:
bot = ctx.create_bot() bot = ctx.create_bot()
assert test_temp_matcher in matchers[test_temp_matcher.priority] assert test_temp_matcher in matchers[test_temp_matcher.priority]
await _check_matcher( await _check_matcher(test_temp_matcher, bot, event, {})
test_temp_matcher.priority, test_temp_matcher, bot, event, {}
)
assert test_temp_matcher not in matchers[test_temp_matcher.priority] assert test_temp_matcher not in matchers[test_temp_matcher.priority]
event = make_fake_event()() event = make_fake_event()()
async with app.test_api() as ctx: async with app.test_api() as ctx:
bot = ctx.create_bot() bot = ctx.create_bot()
assert test_datetime_matcher in matchers[test_datetime_matcher.priority] assert test_datetime_matcher in matchers[test_datetime_matcher.priority]
await _check_matcher( await _check_matcher(test_datetime_matcher, bot, event, {})
test_datetime_matcher.priority, test_datetime_matcher, bot, event, {}
)
assert test_datetime_matcher not in matchers[test_datetime_matcher.priority] assert test_datetime_matcher not in matchers[test_datetime_matcher.priority]
event = make_fake_event()() event = make_fake_event()()
async with app.test_api() as ctx: async with app.test_api() as ctx:
bot = ctx.create_bot() bot = ctx.create_bot()
assert test_timedelta_matcher in matchers[test_timedelta_matcher.priority] assert test_timedelta_matcher in matchers[test_timedelta_matcher.priority]
await _check_matcher( await _check_matcher(test_timedelta_matcher, bot, event, {})
test_timedelta_matcher.priority, test_timedelta_matcher, bot, event, {}
)
assert test_timedelta_matcher not in matchers[test_timedelta_matcher.priority] assert test_timedelta_matcher not in matchers[test_timedelta_matcher.priority]

View File

@@ -0,0 +1,11 @@
import pytest
from nonebug import App
@pytest.mark.asyncio
async def test_manager(app: App, load_plugin):
from nonebot.matcher import DEFAULT_PROVIDER_CLASS, matchers
default_provider = matchers.provider
matchers.set_provider(DEFAULT_PROVIDER_CLASS)
assert matchers.provider == default_provider

View File

@@ -163,20 +163,30 @@ async def test_state(app: App, load_plugin):
from nonebot.params import StateParam, DependParam from nonebot.params import StateParam, DependParam
from nonebot.consts import ( from nonebot.consts import (
CMD_KEY, CMD_KEY,
REGEX_STR,
PREFIX_KEY, PREFIX_KEY,
REGEX_DICT, REGEX_DICT,
SHELL_ARGS, SHELL_ARGS,
SHELL_ARGV, SHELL_ARGV,
CMD_ARG_KEY, CMD_ARG_KEY,
KEYWORD_KEY,
RAW_CMD_KEY, RAW_CMD_KEY,
REGEX_GROUP, REGEX_GROUP,
ENDSWITH_KEY,
CMD_START_KEY, CMD_START_KEY,
FULLMATCH_KEY,
REGEX_MATCHED, REGEX_MATCHED,
STARTSWITH_KEY,
) )
from plugins.param.param_state import ( from plugins.param.param_state import (
state, state,
command, command,
keyword,
endswith,
fullmatch,
regex_str,
regex_dict, regex_dict,
startswith,
command_arg, command_arg,
raw_command, raw_command,
regex_group, regex_group,
@@ -199,8 +209,13 @@ async def test_state(app: App, load_plugin):
SHELL_ARGV: ["-h"], SHELL_ARGV: ["-h"],
SHELL_ARGS: {"help": True}, SHELL_ARGS: {"help": True},
REGEX_MATCHED: "[cq:test,arg=value]", REGEX_MATCHED: "[cq:test,arg=value]",
REGEX_STR: "[cq:test,arg=value]",
REGEX_GROUP: ("test", "arg=value"), REGEX_GROUP: ("test", "arg=value"),
REGEX_DICT: {"type": "test", "arg": "value"}, REGEX_DICT: {"type": "test", "arg": "value"},
STARTSWITH_KEY: "startswith",
ENDSWITH_KEY: "endswith",
FULLMATCH_KEY: "fullmatch",
KEYWORD_KEY: "keyword",
} }
async with app.test_dependent(state, allow_types=[StateParam]) as ctx: async with app.test_dependent(state, allow_types=[StateParam]) as ctx:
@@ -259,6 +274,12 @@ async def test_state(app: App, load_plugin):
ctx.pass_params(state=fake_state) ctx.pass_params(state=fake_state)
ctx.should_return(fake_state[REGEX_MATCHED]) ctx.should_return(fake_state[REGEX_MATCHED])
async with app.test_dependent(
regex_str, allow_types=[StateParam, DependParam]
) as ctx:
ctx.pass_params(state=fake_state)
ctx.should_return(fake_state[REGEX_STR])
async with app.test_dependent( async with app.test_dependent(
regex_group, allow_types=[StateParam, DependParam] regex_group, allow_types=[StateParam, DependParam]
) as ctx: ) as ctx:
@@ -271,6 +292,30 @@ async def test_state(app: App, load_plugin):
ctx.pass_params(state=fake_state) ctx.pass_params(state=fake_state)
ctx.should_return(fake_state[REGEX_DICT]) ctx.should_return(fake_state[REGEX_DICT])
async with app.test_dependent(
startswith, allow_types=[StateParam, DependParam]
) as ctx:
ctx.pass_params(state=fake_state)
ctx.should_return(fake_state[STARTSWITH_KEY])
async with app.test_dependent(
endswith, allow_types=[StateParam, DependParam]
) as ctx:
ctx.pass_params(state=fake_state)
ctx.should_return(fake_state[ENDSWITH_KEY])
async with app.test_dependent(
fullmatch, allow_types=[StateParam, DependParam]
) as ctx:
ctx.pass_params(state=fake_state)
ctx.should_return(fake_state[FULLMATCH_KEY])
async with app.test_dependent(
keyword, allow_types=[StateParam, DependParam]
) as ctx:
ctx.pass_params(state=fake_state)
ctx.should_return(fake_state[KEYWORD_KEY])
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_matcher(app: App, load_plugin): async def test_matcher(app: App, load_plugin):

View File

@@ -65,19 +65,24 @@ async def test_startswith(
text: Optional[str], text: Optional[str],
expected: bool, expected: bool,
): ):
from nonebot.consts import STARTSWITH_KEY
from nonebot.rule import StartswithRule, startswith from nonebot.rule import StartswithRule, startswith
test_startswith = startswith(msg, ignorecase) test_startswith = startswith(msg, ignorecase)
dependent = list(test_startswith.checkers)[0] dependent = list(test_startswith.checkers)[0]
checker = dependent.call checker = dependent.call
msg = (msg,) if isinstance(msg, str) else msg
assert isinstance(checker, StartswithRule) assert isinstance(checker, StartswithRule)
assert checker.msg == (msg,) if isinstance(msg, str) else msg assert checker.msg == msg
assert checker.ignorecase == ignorecase assert checker.ignorecase == ignorecase
message = text if text is None else make_fake_message()(text) message = text if text is None else make_fake_message()(text)
event = make_fake_event(_type=type, _message=message)() event = make_fake_event(_type=type, _message=message)()
assert await dependent(event=event) == expected for prefix in msg:
state = {STARTSWITH_KEY: prefix}
assert await dependent(event=event, state=state) == expected
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -103,19 +108,24 @@ async def test_endswith(
text: Optional[str], text: Optional[str],
expected: bool, expected: bool,
): ):
from nonebot.consts import ENDSWITH_KEY
from nonebot.rule import EndswithRule, endswith from nonebot.rule import EndswithRule, endswith
test_endswith = endswith(msg, ignorecase) test_endswith = endswith(msg, ignorecase)
dependent = list(test_endswith.checkers)[0] dependent = list(test_endswith.checkers)[0]
checker = dependent.call checker = dependent.call
msg = (msg,) if isinstance(msg, str) else msg
assert isinstance(checker, EndswithRule) assert isinstance(checker, EndswithRule)
assert checker.msg == (msg,) if isinstance(msg, str) else msg assert checker.msg == msg
assert checker.ignorecase == ignorecase assert checker.ignorecase == ignorecase
message = text if text is None else make_fake_message()(text) message = text if text is None else make_fake_message()(text)
event = make_fake_event(_type=type, _message=message)() event = make_fake_event(_type=type, _message=message)()
assert await dependent(event=event) == expected for suffix in msg:
state = {ENDSWITH_KEY: suffix}
assert await dependent(event=event, state=state) == expected
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -141,19 +151,24 @@ async def test_fullmatch(
text: Optional[str], text: Optional[str],
expected: bool, expected: bool,
): ):
from nonebot.consts import FULLMATCH_KEY
from nonebot.rule import FullmatchRule, fullmatch from nonebot.rule import FullmatchRule, fullmatch
test_fullmatch = fullmatch(msg, ignorecase) test_fullmatch = fullmatch(msg, ignorecase)
dependent = list(test_fullmatch.checkers)[0] dependent = list(test_fullmatch.checkers)[0]
checker = dependent.call checker = dependent.call
msg = (msg,) if isinstance(msg, str) else msg
assert isinstance(checker, FullmatchRule) assert isinstance(checker, FullmatchRule)
assert checker.msg == ((msg,) if isinstance(msg, str) else msg) assert checker.msg == msg
assert checker.ignorecase == ignorecase assert checker.ignorecase == ignorecase
message = text if text is None else make_fake_message()(text) message = text if text is None else make_fake_message()(text)
event = make_fake_event(_type=type, _message=message)() event = make_fake_event(_type=type, _message=message)()
assert await dependent(event=event) == expected for full in msg:
state = {FULLMATCH_KEY: full}
assert await dependent(event=event, state=state) == expected
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -164,6 +179,7 @@ async def test_fullmatch(
(("key", "foo"), "message", "_foo_", True), (("key", "foo"), "message", "_foo_", True),
(("key",), "message", None, False), (("key",), "message", None, False),
(("key",), "notice", "foo", False), (("key",), "notice", "foo", False),
(("key",), "message", "foo", False),
], ],
) )
async def test_keyword( async def test_keyword(
@@ -173,6 +189,7 @@ async def test_keyword(
text: Optional[str], text: Optional[str],
expected: bool, expected: bool,
): ):
from nonebot.consts import KEYWORD_KEY
from nonebot.rule import KeywordsRule, keyword from nonebot.rule import KeywordsRule, keyword
test_keyword = keyword(*kws) test_keyword = keyword(*kws)
@@ -184,7 +201,9 @@ async def test_keyword(
message = text if text is None else make_fake_message()(text) message = text if text is None else make_fake_message()(text)
event = make_fake_event(_type=type, _message=message)() event = make_fake_event(_type=type, _message=message)()
assert await dependent(event=event) == expected for kw in kws:
state = {KEYWORD_KEY: kw}
assert await dependent(event=event, state=state) == expected
@pytest.mark.asyncio @pytest.mark.asyncio
@@ -277,6 +296,7 @@ async def test_shell_command(app: App):
assert state[SHELL_ARGV] == [] assert state[SHELL_ARGV] == []
assert isinstance(state[SHELL_ARGS], ParserExit) assert isinstance(state[SHELL_ARGS], ParserExit)
assert state[SHELL_ARGS].status != 0 assert state[SHELL_ARGS].status != 0
assert state[SHELL_ARGS].message.startswith(parser.format_usage() + "test: error:")
test_message_parser = shell_command(CMD, parser=parser) test_message_parser = shell_command(CMD, parser=parser)
dependent = list(test_message_parser.checkers)[0] dependent = list(test_message_parser.checkers)[0]
@@ -308,7 +328,7 @@ async def test_shell_command(app: App):
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.parametrize( @pytest.mark.parametrize(
"pattern,type,text,expected,matched,group,dict", "pattern,type,text,expected,matched,string,group,dict",
[ [
( (
r"(?P<key>key\d)", r"(?P<key>key\d)",
@@ -316,11 +336,12 @@ async def test_shell_command(app: App):
"_key1_", "_key1_",
True, True,
"key1", "key1",
"key1",
("key1",), ("key1",),
{"key": "key1"}, {"key": "key1"},
), ),
(r"foo", "message", None, False, None, None, None), (r"foo", "message", None, False, None, None, None, None),
(r"foo", "notice", "foo", False, None, None, None), (r"foo", "notice", "foo", False, None, None, None, None),
], ],
) )
async def test_regex( async def test_regex(
@@ -330,12 +351,13 @@ async def test_regex(
text: Optional[str], text: Optional[str],
expected: bool, expected: bool,
matched: Optional[str], matched: Optional[str],
string: Optional[str],
group: Optional[Tuple[str, ...]], group: Optional[Tuple[str, ...]],
dict: Optional[Dict[str, str]], dict: Optional[Dict[str, str]],
): ):
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.rule import RegexRule, regex from nonebot.rule import RegexRule, regex
from nonebot.consts import REGEX_DICT, REGEX_GROUP, REGEX_MATCHED from nonebot.consts import REGEX_STR, REGEX_DICT, REGEX_GROUP, REGEX_MATCHED
test_regex = regex(pattern) test_regex = regex(pattern)
dependent = list(test_regex.checkers)[0] dependent = list(test_regex.checkers)[0]
@@ -349,6 +371,7 @@ async def test_regex(
state = {} state = {}
assert await dependent(event=event, state=state) == expected assert await dependent(event=event, state=state) == expected
assert state.get(REGEX_MATCHED) == matched assert state.get(REGEX_MATCHED) == matched
assert state.get(REGEX_STR) == string
assert state.get(REGEX_GROUP) == group assert state.get(REGEX_GROUP) == group
assert state.get(REGEX_DICT) == dict assert state.get(REGEX_DICT) == dict

View File

@@ -20,7 +20,11 @@ options:
您可以选择自己喜欢的方式将插件发布到 [**PyPI**](https://pypi.org/),如使用 [**setuptools**](https://pypi.org/project/setuptools/) 或 [**Poetry**](https://pypi.org/project/poetry/)。 您可以选择自己喜欢的方式将插件发布到 [**PyPI**](https://pypi.org/),如使用 [**setuptools**](https://pypi.org/project/setuptools/) 或 [**Poetry**](https://pypi.org/project/poetry/)。
发布时,请您为自己的插件取一个清晰易懂的名字。通常而言,一款 NoneBot2 插件名称使用 `nonebot-plugin-` 作为前缀(如`nonebot-plugin-foo`),以 `nonebot_plugin_` 作为包名的前缀(如`nonebot_plugin_foo`),这并非强制规范,而是为了防止与其他 PyPI 包产生冲突,所以我们推荐您在没有特殊需求的情况下这样做。 发布时,请您为自己的插件取一个清晰易懂的名字。通常而言,一款 NoneBot2 插件名称使用 `nonebot-plugin-` 作为 PyPI 项目名前缀(如`nonebot-plugin-foo`),以 `nonebot_plugin_` 作为 Python 包名的前缀(如`nonebot_plugin_foo`),这并非强制规范,而是为了防止与其他 PyPI 包产生冲突,所以我们推荐您在没有特殊需求的情况下这样做。
:::warning
虽然在 NoneBot 2 载入插件时,插件的 Python 包名中可以使用 `-`,但是在 Python 的 import 语句中,`-` 不会被解析为包名的一部分。如果插件需要向外界提供 import 语法导入的支持,应在 Python 包名中使用 `_` 代替 `-`
:::
发布后,请确保您的插件已能公开的从 PyPI 访问到,试着检查您的插件在 PyPI 的地址,如 `https://pypi.org/project/<您的 NoneBot2 插件项目名>` 发布后,请确保您的插件已能公开的从 PyPI 访问到,试着检查您的插件在 PyPI 的地址,如 `https://pypi.org/project/<您的 NoneBot2 插件项目名>`
@@ -32,7 +36,7 @@ options:
### 申请发布到 NoneBot2 插件商店 ### 申请发布到 NoneBot2 插件商店
完成在 PyPI 的插件发布流程与源代码托管流程后,请您前往 [**NoneBot2 商店**](https://v2.nonebot.dev/store.html)页面,切换到**插件**页签,点击**发布插件**按钮。 完成在 PyPI 的插件发布流程与源代码托管流程后,请您前往 [**NoneBot2 商店**](https://v2.nonebot.dev/store)页面,切换到**插件**页签,点击**发布插件**按钮。
![插件发布界面](./images/plugin_store_publish.png) ![插件发布界面](./images/plugin_store_publish.png)
@@ -61,12 +65,12 @@ import 包名:您的插件通过 Python 导入时使用的包名,如 nonebot
之后NoneBot2 的维护者们将会对插件进行进一步的检查,以确保用户能够正常安装并使用该插件。 之后NoneBot2 的维护者们将会对插件进行进一步的检查,以确保用户能够正常安装并使用该插件。
完成这些步骤后,您的插件将会被合并到 [**NoneBot2 商店**](https://v2.nonebot.dev/store.html),而您也将成为 [**NoneBot2 贡献者**](https://github.com/nonebot/nonebot2/graphs/contributors)中的一员。 完成这些步骤后,您的插件将会被合并到 [**NoneBot2 商店**](https://v2.nonebot.dev/store),而您也将成为 [**NoneBot2 贡献者**](https://github.com/nonebot/nonebot2/graphs/contributors)中的一员。
## 完成 ## 完成
恭喜您,经过上述的发布流程,您的插件已经成功发布到 NoneBot2 商店了。 恭喜您,经过上述的发布流程,您的插件已经成功发布到 NoneBot2 商店了。
此时,您可以在 [**NoneBot2 商店**](https://v2.nonebot.dev/store.html)的插件页签查找到您的插件。同时,欢迎您成为 [**NoneBot2 贡献者**](https://github.com/nonebot/nonebot2/graphs/contributors) 此时,您可以在 [**NoneBot2 商店**](https://v2.nonebot.dev/store)的插件页签查找到您的插件。同时,欢迎您成为 [**NoneBot2 贡献者**](https://github.com/nonebot/nonebot2/graphs/contributors)
**Congratulations!** **Congratulations!**

View File

@@ -49,8 +49,8 @@ async def init_adapter(app: App, import_hook):
1. 反向 HTTPWebHook 1. 反向 HTTPWebHook
2. 反向 WebSocket 2. 反向 WebSocket
3. ~~正向 HTTP(尚未实现)~~ 3. 正向 HTTP
4. ~~正向 WebSocket(尚未实现)~~ 4. 正向 WebSocket
NoneBug 的 `test_server` 方法可以供我们测试反向连接方式。 NoneBug 的 `test_server` 方法可以供我们测试反向连接方式。

View File

@@ -24,7 +24,7 @@ nb adapter list
或者自行输入命令安装: 或者自行输入命令安装:
```bash ```bash
nb adapter install <adapter-name> nb adapter install <adapter-package>
``` ```
或者使用交互模式安装: 或者使用交互模式安装:
@@ -36,7 +36,7 @@ nb adapter install
也可以使用 pip 安装 也可以使用 pip 安装
```bash ```bash
pip install <adapter-name> pip install <adapter-package>
``` ```
<Asciinema <Asciinema

View File

@@ -7,7 +7,7 @@ import Asciinema from "@site/src/components/Asciinema";
# 安装驱动器 # 安装驱动器
NoneBot 在默认安装情况下内置了 `fastapi` 服务端驱动器,其他驱动器如 `httpx`、`aiohttp` 则需要额外安装。 NoneBot 在默认安装情况下内置了 `none` 驱动器,其他驱动器如 `fastapi`、`httpx`、`aiohttp` 则需要额外安装。
## 查看 ## 查看
@@ -38,7 +38,7 @@ nb driver install
也可以使用 pip 安装 也可以使用 pip 安装
```bash ```bash
pip install <driver-name> pip install <driver-package>
``` ```
<Asciinema <Asciinema

View File

@@ -24,7 +24,7 @@ nb plugin list
或者自行输入命令安装: 或者自行输入命令安装:
```bash ```bash
nb plugin install <plugin-name> nb plugin install <plugin-package>
``` ```
或者使用交互模式安装: 或者使用交互模式安装:
@@ -36,7 +36,7 @@ nb plugin install
也可以使用 pip 安装 也可以使用 pip 安装
```bash ```bash
pip install <plugin-name> pip install <plugin-package>
``` ```
<Asciinema <Asciinema

View File

@@ -27,13 +27,21 @@ pip uninstall nonebot
## 通过脚手架安装(推荐) ## 通过脚手架安装(推荐)
1. (可选)使用你喜欢的 Python 环境管理工具(如 Poetry、venv、Conda 等)创建新的虚拟环境 1. 安装 [pipx](https://pypa.github.io/pipx/)
2. 使用 pip 或其他包管理工具安装 nb-cliNoneBot2 会作为其依赖被一起安装
```bash ```bash
pip install nb-cli python -m pip install --user pipx
python -m pipx ensurepath
``` ```
2. 安装脚手架
```bash
pipx install nb-cli
```
安装完成后,你可以在命令行使用 `nb` 命令来使用脚手架。如果出现无法找到命令的情况(例如:`Command not found`),请参考 [pipx 文档](https://pypa.github.io/pipx/) 检查你的环境变量。
<Asciinema <Asciinema
url="https://asciinema.org/a/464654.cast" url="https://asciinema.org/a/464654.cast"
options={{ theme: "monokai", poster: "npt:2.8" }} options={{ theme: "monokai", poster: "npt:2.8" }}
@@ -48,9 +56,9 @@ nb-cli 的使用方法详见[使用脚手架](./nb-cli.md)
如果你不想使用脚手架,可以直接安装 NoneBot2并自行完成开发配置。 如果你不想使用脚手架,可以直接安装 NoneBot2并自行完成开发配置。
```bash ```bash
pip install nonebot2 pip install nonebot2[fastapi]
# 也可以通过 Poetry 安装 # 也可以通过 Poetry 安装
poetry add nonebot2 poetry add nonebot2[fastapi]
``` ```
## 从 GitHub 安装 ## 从 GitHub 安装

View File

@@ -10,10 +10,12 @@ options:
# 使用脚手架 # 使用脚手架
`nb-cli` 详细参考文档已移至 <https://cli.nonebot.dev>
## 安装 ## 安装
```bash ```bash
pip install nb-cli pipx install nb-cli
``` ```
## 初次使用 ## 初次使用
@@ -21,14 +23,11 @@ pip install nb-cli
在安装完成之后,即可在命令行使用 nb-cli 的命令 `nb` 进行开发: 在安装完成之后,即可在命令行使用 nb-cli 的命令 `nb` 进行开发:
```bash ```bash
# 直接使用 nb 命令
nb nb
# 或使用 Python 执行 module
python -m nb_cli
``` ```
:::warning 注意 :::warning 注意
通常情况下,你可以直接在命令行使用 `nb` 命令,但如果命令行出现 `Command not found` 错误,这是由于环境变量 `PATH` 没有正确配置或未配置导致的,可以使用第二种方式代替 通常情况下,你可以直接在命令行使用 `nb` 命令。如果出现无法找到命令的情况(例如:`Command not found`),请参考 [pipx 文档](https://pypa.github.io/pipx/) 检查你的环境变量
::: :::
## 使用方式 ## 使用方式

View File

@@ -14,16 +14,36 @@ options:
NoneBot 提供了两种方式来调用机器人平台 API两种方式都需要首先获得 Bot 实例,然后调用相应的方法。 NoneBot 提供了两种方式来调用机器人平台 API两种方式都需要首先获得 Bot 实例,然后调用相应的方法。
例如,如果需要调用机器人平台的 `get_user_info` API可以这样做 ## 获取 Bot 实例
```python ```python
from nonebot import get_bot from nonebot import get_bot
bot = get_bot("bot_id") bot = get_bot() # 获取第一个已连接的 bot 实例
bot = get_bot("bot_id") # 获取指定 bot_id 的 bot 实例
```
在事件处理依赖中,我们可以使用更为简便的办法来获取 bot 实例,详情可以参考 [获取上下文信息-Bot](https://v2.nonebot.dev/docs/tutorial/plugin/create-handler#bot)
```python
from nonebot.adapters import Bot
async def handle_func(bot: Bot): # 通过依赖注入获取 bot 实例
...
```
## 调用 API
如果需要调用某个机器人平台的 `get_user_info` API我们可以使用以下任意一种方式
```python
# 通过 bot 实例上的魔术方法直接使用.操作符调用 API
result = await bot.get_user_info(user_id=12345678) result = await bot.get_user_info(user_id=12345678)
await bot.call_api("get_user_info", user_id=12345678)
# 通过 bot 实例上的 call_api 方法调用 API
result = await bot.call_api("get_user_info", user_id=12345678)
``` ```
:::tip 提示 :::tip 提示
API 由平台提供,请参考平台文档。 实际可用的 API 由平台提供,请参考平台文档。
::: :::

View File

@@ -146,6 +146,12 @@ nonebot.run(app="bot:app")
默认值:`None` 默认值:`None`
说明:不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值 说明:不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
##### `fastapi_extra`
类型:`Dist[str, Any]`
默认值:`{}`
说明:传递给 `FastAPI` 的其他参数
### Quart ### Quart
类型:`ReverseDriver` 类型:`ReverseDriver`
@@ -199,6 +205,12 @@ nonebot.run(app="bot:app")
默认值:`None` 默认值:`None`
说明:不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值 说明:不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
##### `quart_extra`
类型:`Dist[str, Any]`
默认值:`{}`
说明:传递给 `Quart` 的其他参数
### HTTPX ### HTTPX
类型:`ForwardDriver` 类型:`ForwardDriver`

View File

@@ -224,8 +224,8 @@ NICKNAME=["bot"]
命令消息的起始符和分隔符。用于 [`command`](../api/rule.md#command) 规则。 命令消息的起始符和分隔符。用于 [`command`](../api/rule.md#command) 规则。
```env ```env
COMMAND_START={"/", "!"} COMMAND_START=["/", "!"]
COMMAND_SEP={".", "/"} COMMAND_SEP=[".", "/"]
``` ```
### Session Expire Timeout ### Session Expire Timeout

View File

@@ -11,7 +11,7 @@ NoneBot 使用 [Loguru](https://loguru.readthedocs.io/) 进行日志记录,并
NoneBot 启动时会添加一个默认的日志 handler。此 handler 将会将日志输出到 **stdout**,并且根据配置的日志级别进行过滤。 NoneBot 启动时会添加一个默认的日志 handler。此 handler 将会将日志输出到 **stdout**,并且根据配置的日志级别进行过滤。
[默认格式](../api/log.md#default_format)): [默认格式](../api/log.md#default_format):
```python ```python
default_format: str = ( default_format: str = (

View File

@@ -321,18 +321,18 @@ matcher = on_shell_command("cmd")
async def _(foo: List[Union[str, MessageSegment]] = ShellCommandArgv()): ... async def _(foo: List[Union[str, MessageSegment]] = ShellCommandArgv()): ...
``` ```
### RegexMatched ### RegexStr
获取正则匹配结果。 获取正则匹配结果的文本
```python {7} ```python {7}
from nonebot import on_regex from nonebot import on_regex
from nonebot.params import RegexMatched from nonebot.params import RegexStr
matcher = on_regex("regex") matcher = on_regex("regex")
@matcher.handle() @matcher.handle()
async def _(foo: str = RegexMatched()): ... async def _(foo: str = RegexStr()): ...
``` ```
### RegexGroup ### RegexGroup
@@ -363,6 +363,62 @@ matcher = on_regex("regex")
async def _(foo: Dict[str, Any] = RegexDict()): ... async def _(foo: Dict[str, Any] = RegexDict()): ...
``` ```
### Startswith
获取触发响应器的消息前缀字符串。
```python {7}
from nonebot import on_startswith
from nonebot.params import Startswith
matcher = on_startswith("prefix")
@matcher.handle()
async def _(foo: str = Startswith()): ...
```
### Endswith
获取触发响应器的消息后缀字符串。
```python {7}
from nonebot import on_endswith
from nonebot.params import Endswith
matcher = on_endswith("suffix")
@matcher.handle()
async def _(foo: str = Endswith()): ...
```
### Fullmatch
获取触发响应器的消息字符串。
```python {7}
from nonebot import on_fullmatch
from nonebot.params import Fullmatch
matcher = on_fullmatch("fullmatch")
@matcher.handle()
async def _(foo: str = Fullmatch()): ...
```
### Keyword
获取触发响应器的关键字字符串。
```python {7}
from nonebot import on_keyword
from nonebot.params import Keyword
matcher = on_keyword({"keyword"})
@matcher.handle()
async def _(foo: str = Keyword()): ...
```
### Matcher ### Matcher
获取当前事件响应器实例。 获取当前事件响应器实例。

View File

@@ -17,14 +17,16 @@ export type Obj = {
}; };
export function filterObjs(filter: string, objs: Obj[]): Obj[] { export function filterObjs(filter: string, objs: Obj[]): Obj[] {
let filterLower = filter.toLowerCase();
return objs.filter((o) => { return objs.filter((o) => {
return ( return (
o.module_name?.includes(filter) || o.module_name?.toLowerCase().includes(filterLower) ||
o.project_link?.includes(filter) || o.project_link?.toLowerCase().includes(filterLower) ||
o.name.includes(filter) || o.name.toLowerCase().includes(filterLower) ||
o.desc.includes(filter) || o.desc.toLowerCase().includes(filterLower) ||
o.author.includes(filter) || o.author.toLowerCase().includes(filterLower) ||
o.tags.filter((t) => t.label.includes(filter)).length > 0 o.tags.filter((t) => t.label.toLowerCase().includes(filterLower)).length >
0
); );
}); });
} }

View File

@@ -5,6 +5,207 @@ toc_max_heading_level: 2
# 更新日志 # 更新日志
## v2.0.0rc3
### 🚀 新功能
- Feature: 添加事件响应器检查完成日志 [@A-kirami](https://github.com/A-kirami) ([#1578](https://github.com/nonebot/nonebot2/pull/1578))
- Remove: 移除默认安装 FastAPI [@yanyongyu](https://github.com/yanyongyu) ([#1557](https://github.com/nonebot/nonebot2/pull/1557))
- Feature: 支持给 `FastAPI``Quart` 传递额外的参数 [@A-kirami](https://github.com/A-kirami) ([#1543](https://github.com/nonebot/nonebot2/pull/1543))
- Feature: 添加 `logger` 重导出 [@A-kirami](https://github.com/A-kirami) ([#1526](https://github.com/nonebot/nonebot2/pull/1526))
- Feature: 将 block driver 转正为 none 驱动器 [@he0119](https://github.com/he0119) ([#1522](https://github.com/nonebot/nonebot2/pull/1522))
- Develop: 使用 pycln 自动移除未使用的 import [@yanyongyu](https://github.com/yanyongyu) ([#1481](https://github.com/nonebot/nonebot2/pull/1481))
- Feature: 添加正则匹配文本注入 [@A-kirami](https://github.com/A-kirami) ([#1457](https://github.com/nonebot/nonebot2/pull/1457))
- Feature: 支持主动销毁事件响应器 [@A-kirami](https://github.com/A-kirami) ([#1444](https://github.com/nonebot/nonebot2/pull/1444))
### 🐛 Bug 修复
- Fix: 屏蔽 fastapi 0.89.0 [@yanyongyu](https://github.com/yanyongyu) ([#1574](https://github.com/nonebot/nonebot2/pull/1574))
- Fix: 修复子插件加载失败时没有从父插件中移除的问题 [@A-kirami](https://github.com/A-kirami) ([#1559](https://github.com/nonebot/nonebot2/pull/1559))
- Fix: 修复客户端请求未处理 cookies [@yanyongyu](https://github.com/yanyongyu) ([#1491](https://github.com/nonebot/nonebot2/pull/1491))
- Fix: `on_type` typing error [@yanyongyu](https://github.com/yanyongyu) ([#1482](https://github.com/nonebot/nonebot2/pull/1482))
- Fix: 修复 ArgumentParser 错误信息叠加问题 [@yanyongyu](https://github.com/yanyongyu) ([#1426](https://github.com/nonebot/nonebot2/pull/1426))
### 📝 文档
- Docs: 修改更新部分文档 [@yanyongyu](https://github.com/yanyongyu) ([#1615](https://github.com/nonebot/nonebot2/pull/1615))
- Docs: 商店搜索大小写不敏感 [@StarHeartHunt](https://github.com/StarHeartHunt) ([#1609](https://github.com/nonebot/nonebot2/pull/1609))
- Docs: 更新测试文档中的连接方式\&细化插件发布描述 [@StarHeartHunt](https://github.com/StarHeartHunt) ([#1504](https://github.com/nonebot/nonebot2/pull/1504))
- Docs: 修复文档中部分超链接跳转到 `/store.html` 的问题 [@yzyyz1387](https://github.com/yzyyz1387) ([#1470](https://github.com/nonebot/nonebot2/pull/1470))
- Fix: 补充 `params` 模块的类型注解 [@A-kirami](https://github.com/A-kirami) ([#1458](https://github.com/nonebot/nonebot2/pull/1458))
- Docs: 移除文档 `自定义日志` 中多余的符号 [@A-kirami](https://github.com/A-kirami) ([#1448](https://github.com/nonebot/nonebot2/pull/1448))
- Docs: 完善 `调用平台 API` 部分 [@A-kirami](https://github.com/A-kirami) ([#1447](https://github.com/nonebot/nonebot2/pull/1447))
- Docs: 修正文档中部分配置文件示例的符号误用 [@MingxuanGame](https://github.com/MingxuanGame) ([#1432](https://github.com/nonebot/nonebot2/pull/1432))
### 💫 杂项
- Plugin: 移除 nonebot-plugin-puppet [@j1g5awi](https://github.com/j1g5awi) ([#1605](https://github.com/nonebot/nonebot2/pull/1605))
- Plugin: 更新 MC 的插件信息 [@nikissXI](https://github.com/nikissXI) ([#1589](https://github.com/nonebot/nonebot2/pull/1589))
- Plugin: 移除 `nonebot-plugin-aidraw` [@A-kirami](https://github.com/A-kirami) ([#1588](https://github.com/nonebot/nonebot2/pull/1588))
- Plugins: 更新 ayaka_games 插件名和描述 [@bridgeL](https://github.com/bridgeL) ([#1586](https://github.com/nonebot/nonebot2/pull/1586))
- Plugin: 更新 tts_gal 插件名和描述 [@dpm12345](https://github.com/dpm12345) ([#1581](https://github.com/nonebot/nonebot2/pull/1581))
- Plugin: 移除 `nonebot_plugin_super_resolution` [@A-kirami](https://github.com/A-kirami) ([#1561](https://github.com/nonebot/nonebot2/pull/1561))
- Plugin: 更新 OlivOS.nb2 import 包名 [@j1g5awi](https://github.com/j1g5awi) ([#1560](https://github.com/nonebot/nonebot2/pull/1560))
- Develop: 添加 pyright 环境配置 [@yanyongyu](https://github.com/yanyongyu) ([#1554](https://github.com/nonebot/nonebot2/pull/1554))
- CI: 优化触发条件减少无效运行 [@he0119](https://github.com/he0119) ([#1545](https://github.com/nonebot/nonebot2/pull/1545))
- Plugin: 删除 ayaka_who_is_suspect 插件 [@bridgeL](https://github.com/bridgeL) ([#1525](https://github.com/nonebot/nonebot2/pull/1525))
- Fix: 修复异常在 traceback 中无法正常显示信息 [@he0119](https://github.com/he0119) ([#1521](https://github.com/nonebot/nonebot2/pull/1521))
- CI: 添加插件加载测试 [@he0119](https://github.com/he0119) ([#1519](https://github.com/nonebot/nonebot2/pull/1519))
- Plugin: 移除 `nonebot-plugin-filehost` [@mnixry](https://github.com/mnixry) ([#1516](https://github.com/nonebot/nonebot2/pull/1516))
- Plugin: 更新 `abstain_diary` 插件名和描述 [@Ikaros-521](https://github.com/Ikaros-521) ([#1509](https://github.com/nonebot/nonebot2/pull/1509))
- Plugin: 更新 gpt3 插件模块名 [@chrisyy2003](https://github.com/chrisyy2003) ([#1501](https://github.com/nonebot/nonebot2/pull/1501))
- Plugin: 更新 随机禁言 插件功能描述 [@Ikaros-521](https://github.com/Ikaros-521) ([#1495](https://github.com/nonebot/nonebot2/pull/1495))
- Plugin: 更新 multi chatgpt 插件仓库地址 [@chrisyy2003](https://github.com/chrisyy2003) ([#1487](https://github.com/nonebot/nonebot2/pull/1487))
- Plugin: 更新 ayaka_games 介绍 [@bridgeL](https://github.com/bridgeL) ([#1431](https://github.com/nonebot/nonebot2/pull/1431))
- Plugin: 修改 novelai send magiadice 插件模块名 [@sena-nana](https://github.com/sena-nana) ([#1423](https://github.com/nonebot/nonebot2/pull/1423))
### 🍻 插件发布
- Plugin: 反向词典 [@yanyongyu](https://github.com/yanyongyu) ([#1619](https://github.com/nonebot/nonebot2/pull/1619))
- Plugin: PicMCStat [@yanyongyu](https://github.com/yanyongyu) ([#1614](https://github.com/nonebot/nonebot2/pull/1614))
- Plugin: 犯人在跳舞 [@yanyongyu](https://github.com/yanyongyu) ([#1608](https://github.com/nonebot/nonebot2/pull/1608))
- Plugin: 喵喵自记菜谱 [@yanyongyu](https://github.com/yanyongyu) ([#1599](https://github.com/nonebot/nonebot2/pull/1599))
- Plugin: 语音功能 [@yanyongyu](https://github.com/yanyongyu) ([#1597](https://github.com/nonebot/nonebot2/pull/1597))
- Plugin: OrangeDice! [@yanyongyu](https://github.com/yanyongyu) ([#1595](https://github.com/nonebot/nonebot2/pull/1595))
- Plugin: 简易谷歌翻译插件 [@yanyongyu](https://github.com/yanyongyu) ([#1593](https://github.com/nonebot/nonebot2/pull/1593))
- Plugin: 哔哩哔哩 q 群登录 [@yanyongyu](https://github.com/yanyongyu) ([#1591](https://github.com/nonebot/nonebot2/pull/1591))
- Plugin: 原神实时公告 [@yanyongyu](https://github.com/yanyongyu) ([#1585](https://github.com/nonebot/nonebot2/pull/1585))
- Plugin: 心灵鸡汤 [@yanyongyu](https://github.com/yanyongyu) ([#1580](https://github.com/nonebot/nonebot2/pull/1580))
- Plugin: Bing 每日图片获取 [@yanyongyu](https://github.com/yanyongyu) ([#1577](https://github.com/nonebot/nonebot2/pull/1577))
- Plugin: 星座运势 [@yanyongyu](https://github.com/yanyongyu) ([#1572](https://github.com/nonebot/nonebot2/pull/1572))
- Plugin: 回声洞 [@yanyongyu](https://github.com/yanyongyu) ([#1573](https://github.com/nonebot/nonebot2/pull/1573))
- Plugin: 整点报时 [@yanyongyu](https://github.com/yanyongyu) ([#1569](https://github.com/nonebot/nonebot2/pull/1569))
- Plugin: Hypixel 数据查询 [@yanyongyu](https://github.com/yanyongyu) ([#1556](https://github.com/nonebot/nonebot2/pull/1556))
- Plugin: 查找图片出处 [@yanyongyu](https://github.com/yanyongyu) ([#1553](https://github.com/nonebot/nonebot2/pull/1553))
- Plugin: 云签到 [@yanyongyu](https://github.com/yanyongyu) ([#1551](https://github.com/nonebot/nonebot2/pull/1551))
- Plugin: 图像标注 [@yanyongyu](https://github.com/yanyongyu) ([#1550](https://github.com/nonebot/nonebot2/pull/1550))
- Plugin: 对对联 [@yanyongyu](https://github.com/yanyongyu) ([#1542](https://github.com/nonebot/nonebot2/pull/1542))
- Plugin: 群聊学习 [@yanyongyu](https://github.com/yanyongyu) ([#1540](https://github.com/nonebot/nonebot2/pull/1540))
- Plugin: 求生之路 2——服务器操作 [@yanyongyu](https://github.com/yanyongyu) ([#1538](https://github.com/nonebot/nonebot2/pull/1538))
- Plugin: setu_customization [@yanyongyu](https://github.com/yanyongyu) ([#1537](https://github.com/nonebot/nonebot2/pull/1537))
- Plugin: 主动消息撤回 [@yanyongyu](https://github.com/yanyongyu) ([#1536](https://github.com/nonebot/nonebot2/pull/1536))
- Plugin: HttpCat🐱 猫猫 http 状态码 [@yanyongyu](https://github.com/yanyongyu) ([#1529](https://github.com/nonebot/nonebot2/pull/1529))
- Plugin: 命令探查 [@yanyongyu](https://github.com/yanyongyu) ([#1524](https://github.com/nonebot/nonebot2/pull/1524))
- Plugin: AnimalVoice_Convert [@yanyongyu](https://github.com/yanyongyu) ([#1518](https://github.com/nonebot/nonebot2/pull/1518))
- Plugin: 服务状态查询 [@yanyongyu](https://github.com/yanyongyu) ([#1513](https://github.com/nonebot/nonebot2/pull/1513))
- Plugin: 腾讯云图像变换 [@yanyongyu](https://github.com/yanyongyu) ([#1515](https://github.com/nonebot/nonebot2/pull/1515))
- Plugin: Ping [@yanyongyu](https://github.com/yanyongyu) ([#1508](https://github.com/nonebot/nonebot2/pull/1508))
- Plugin: 群友召唤术 [@yanyongyu](https://github.com/yanyongyu) ([#1503](https://github.com/nonebot/nonebot2/pull/1503))
- Plugin: 战地群聊天插件 [@yanyongyu](https://github.com/yanyongyu) ([#1506](https://github.com/nonebot/nonebot2/pull/1506))
- Plugin: 不要复读 [@yanyongyu](https://github.com/yanyongyu) ([#1500](https://github.com/nonebot/nonebot2/pull/1500))
- Plugin: JAVA MC 服务器信息查询 [@yanyongyu](https://github.com/yanyongyu) ([#1497](https://github.com/nonebot/nonebot2/pull/1497))
- Plugin: 防撤回 [@yanyongyu](https://github.com/yanyongyu) ([#1489](https://github.com/nonebot/nonebot2/pull/1489))
- Plugin: 随机禁言 [@yanyongyu](https://github.com/yanyongyu) ([#1486](https://github.com/nonebot/nonebot2/pull/1486))
- Plugin: 只因进化录 [@yanyongyu](https://github.com/yanyongyu) ([#1484](https://github.com/nonebot/nonebot2/pull/1484))
- Plugin: GPT3 [@yanyongyu](https://github.com/yanyongyu) ([#1480](https://github.com/nonebot/nonebot2/pull/1480))
- Plugin: 熊老板 [@yanyongyu](https://github.com/yanyongyu) ([#1472](https://github.com/nonebot/nonebot2/pull/1472))
- Plugin: QQ 群文件备份 [@yanyongyu](https://github.com/yanyongyu) ([#1478](https://github.com/nonebot/nonebot2/pull/1478))
- Plugin: 戒色打卡日记 [@yanyongyu](https://github.com/yanyongyu) ([#1475](https://github.com/nonebot/nonebot2/pull/1475))
- Plugin: nonebot_plugin_idiom [@yanyongyu](https://github.com/yanyongyu) ([#1469](https://github.com/nonebot/nonebot2/pull/1469))
- Plugin: 随机配色方案 [@yanyongyu](https://github.com/yanyongyu) ([#1466](https://github.com/nonebot/nonebot2/pull/1466))
- Plugin: multi-ChatGPT [@yanyongyu](https://github.com/yanyongyu) ([#1462](https://github.com/nonebot/nonebot2/pull/1462))
- Plugin: 权限控制 [@yanyongyu](https://github.com/yanyongyu) ([#1464](https://github.com/nonebot/nonebot2/pull/1464))
- Plugin: 汇率换算 [@yanyongyu](https://github.com/yanyongyu) ([#1452](https://github.com/nonebot/nonebot2/pull/1452))
- Plugin: 全群广播 [@yanyongyu](https://github.com/yanyongyu) ([#1450](https://github.com/nonebot/nonebot2/pull/1450))
- Plugin: 图片背景消除 [@yanyongyu](https://github.com/yanyongyu) ([#1446](https://github.com/nonebot/nonebot2/pull/1446))
- Plugin: 雀魂信息查询 [@yanyongyu](https://github.com/yanyongyu) ([#1443](https://github.com/nonebot/nonebot2/pull/1443))
- Plugin: ChatGPT [@yanyongyu](https://github.com/yanyongyu) ([#1439](https://github.com/nonebot/nonebot2/pull/1439))
- Plugin: 免费快捷点歌插件 [@yanyongyu](https://github.com/yanyongyu) ([#1436](https://github.com/nonebot/nonebot2/pull/1436))
- Plugin: 动画截图追溯来源 [@yanyongyu](https://github.com/yanyongyu) ([#1434](https://github.com/nonebot/nonebot2/pull/1434))
- Plugin: b 站图片下载 [@yanyongyu](https://github.com/yanyongyu) ([#1430](https://github.com/nonebot/nonebot2/pull/1430))
- Plugin: 记事本 [@yanyongyu](https://github.com/yanyongyu) ([#1420](https://github.com/nonebot/nonebot2/pull/1420))
- Plugin: 原神前瞻直播兑换码查询 [@yanyongyu](https://github.com/yanyongyu) ([#1422](https://github.com/nonebot/nonebot2/pull/1422))
### 🍻 机器人发布
- Bot: SuzunoBot [@yanyongyu](https://github.com/yanyongyu) ([#1601](https://github.com/nonebot/nonebot2/pull/1601))
- Bot: 辞辞(cici)Bot [@yanyongyu](https://github.com/yanyongyu) ([#1583](https://github.com/nonebot/nonebot2/pull/1583))
- Bot: RanBot [@yanyongyu](https://github.com/yanyongyu) ([#1511](https://github.com/nonebot/nonebot2/pull/1511))
### 🍻 适配器发布
- Adapter: BilibiliLive [@yanyongyu](https://github.com/yanyongyu) ([#1617](https://github.com/nonebot/nonebot2/pull/1617))
- Adapter: Spigot [@yanyongyu](https://github.com/yanyongyu) ([#1612](https://github.com/nonebot/nonebot2/pull/1612))
## v2.0.0rc2
### 💥 破坏性变更
- Feature: 使用 `importlib.metadata` 替换 `pkg_resources` [@A-kirami](https://github.com/A-kirami) ([#1388](https://github.com/nonebot/nonebot2/pull/1388))
### 🚀 新功能
- Feature: 支持自定义 matchers 存储管理 [@yanyongyu](https://github.com/yanyongyu) ([#1395](https://github.com/nonebot/nonebot2/pull/1395))
- Feature: 升级 devcontainer 配置 [@yanyongyu](https://github.com/yanyongyu) ([#1392](https://github.com/nonebot/nonebot2/pull/1392))
- Feature: 使用 `importlib.metadata` 替换 `pkg_resources` [@A-kirami](https://github.com/A-kirami) ([#1388](https://github.com/nonebot/nonebot2/pull/1388))
- CI: 测试环境添加 Python 3.11 [@StarHeartHunt](https://github.com/StarHeartHunt) ([#1366](https://github.com/nonebot/nonebot2/pull/1366))
- Feature: 新增 dotenv 嵌套配置项支持 [@yanyongyu](https://github.com/yanyongyu) ([#1324](https://github.com/nonebot/nonebot2/pull/1324))
- Feature: 添加 State 响应器触发消息注入 [@A-kirami](https://github.com/A-kirami) ([#1315](https://github.com/nonebot/nonebot2/pull/1315))
- Remove: 移除无用的 namespace 声明 [@yanyongyu](https://github.com/yanyongyu) ([#1306](https://github.com/nonebot/nonebot2/pull/1306))
### 🐛 Bug 修复
- Fix: Bot `__getattr__` 不再对 `__xxx__` 方法返回 [@synodriver](https://github.com/synodriver) ([#1398](https://github.com/nonebot/nonebot2/pull/1398))
- Fix: 修复 run pre/post hook 没有在正确的上下文中运行 [@yanyongyu](https://github.com/yanyongyu) ([#1391](https://github.com/nonebot/nonebot2/pull/1391))
### 📝 文档
- Docs: 添加 ntchat 社区适配器 [@JustUndertaker](https://github.com/JustUndertaker) ([#1414](https://github.com/nonebot/nonebot2/pull/1414))
### 💫 杂项
- Plugin: b 站用户信息查询 [@Ikaros-521](https://github.com/Ikaros-521) ([#1410](https://github.com/nonebot/nonebot2/pull/1410))
- Plugin: 由于 Sena-nana 项目拆分,之前的插件地址更改 [@sena-nana](https://github.com/sena-nana) ([#1378](https://github.com/nonebot/nonebot2/pull/1378))
- Plugin: 更新 ayaka 插件的主页链接 [@bridgeL](https://github.com/bridgeL) ([#1346](https://github.com/nonebot/nonebot2/pull/1346))
- Plugin: 补充 novelai 插件信息 [@sena-nana](https://github.com/sena-nana) ([#1333](https://github.com/nonebot/nonebot2/pull/1333))
- Bot: 修改 Inkar Suki 描述 [@HornCopper](https://github.com/HornCopper) ([#1312](https://github.com/nonebot/nonebot2/pull/1312))
- Plugin: 修改插件 MCQQ MCRcon 主页地址 [@17TheWord](https://github.com/17TheWord) ([#1303](https://github.com/nonebot/nonebot2/pull/1303))
### 🍻 插件发布
- Plugin: 谁在窥屏 [@yanyongyu](https://github.com/yanyongyu) ([#1416](https://github.com/nonebot/nonebot2/pull/1416))
- Plugin: 免费版 NovelAI 生图插件 [@yanyongyu](https://github.com/yanyongyu) ([#1408](https://github.com/nonebot/nonebot2/pull/1408))
- Plugin: sky 光遇 [@yanyongyu](https://github.com/yanyongyu) ([#1394](https://github.com/nonebot/nonebot2/pull/1394))
- Plugin: Colab-NovelAI [@yanyongyu](https://github.com/yanyongyu) ([#1390](https://github.com/nonebot/nonebot2/pull/1390))
- Plugin: b 站用户直播号、粉丝、舰团数查询 [@yanyongyu](https://github.com/yanyongyu) ([#1385](https://github.com/nonebot/nonebot2/pull/1385))
- Plugin: 投胎模拟器 [@yanyongyu](https://github.com/yanyongyu) ([#1382](https://github.com/nonebot/nonebot2/pull/1382))
- Plugin: Apex API Query [@yanyongyu](https://github.com/yanyongyu) ([#1375](https://github.com/nonebot/nonebot2/pull/1375))
- Plugin: 随个人 [@yanyongyu](https://github.com/yanyongyu) ([#1373](https://github.com/nonebot/nonebot2/pull/1373))
- Plugin: 动漫资源获取 [@yanyongyu](https://github.com/yanyongyu) ([#1371](https://github.com/nonebot/nonebot2/pull/1371))
- Plugin: 日麻小工具 [@yanyongyu](https://github.com/yanyongyu) ([#1365](https://github.com/nonebot/nonebot2/pull/1365))
- Plugin: 图像超分辨率增强 [@yanyongyu](https://github.com/yanyongyu) ([#1362](https://github.com/nonebot/nonebot2/pull/1362))
- Plugin: 二次元化图像 [@yanyongyu](https://github.com/yanyongyu) ([#1360](https://github.com/nonebot/nonebot2/pull/1360))
- Plugin: 日麻寄分器 [@yanyongyu](https://github.com/yanyongyu) ([#1357](https://github.com/nonebot/nonebot2/pull/1357))
- Plugin: 文本生成器 [@yanyongyu](https://github.com/yanyongyu) ([#1355](https://github.com/nonebot/nonebot2/pull/1355))
- Plugin: 反嘴臭插件 [@yanyongyu](https://github.com/yanyongyu) ([#1350](https://github.com/nonebot/nonebot2/pull/1350))
- Plugin: 用户\&群聊黑名单 [@yanyongyu](https://github.com/yanyongyu) ([#1348](https://github.com/nonebot/nonebot2/pull/1348))
- Plugin: NoneBot SQLAlchemy 封装 [@yanyongyu](https://github.com/yanyongyu) ([#1345](https://github.com/nonebot/nonebot2/pull/1345))
- Plugin: 通用抽图/语音 [@yanyongyu](https://github.com/yanyongyu) ([#1341](https://github.com/nonebot/nonebot2/pull/1341))
- Plugin: kfcrazy [@yanyongyu](https://github.com/yanyongyu) ([#1339](https://github.com/nonebot/nonebot2/pull/1339))
- Plugin: 二次元图像鉴赏 [@yanyongyu](https://github.com/yanyongyu) ([#1337](https://github.com/nonebot/nonebot2/pull/1337))
- Plugin: ayaka 衍生插件 - 坏词撤回 [@yanyongyu](https://github.com/yanyongyu) ([#1335](https://github.com/nonebot/nonebot2/pull/1335))
- Plugin: ayaka 衍生插件 - 时区助手 [@yanyongyu](https://github.com/yanyongyu) ([#1332](https://github.com/nonebot/nonebot2/pull/1332))
- Plugin: ayaka 衍生插件 - 谁是卧底 [@yanyongyu](https://github.com/yanyongyu) ([#1330](https://github.com/nonebot/nonebot2/pull/1330))
- Plugin: ayaka 衍生插件 - 小游戏合集 [@yanyongyu](https://github.com/yanyongyu) ([#1328](https://github.com/nonebot/nonebot2/pull/1328))
- Plugin: bnhhsh -「不能好好说话!」 [@yanyongyu](https://github.com/yanyongyu) ([#1326](https://github.com/nonebot/nonebot2/pull/1326))
- Plugin: AI 绘图 [@yanyongyu](https://github.com/yanyongyu) ([#1323](https://github.com/nonebot/nonebot2/pull/1323))
- Plugin: novelai [@yanyongyu](https://github.com/yanyongyu) ([#1319](https://github.com/nonebot/nonebot2/pull/1319))
- Plugin: 游戏王小程序查价 [@yanyongyu](https://github.com/yanyongyu) ([#1317](https://github.com/nonebot/nonebot2/pull/1317))
- Plugin: 监测群事件 [@yanyongyu](https://github.com/yanyongyu) ([#1320](https://github.com/nonebot/nonebot2/pull/1320))
- Plugin: 轮盘禁言小游戏 [@yanyongyu](https://github.com/yanyongyu) ([#1311](https://github.com/nonebot/nonebot2/pull/1311))
- Plugin: 真白萌自动签到 [@yanyongyu](https://github.com/yanyongyu) ([#1308](https://github.com/nonebot/nonebot2/pull/1308))
- Plugin: BiliRequestAll [@yanyongyu](https://github.com/yanyongyu) ([#1302](https://github.com/nonebot/nonebot2/pull/1302))
- Plugin: 监听者 [@yanyongyu](https://github.com/yanyongyu) ([#1299](https://github.com/nonebot/nonebot2/pull/1299))
### 🍻 机器人发布
- Bot: Bread Dog Bot [@yanyongyu](https://github.com/yanyongyu) ([#1380](https://github.com/nonebot/nonebot2/pull/1380))
- Bot: hsbot [@yanyongyu](https://github.com/yanyongyu) ([#1369](https://github.com/nonebot/nonebot2/pull/1369))
### 🍻 适配器发布
- Adapter: Ntchat [@yanyongyu](https://github.com/yanyongyu) ([#1314](https://github.com/nonebot/nonebot2/pull/1314))
## v2.0.0-rc.1 ## v2.0.0-rc.1
### 💥 破坏性变更 ### 💥 破坏性变更

View File

@@ -98,5 +98,45 @@
"homepage": "https://github.com/nonebot/adapter-github", "homepage": "https://github.com/nonebot/adapter-github",
"tags": [], "tags": [],
"is_official": true "is_official": true
},
{
"module_name": "nonebot.adapters.ntchat",
"project_link": "nonebot-adapter-ntchat",
"name": "Ntchat",
"desc": "pc hook的微信客户端适配",
"author": "JustUndertaker",
"homepage": "https://github.com/JustUndertaker/adapter-ntchat",
"tags": [
{
"label": "微信",
"color": "#ea5252"
}
],
"is_official": false
},
{
"module_name": "nonebot.adapters.spigot",
"project_link": "nonebot-adapter-spigot",
"name": "Spigot",
"desc": "MineCraft通信适配",
"author": "17TheWord",
"homepage": "https://github.com/17TheWord/nonebot-adapter-spigot",
"tags": [
{
"label": "Minecraft",
"color": "#4ef0ea"
}
],
"is_official": false
},
{
"module_name": "nonebot.adapter.bilibili",
"project_link": "nonebot-adapter-bilibili",
"name": "BilibiliLive",
"desc": "b站直播间ws协议",
"author": "wwweww",
"homepage": "https://github.com/wwweww/adapter-bilibili",
"tags": [],
"is_official": false
} }
] ]

View File

@@ -123,7 +123,7 @@
}, },
{ {
"name": "Inkar Suki", "name": "Inkar Suki",
"desc": "一个十分方便的Bot支持包括Webhook、群管、互动等一系列功能", "desc": "一个十分方便的Bot支持包括Webhook、群管、剑网3等一系列功能,持续更新中……",
"author": "HornCopper", "author": "HornCopper",
"homepage": "https://github.com/HornCopper/Inkar-Suki", "homepage": "https://github.com/HornCopper/Inkar-Suki",
"tags": [ "tags": [
@@ -136,8 +136,8 @@
"color": "#374fd0" "color": "#374fd0"
}, },
{ {
"label": "群管", "label": "剑网3",
"color": "#4ede39" "color": "#ff0033"
} }
], ],
"is_official": false "is_official": false
@@ -276,5 +276,89 @@
} }
], ],
"is_official": false "is_official": false
},
{
"name": "hsbot",
"desc": "服务于《炉石传说》玩家的机器人,上线至今已有加入十余个个炉石相关群聊,上千名用户使用,响应请求数万次。 数据使用HSreplay, Fbigame, Hearthstone API",
"author": "gzy02",
"homepage": "https://github.com/gzy02/hsbot",
"tags": [
{
"label": "炉石传说",
"color": "#526fea"
}
],
"is_official": false
},
{
"name": "Bread Dog Bot",
"desc": "Terraria TShock QQ 机器人",
"author": "Qianyiovo",
"homepage": "https://github.com/Qianyiovo/bread_dog_bot",
"tags": [
{
"label": "TShock",
"color": "#ea5252"
},
{
"label": "泰拉瑞亚",
"color": "#5dea52"
},
{
"label": "Terraria",
"color": "#5dea52"
}
],
"is_official": false
},
{
"name": "RanBot",
"desc": "不@会很安静的Bot",
"author": "IAXRetailer",
"homepage": "https://github.com/Hecatia-Hell-Workshop/RanBot",
"tags": [],
"is_official": false
},
{
"name": "辞辞(cici)Bot",
"desc": "一个集成娱乐和群管为一体的机器人",
"author": "mengxinyuan638",
"homepage": "https://github.com/mengxinyuan638/cici-bot",
"tags": [
{
"label": "辞辞Bot",
"color": "#04de4d"
},
{
"label": "萌新源",
"color": "#fd1c06"
},
{
"label": "群管",
"color": "#06b8fd"
}
],
"is_official": false
},
{
"name": "SuzunoBot",
"desc": "多功能音游bot主要服务maimaiDX、Arcaea",
"author": "Rinfair-CSP-A016",
"homepage": "https://github.com/Rinfair-CSP-A016/SuzunoBot-AGLAS",
"tags": [
{
"label": "t:maimaiDX",
"color": "#189ede"
},
{
"label": "t:Arcaea",
"color": "#d551ef"
},
{
"label": "t:coc",
"color": "#7fe4d0"
}
],
"is_official": false
} }
] ]

View File

@@ -1,7 +1,17 @@
[ [
{ {
"module_name": "nonebot.drivers.fastapi", "module_name": "~none",
"project_link": "", "project_link": "",
"name": "None",
"desc": "None 驱动器",
"author": "yanyongyu",
"homepage": "/docs/tutorial/choose-driver",
"tags": [],
"is_official": true
},
{
"module_name": "~fastapi",
"project_link": "nonebot2[fastapi]",
"name": "FastAPI", "name": "FastAPI",
"desc": "FastAPI 驱动器", "desc": "FastAPI 驱动器",
"author": "yanyongyu", "author": "yanyongyu",
@@ -10,8 +20,8 @@
"is_official": true "is_official": true
}, },
{ {
"module_name": "nonebot.drivers.quart", "module_name": "~quart",
"project_link": "quart", "project_link": "nonebot2[quart]",
"name": "Quart", "name": "Quart",
"desc": "Quart 驱动器", "desc": "Quart 驱动器",
"author": "yanyongyu", "author": "yanyongyu",
@@ -20,8 +30,8 @@
"is_official": true "is_official": true
}, },
{ {
"module_name": "nonebot.drivers.httpx", "module_name": "~httpx",
"project_link": "httpx", "project_link": "nonebot2[httpx]",
"name": "HTTPX", "name": "HTTPX",
"desc": "HTTPX 驱动器", "desc": "HTTPX 驱动器",
"author": "yanyongyu", "author": "yanyongyu",
@@ -30,8 +40,8 @@
"is_official": true "is_official": true
}, },
{ {
"module_name": "nonebot.drivers.websockets", "module_name": "~websockets",
"project_link": "websockets", "project_link": "nonebot2[websockets]",
"name": "websockets", "name": "websockets",
"desc": "websockets 驱动器", "desc": "websockets 驱动器",
"author": "yanyongyu", "author": "yanyongyu",
@@ -40,8 +50,8 @@
"is_official": true "is_official": true
}, },
{ {
"module_name": "nonebot.drivers.aiohttp", "module_name": "~aiohttp",
"project_link": "aiohttp", "project_link": "nonebot2[aiohttp]",
"name": "AIOHTTP", "name": "AIOHTTP",
"desc": "AIOHTTP 驱动器", "desc": "AIOHTTP 驱动器",
"author": "yanyongyu", "author": "yanyongyu",

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +0,0 @@
---
sidebar_position: 8
description: 调用机器人平台 API完成更多的功能
options:
menu:
weight: 29
category: guide
---
# 调用平台 API
在使用机器人功能时,除了发送消息以外,还可能需要调用机器人平台的 API 来完成更多的功能。
NoneBot 提供了两种方式来调用机器人平台 API两种方式都需要首先获得 Bot 实例,然后调用相应的方法。
例如,如果需要调用机器人平台的 `get_user_info` API可以这样做
```python
from nonebot import get_bot
bot = get_bot("bot_id")
result = await bot.get_user_info(user_id=12345678)
await bot.call_api("get_user_info", user_id=12345678)
```
:::tip 提示
API 由平台提供,请参考平台文档。
:::

View File

@@ -20,7 +20,11 @@ options:
您可以选择自己喜欢的方式将插件发布到 [**PyPI**](https://pypi.org/),如使用 [**setuptools**](https://pypi.org/project/setuptools/) 或 [**Poetry**](https://pypi.org/project/poetry/)。 您可以选择自己喜欢的方式将插件发布到 [**PyPI**](https://pypi.org/),如使用 [**setuptools**](https://pypi.org/project/setuptools/) 或 [**Poetry**](https://pypi.org/project/poetry/)。
发布时,请您为自己的插件取一个清晰易懂的名字。通常而言,一款 NoneBot2 插件名称使用 `nonebot-plugin-` 作为前缀(如`nonebot-plugin-foo`),以 `nonebot_plugin_` 作为包名的前缀(如`nonebot_plugin_foo`),这并非强制规范,而是为了防止与其他 PyPI 包产生冲突,所以我们推荐您在没有特殊需求的情况下这样做。 发布时,请您为自己的插件取一个清晰易懂的名字。通常而言,一款 NoneBot2 插件名称使用 `nonebot-plugin-` 作为 PyPI 项目名前缀(如`nonebot-plugin-foo`),以 `nonebot_plugin_` 作为 Python 包名的前缀(如`nonebot_plugin_foo`),这并非强制规范,而是为了防止与其他 PyPI 包产生冲突,所以我们推荐您在没有特殊需求的情况下这样做。
:::warning
虽然在 NoneBot 2 载入插件时,插件的 Python 包名中可以使用 `-`,但是在 Python 的 import 语句中,`-` 不会被解析为包名的一部分。如果插件需要向外界提供 import 语法导入的支持,应在 Python 包名中使用 `_` 代替 `-`
:::
发布后,请确保您的插件已能公开的从 PyPI 访问到,试着检查您的插件在 PyPI 的地址,如 `https://pypi.org/project/<您的 NoneBot2 插件项目名>` 发布后,请确保您的插件已能公开的从 PyPI 访问到,试着检查您的插件在 PyPI 的地址,如 `https://pypi.org/project/<您的 NoneBot2 插件项目名>`
@@ -32,7 +36,7 @@ options:
### 申请发布到 NoneBot2 插件商店 ### 申请发布到 NoneBot2 插件商店
完成在 PyPI 的插件发布流程与源代码托管流程后,请您前往 [**NoneBot2 商店**](https://v2.nonebot.dev/store.html)页面,切换到**插件**页签,点击**发布插件**按钮。 完成在 PyPI 的插件发布流程与源代码托管流程后,请您前往 [**NoneBot2 商店**](https://v2.nonebot.dev/store)页面,切换到**插件**页签,点击**发布插件**按钮。
![插件发布界面](./images/plugin_store_publish.png) ![插件发布界面](./images/plugin_store_publish.png)
@@ -61,12 +65,12 @@ import 包名:您的插件通过 Python 导入时使用的包名,如 nonebot
之后NoneBot2 的维护者们将会对插件进行进一步的检查,以确保用户能够正常安装并使用该插件。 之后NoneBot2 的维护者们将会对插件进行进一步的检查,以确保用户能够正常安装并使用该插件。
完成这些步骤后,您的插件将会被合并到 [**NoneBot2 商店**](https://v2.nonebot.dev/store.html),而您也将成为 [**NoneBot2 贡献者**](https://github.com/nonebot/nonebot2/graphs/contributors)中的一员。 完成这些步骤后,您的插件将会被合并到 [**NoneBot2 商店**](https://v2.nonebot.dev/store),而您也将成为 [**NoneBot2 贡献者**](https://github.com/nonebot/nonebot2/graphs/contributors)中的一员。
## 完成 ## 完成
恭喜您,经过上述的发布流程,您的插件已经成功发布到 NoneBot2 商店了。 恭喜您,经过上述的发布流程,您的插件已经成功发布到 NoneBot2 商店了。
此时,您可以在 [**NoneBot2 商店**](https://v2.nonebot.dev/store.html)的插件页签查找到您的插件。同时,欢迎您成为 [**NoneBot2 贡献者**](https://github.com/nonebot/nonebot2/graphs/contributors) 此时,您可以在 [**NoneBot2 商店**](https://v2.nonebot.dev/store)的插件页签查找到您的插件。同时,欢迎您成为 [**NoneBot2 贡献者**](https://github.com/nonebot/nonebot2/graphs/contributors)
**Congratulations!** **Congratulations!**

View File

@@ -49,8 +49,8 @@ async def init_adapter(app: App, import_hook):
1. 反向 HTTPWebHook 1. 反向 HTTPWebHook
2. 反向 WebSocket 2. 反向 WebSocket
3. ~~正向 HTTP(尚未实现)~~ 3. 正向 HTTP
4. ~~正向 WebSocket(尚未实现)~~ 4. 正向 WebSocket
NoneBug 的 `test_server` 方法可以供我们测试反向连接方式。 NoneBug 的 `test_server` 方法可以供我们测试反向连接方式。

View File

@@ -546,9 +546,9 @@ description: nonebot.adapters 模块
- **参数** - **参数**
- `template`: 模板 - `template` (str | (~ TM)): 模板
- `factory`: 消息类型工厂,默认为 `str` - `factory` (Type[str] | Type[(~ TM)]): 消息类型工厂,默认为 `str`
### _method_ `add_format_spec(self, spec, name=None)` {#MessageTemplate-add_format_spec} ### _method_ `add_format_spec(self, spec, name=None)` {#MessageTemplate-add_format_spec}

View File

@@ -21,7 +21,7 @@ NoneBot 使用 [`pydantic`](https://pydantic-docs.helpmanual.io/) 以及 [`pytho
- **参数** - **参数**
- `_env_file` (str | os.PathLike | NoneType) - `_env_file` (str | os.PathLike | list[str | os.PathLike] | tuple[str | os.PathLike, ...] | NoneType)
- `_env_file_encoding` (str | None) - `_env_file_encoding` (str | None)
@@ -56,7 +56,7 @@ NoneBot 使用 [`pydantic`](https://pydantic-docs.helpmanual.io/) 以及 [`pytho
- **参数** - **参数**
- `_env_file` (str | os.PathLike | NoneType) - `_env_file` (str | os.PathLike | list[str | os.PathLike] | tuple[str | os.PathLike, ...] | NoneType)
- `_env_file_encoding` (str | None) - `_env_file_encoding` (str | None)
@@ -72,7 +72,7 @@ NoneBot 使用 [`pydantic`](https://pydantic-docs.helpmanual.io/) 以及 [`pytho
- `log_level` (int | str) - `log_level` (int | str)
- `api_timeout` (float) - `api_timeout` (float | None)
- `superusers` (set[str]) - `superusers` (set[str])

View File

@@ -85,6 +85,12 @@ description: nonebot.consts 模块
- **说明:** 正则匹配结果存储 key - **说明:** 正则匹配结果存储 key
## _var_ `REGEX_STR` {#REGEX_STR}
- **类型:** Literal['_matched_str']
- **说明:** 正则匹配文本存储 key
## _var_ `REGEX_GROUP` {#REGEX_GROUP} ## _var_ `REGEX_GROUP` {#REGEX_GROUP}
- **类型:** Literal['_matched_groups'] - **类型:** Literal['_matched_groups']
@@ -96,3 +102,27 @@ description: nonebot.consts 模块
- **类型:** Literal['_matched_dict'] - **类型:** Literal['_matched_dict']
- **说明:** 正则匹配 group 字典存储 key - **说明:** 正则匹配 group 字典存储 key
## _var_ `STARTSWITH_KEY` {#STARTSWITH_KEY}
- **类型:** Literal['_startswith']
- **说明:** 响应触发前缀 key
## _var_ `ENDSWITH_KEY` {#ENDSWITH_KEY}
- **类型:** Literal['_endswith']
- **说明:** 响应触发后缀 key
## _var_ `FULLMATCH_KEY` {#FULLMATCH_KEY}
- **类型:** Literal['_fullmatch']
- **说明:** 响应触发完整消息 key
## _var_ `KEYWORD_KEY` {#KEYWORD_KEY}
- **类型:** Literal['_keyword']
- **说明:** 响应触发关键字 key

View File

@@ -7,11 +7,17 @@ description: nonebot.drivers.fastapi 模块
[FastAPI](https://fastapi.tiangolo.com/) 驱动适配 [FastAPI](https://fastapi.tiangolo.com/) 驱动适配
```bash
nb driver install fastapi
# 或者
pip install nonebot2[fastapi]
```
:::tip 提示 :::tip 提示
本驱动仅支持服务端连接 本驱动仅支持服务端连接
::: :::
## _class_ `Config(_env_file='<object object>', _env_file_encoding=None, _env_nested_delimiter=None, _secrets_dir=None, *, fastapi_openapi_url=None, fastapi_docs_url=None, fastapi_redoc_url=None, fastapi_include_adapter_schema=True, fastapi_reload=False, fastapi_reload_dirs=None, fastapi_reload_delay=0.25, fastapi_reload_includes=None, fastapi_reload_excludes=None)` {#Config} ## _class_ `Config(_env_file='<object object>', _env_file_encoding=None, _env_nested_delimiter=None, _secrets_dir=None, *, fastapi_openapi_url=None, fastapi_docs_url=None, fastapi_redoc_url=None, fastapi_include_adapter_schema=True, fastapi_reload=False, fastapi_reload_dirs=None, fastapi_reload_delay=0.25, fastapi_reload_includes=None, fastapi_reload_excludes=None, fastapi_extra={})` {#Config}
- **说明** - **说明**
@@ -19,7 +25,7 @@ description: nonebot.drivers.fastapi 模块
- **参数** - **参数**
- `_env_file` (str | os.PathLike | NoneType) - `_env_file` (str | os.PathLike | list[str | os.PathLike] | tuple[str | os.PathLike, ...] | NoneType)
- `_env_file_encoding` (str | None) - `_env_file_encoding` (str | None)
@@ -27,23 +33,25 @@ description: nonebot.drivers.fastapi 模块
- `_secrets_dir` (str | os.PathLike | NoneType) - `_secrets_dir` (str | os.PathLike | NoneType)
- `fastapi_openapi_url` (str) - `fastapi_openapi_url` (str | None)
- `fastapi_docs_url` (str) - `fastapi_docs_url` (str | None)
- `fastapi_redoc_url` (str) - `fastapi_redoc_url` (str | None)
- `fastapi_include_adapter_schema` (bool) - `fastapi_include_adapter_schema` (bool)
- `fastapi_reload` (bool) - `fastapi_reload` (bool)
- `fastapi_reload_dirs` (list[str]) - `fastapi_reload_dirs` (list[str] | None)
- `fastapi_reload_delay` (float) - `fastapi_reload_delay` (float)
- `fastapi_reload_includes` (list[str]) - `fastapi_reload_includes` (list[str] | None)
- `fastapi_reload_excludes` (list[str]) - `fastapi_reload_excludes` (list[str] | None)
- `fastapi_extra` (dict[str, Any])
### _class-var_ `fastapi_openapi_url` {#Config-fastapi_openapi_url} ### _class-var_ `fastapi_openapi_url` {#Config-fastapi_openapi_url}
@@ -99,6 +107,12 @@ description: nonebot.drivers.fastapi 模块
- **说明:** 不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值 - **说明:** 不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值
### _class-var_ `fastapi_extra` {#Config-fastapi_extra}
- **类型:** dict[str, Any]
- **说明:** 传递给 `FastAPI` 的其他参数。
## _class_ `Driver(env, config)` {#Driver} ## _class_ `Driver(env, config)` {#Driver}
- **说明** - **说明**

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