mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-10-08 11:46:43 +00:00
Compare commits
146 Commits
v2.0.0-bet
...
v2.0.0-rc.
Author | SHA1 | Date | |
---|---|---|---|
|
3234871b53 | ||
|
03543f01f2 | ||
|
ba5c0303c7 | ||
|
e56fdd04ad | ||
|
9f10bb70db | ||
|
71aad502d1 | ||
|
ab85b8651e | ||
|
4fe8929441 | ||
|
5c303710f6 | ||
|
68d2ada94b | ||
|
75470fe157 | ||
|
47b3fc516a | ||
|
84c24b014f | ||
|
756cde6525 | ||
|
57ef19af94 | ||
|
5927b517e2 | ||
|
132205bfcc | ||
|
b31dfa9ab0 | ||
|
b249802c38 | ||
|
9df705aaa7 | ||
|
a0df535f0c | ||
|
31022a653d | ||
|
ba77443dde | ||
|
984f743097 | ||
|
638a9c94af | ||
|
92f1d5a4d7 | ||
|
248af2ae1a | ||
|
4c37be7312 | ||
|
2cb8eafa81 | ||
|
05bff5ec17 | ||
|
13245cb58f | ||
|
37bc7326b5 | ||
|
f6d189d8c5 | ||
|
600ef7031f | ||
|
7bedf7c8d0 | ||
|
f62ee5893c | ||
|
71234e9a68 | ||
|
3bbca0fa70 | ||
|
20f144ba93 | ||
|
4c8bc9f0cb | ||
|
064509f26b | ||
|
8c42490a7e | ||
|
179d7105c9 | ||
|
1c14e638c8 | ||
|
c6eef06b55 | ||
|
beef564a22 | ||
|
672f2ceecc | ||
|
28142402d7 | ||
|
b886329fb8 | ||
|
a0b186aff3 | ||
|
595c64e760 | ||
|
5114749073 | ||
|
af2d7b5797 | ||
|
56943c0908 | ||
|
45478deb95 | ||
|
d281ec5bf9 | ||
|
291a7cbb8b | ||
|
41259546bd | ||
|
f96038241f | ||
|
b051320d78 | ||
|
d12efac9f4 | ||
|
f87a38a30a | ||
|
bf016b3f69 | ||
|
373f5255f1 | ||
|
5a35015195 | ||
|
d3a2f1dc08 | ||
|
f1aec4eb10 | ||
|
cd30be21ba | ||
|
f150a9ee89 | ||
|
e68281f60f | ||
|
32be64485a | ||
|
c76f492305 | ||
|
29b0351644 | ||
|
ef3350fd9c | ||
|
459699de5c | ||
|
31c3eb8fd6 | ||
|
1cfdee2645 | ||
|
4e76518a58 | ||
|
b53e029df1 | ||
|
c1faf68806 | ||
|
fe64b904ff | ||
|
a621ade449 | ||
|
3fda978064 | ||
|
60ab93164c | ||
|
2b22d5abda | ||
|
e3a4834383 | ||
|
21087036af | ||
|
94d336ef4d | ||
|
07707213a5 | ||
|
7579878fb4 | ||
|
6599b6420e | ||
|
135c6e8168 | ||
|
743e7363ea | ||
|
a101428c81 | ||
|
0d2b1f693e | ||
|
e5a53dfd5c | ||
|
915c2b3e43 | ||
|
767b6a9913 | ||
|
3f8af04803 | ||
|
00af815b8a | ||
|
24df594b97 | ||
|
d6567f9288 | ||
|
4eb158245e | ||
|
ef35266d3e | ||
|
1d1beb100a | ||
|
06ab6093b7 | ||
|
c1ce7fb940 | ||
|
6e03ddbf12 | ||
|
40c8787828 | ||
|
be459e0bbb | ||
|
1056828f90 | ||
|
ef18e8943d | ||
|
92ff1df419 | ||
|
be5ac88a18 | ||
|
fac647370a | ||
|
05a3891903 | ||
|
4deae8f00c | ||
|
0f70e975b0 | ||
|
982680be91 | ||
|
96b0a863e6 | ||
|
d64bb37c6d | ||
|
51d7f1783d | ||
|
64c18379c9 | ||
|
2735f0cba9 | ||
|
660dbaf3b8 | ||
|
898c29d7ee | ||
|
cdc507bab9 | ||
|
f32bcdc1fc | ||
|
013602da21 | ||
|
4974c596ec | ||
|
0620bec51f | ||
|
8870e6a26e | ||
|
0e3ed0e7ab | ||
|
6cc3b68447 | ||
|
549a37b172 | ||
|
16394ad68b | ||
|
57e580c255 | ||
|
6c23d89494 | ||
|
675e70f579 | ||
|
7a098b96f8 | ||
|
c9794bf91d | ||
|
1766d4da69 | ||
|
6583bc8c61 | ||
|
179f16346a | ||
|
badb0c9ff4 | ||
|
ee0ea85e40 |
2
.github/actions/setup-python/action.yml
vendored
2
.github/actions/setup-python/action.yml
vendored
@@ -5,7 +5,7 @@ inputs:
|
|||||||
python-version:
|
python-version:
|
||||||
description: Python version
|
description: Python version
|
||||||
required: false
|
required: false
|
||||||
default: "3.9"
|
default: "3.10"
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
|
2
.github/workflows/codecov.yml
vendored
2
.github/workflows/codecov.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
python-version: ["3.8", "3.9", "3.10"]
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
env:
|
env:
|
||||||
|
@@ -13,20 +13,20 @@ repos:
|
|||||||
stages: [commit]
|
stages: [commit]
|
||||||
|
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 22.6.0
|
rev: 22.8.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: v2.7.1
|
rev: v3.0.0-alpha.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: prettier
|
- id: prettier
|
||||||
types_or: [javascript, jsx, ts, tsx, markdown, yaml]
|
types_or: [javascript, jsx, ts, tsx, markdown, yaml]
|
||||||
stages: [commit]
|
stages: [commit]
|
||||||
|
|
||||||
- repo: https://github.com/nonebot/nonemoji
|
- repo: https://github.com/nonebot/nonemoji
|
||||||
rev: v0.1.2
|
rev: v0.1.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: nonemoji
|
- id: nonemoji
|
||||||
stages: [prepare-commit-msg]
|
stages: [prepare-commit-msg]
|
||||||
|
44
README.md
44
README.md
@@ -21,7 +21,7 @@ _✨ 跨平台 Python 异步机器人框架 ✨_
|
|||||||
<a href="https://pypi.python.org/pypi/nonebot2">
|
<a href="https://pypi.python.org/pypi/nonebot2">
|
||||||
<img src="https://img.shields.io/pypi/v/nonebot2" alt="pypi">
|
<img src="https://img.shields.io/pypi/v/nonebot2" alt="pypi">
|
||||||
</a>
|
</a>
|
||||||
<img src="https://img.shields.io/badge/python-3.7.3+-blue" alt="python">
|
<img src="https://img.shields.io/badge/python-3.8+-blue" alt="python">
|
||||||
<a href="https://codecov.io/gh/nonebot/nonebot2">
|
<a href="https://codecov.io/gh/nonebot/nonebot2">
|
||||||
<img src="https://codecov.io/gh/nonebot/nonebot2/branch/master/graph/badge.svg?token=2P0G0VS7N4" alt="codecov"/>
|
<img src="https://codecov.io/gh/nonebot/nonebot2/branch/master/graph/badge.svg?token=2P0G0VS7N4" alt="codecov"/>
|
||||||
</a>
|
</a>
|
||||||
@@ -38,17 +38,20 @@ _✨ 跨平台 Python 异步机器人框架 ✨_
|
|||||||
<a href="https://onebot.dev/">
|
<a href="https://onebot.dev/">
|
||||||
<img src="https://img.shields.io/badge/OneBot-v12-black?style=social&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAAIVBMVEUAAAAAAAADAwMHBwceHh4UFBQNDQ0ZGRkoKCgvLy8iIiLWSdWYAAAAAXRSTlMAQObYZgAAAQVJREFUSMftlM0RgjAQhV+0ATYK6i1Xb+iMd0qgBEqgBEuwBOxU2QDKsjvojQPvkJ/ZL5sXkgWrFirK4MibYUdE3OR2nEpuKz1/q8CdNxNQgthZCXYVLjyoDQftaKuniHHWRnPh2GCUetR2/9HsMAXyUT4/3UHwtQT2AggSCGKeSAsFnxBIOuAggdh3AKTL7pDuCyABcMb0aQP7aM4AnAbc/wHwA5D2wDHTTe56gIIOUA/4YYV2e1sg713PXdZJAuncdZMAGkAukU9OAn40O849+0ornPwT93rphWF0mgAbauUrEOthlX8Zu7P5A6kZyKCJy75hhw1Mgr9RAUvX7A3csGqZegEdniCx30c3agAAAABJRU5ErkJggg==" alt="onebot">
|
<img src="https://img.shields.io/badge/OneBot-v12-black?style=social&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABABAMAAABYR2ztAAAAIVBMVEUAAAAAAAADAwMHBwceHh4UFBQNDQ0ZGRkoKCgvLy8iIiLWSdWYAAAAAXRSTlMAQObYZgAAAQVJREFUSMftlM0RgjAQhV+0ATYK6i1Xb+iMd0qgBEqgBEuwBOxU2QDKsjvojQPvkJ/ZL5sXkgWrFirK4MibYUdE3OR2nEpuKz1/q8CdNxNQgthZCXYVLjyoDQftaKuniHHWRnPh2GCUetR2/9HsMAXyUT4/3UHwtQT2AggSCGKeSAsFnxBIOuAggdh3AKTL7pDuCyABcMb0aQP7aM4AnAbc/wHwA5D2wDHTTe56gIIOUA/4YYV2e1sg713PXdZJAuncdZMAGkAukU9OAn40O849+0ornPwT93rphWF0mgAbauUrEOthlX8Zu7P5A6kZyKCJy75hhw1Mgr9RAUvX7A3csGqZegEdniCx30c3agAAAABJRU5ErkJggg==" alt="onebot">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://ding-doc.dingtalk.com/document#/org-dev-guide/elzz1p">
|
|
||||||
<img src="https://img.shields.io/badge/%E9%92%89%E9%92%89-Bot-lightgrey?style=social&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAnFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4jUzeAAAAM3RSTlMAQKSRaA+/f0YyFevh29R3cyklIfrlyrGsn41tVUs48c/HqJm9uZdhX1otGwkF9IN8V1CX0Q+IAAABY0lEQVRYw+3V2W7CMBAF0JuNQAhhX9OEfYdu9///rUVWpagE27Ef2gfO+0zGozsKnv6bMGzAhkNytIe5gDdzrwtTCwrbI8x4/NF668NAxgI3Q3UtFi3TyPwNQtPLUUmDd8YfqGLNe4v22XwEYb5zoOuF5baHq2UHtsKe5ivWfGAwrWu2mC34QM0PoCAuqZdOmiwV+5BLyMRtZ7dTSEcs48rzWfzwptMLyzpApka1SJ5FtR4kfCqNIBPEVDmqoqgwUYY5plQOlf6UEjNoOPnuKB6wzDyCrks///TDza8+PnR109WQdxLo8RKWq0PPnuXG0OXKQ6wWLFnCg75uYYbhmMIVVdQ709q33aHbGIj6Duz+2k1HQFX9VwqmY8xYsEJll2ahvhWgsjYLHFRXvIi2Qb0jzMQCzC3FAoydxCma88UCzE3JCWwkjCNYyMUCzHX4DiuTMawEwwhW6hnshPhjZzzJfAH0YacpbmRd7QAAAABJRU5ErkJggg==" alt="dingtalk">
|
|
||||||
</a>
|
|
||||||
<a href="https://core.telegram.org/bots/api">
|
<a href="https://core.telegram.org/bots/api">
|
||||||
<img src="https://img.shields.io/badge/telegram-Bot-lightgrey?style=social&logo=telegram" alt="telegram">
|
<img src="https://img.shields.io/badge/telegram-Bot-lightgrey?style=social&logo=telegram" alt="telegram">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://open.feishu.cn/document/home/index">
|
<a href="https://open.feishu.cn/document/home/index">
|
||||||
<img src="https://img.shields.io/badge/%E9%A3%9E%E4%B9%A6-Bot-lightgrey?style=social&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDQ4IDQ4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNyAyOUMyMSAyOSAyNSAyNi45MzM5IDI4IDIzLjQwNjVDMzYgMTQgNDEuNDI0MiAxNi44MTY2IDQ0IDE3Ljk5OThDMzguNSAyMC45OTk4IDQwLjUgMjkuNjIzMyAzMyAzNS45OTk4QzI4LjM4MiAzOS45MjU5IDIzLjQ5NDUgNDEuMDE0IDE5IDQxQzEyLjUyMzEgNDAuOTc5OSA2Ljg2MjI2IDM3Ljc2MzcgNCAzNS40MDYzVjE2Ljk5OTgiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNNS42NDgwOCAxNS44NjY5QzUuMDIyMzEgMTQuOTU2NyAzLjc3NzE1IDE0LjcyNjEgMi44NjY5NCAxNS4zNTE5QzEuOTU2NzMgMTUuOTc3NyAxLjcyNjE1IDE3LjIyMjggMi4zNTE5MiAxOC4xMzMxTDUuNjQ4MDggMTUuODY2OVpNMzYuMDAyMSAzNS43MzA5QzM2Ljk1OCAzNS4xNzc0IDM3LjI4NDMgMzMuOTUzOSAzNi43MzA5IDMyLjk5NzlDMzYuMTc3NCAzMi4wNDIgMzQuOTUzOSAzMS43MTU3IDMzLjk5NzkgMzIuMjY5MUwzNi4wMDIxIDM1LjczMDlaTTIuMzUxOTIgMTguMTMzMUM1LjI0MzUgMjIuMzM5IDEwLjc5OTIgMjguMTQ0IDE2Ljg4NjUgMzIuMjIzOUMxOS45MzQ1IDM0LjI2NjcgMjMuMjE3IDM1Ljk0NiAyNi40NDkgMzYuNzMyNEMyOS42OTQ2IDM3LjUyMiAzMy4wNDUxIDM3LjQ0MjggMzYuMDAyMSAzNS43MzA5TDMzLjk5NzkgMzIuMjY5MUMzMi4yMDQ5IDMzLjMwNzIgMjkuOTkyOSAzMy40NzggMjcuMzk0NyAzMi44NDU4QzI0Ljc4MyAzMi4yMTAzIDIxLjk0MDUgMzAuNzk1OCAxOS4xMTM1IDI4LjkwMTFDMTMuNDUwOCAyNS4xMDYgOC4yNTY1IDE5LjY2MSA1LjY0ODA4IDE1Ljg2NjlMMi4zNTE5MiAxOC4xMzMxWiIgZmlsbD0iIzMzMyIvPjxwYXRoIGQ9Ik0zMy41OTQ1IDE3QzMyLjgzOTggMTQuNzAyNyAzMC44NTQ5IDkuOTQwNTQgMjcuNTk0NSA3SDExLjU5NDVDMTUuMjE3MSAxMC42NzU3IDIzIDE2IDI3IDI0IiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PC9zdmc+" alt="feishu">
|
<img src="https://img.shields.io/badge/%E9%A3%9E%E4%B9%A6-Bot-lightgrey?style=social&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDQ4IDQ4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNyAyOUMyMSAyOSAyNSAyNi45MzM5IDI4IDIzLjQwNjVDMzYgMTQgNDEuNDI0MiAxNi44MTY2IDQ0IDE3Ljk5OThDMzguNSAyMC45OTk4IDQwLjUgMjkuNjIzMyAzMyAzNS45OTk4QzI4LjM4MiAzOS45MjU5IDIzLjQ5NDUgNDEuMDE0IDE5IDQxQzEyLjUyMzEgNDAuOTc5OSA2Ljg2MjI2IDM3Ljc2MzcgNCAzNS40MDYzVjE2Ljk5OTgiIHN0cm9rZT0iIzMzMyIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNNS42NDgwOCAxNS44NjY5QzUuMDIyMzEgMTQuOTU2NyAzLjc3NzE1IDE0LjcyNjEgMi44NjY5NCAxNS4zNTE5QzEuOTU2NzMgMTUuOTc3NyAxLjcyNjE1IDE3LjIyMjggMi4zNTE5MiAxOC4xMzMxTDUuNjQ4MDggMTUuODY2OVpNMzYuMDAyMSAzNS43MzA5QzM2Ljk1OCAzNS4xNzc0IDM3LjI4NDMgMzMuOTUzOSAzNi43MzA5IDMyLjk5NzlDMzYuMTc3NCAzMi4wNDIgMzQuOTUzOSAzMS43MTU3IDMzLjk5NzkgMzIuMjY5MUwzNi4wMDIxIDM1LjczMDlaTTIuMzUxOTIgMTguMTMzMUM1LjI0MzUgMjIuMzM5IDEwLjc5OTIgMjguMTQ0IDE2Ljg4NjUgMzIuMjIzOUMxOS45MzQ1IDM0LjI2NjcgMjMuMjE3IDM1Ljk0NiAyNi40NDkgMzYuNzMyNEMyOS42OTQ2IDM3LjUyMiAzMy4wNDUxIDM3LjQ0MjggMzYuMDAyMSAzNS43MzA5TDMzLjk5NzkgMzIuMjY5MUMzMi4yMDQ5IDMzLjMwNzIgMjkuOTkyOSAzMy40NzggMjcuMzk0NyAzMi44NDU4QzI0Ljc4MyAzMi4yMTAzIDIxLjk0MDUgMzAuNzk1OCAxOS4xMTM1IDI4LjkwMTFDMTMuNDUwOCAyNS4xMDYgOC4yNTY1IDE5LjY2MSA1LjY0ODA4IDE1Ljg2NjlMMi4zNTE5MiAxOC4xMzMxWiIgZmlsbD0iIzMzMyIvPjxwYXRoIGQ9Ik0zMy41OTQ1IDE3QzMyLjgzOTggMTQuNzAyNyAzMC44NTQ5IDkuOTQwNTQgMjcuNTk0NSA3SDExLjU5NDVDMTUuMjE3MSAxMC42NzU3IDIzIDE2IDI3IDI0IiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PC9zdmc+" alt="feishu">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://docs.github.com/en/developers/apps">
|
||||||
|
<img src="https://img.shields.io/badge/GitHub-Bot-181717?style=social&logo=github" alt="github"/>
|
||||||
|
</a>
|
||||||
<a href="https://bot.q.qq.com/wiki/">
|
<a href="https://bot.q.qq.com/wiki/">
|
||||||
<img src="https://img.shields.io/badge/QQ%E9%A2%91%E9%81%93-Bot-lightgrey?style=social&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMTIuODIgMTMwLjg5Ij48ZyBkYXRhLW5hbWU9IuWbvuWxgiAyIj48ZyBkYXRhLW5hbWU9IuWbvuWxgiAxIj48cGF0aCBkPSJNNTUuNjMgMTMwLjhjLTcgMC0xMy45LjA4LTIwLjg2IDAtMTkuMTUtLjI1LTMxLjcxLTExLjQtMzQuMjItMzAuMy00LjA3LTMwLjY2IDE0LjkzLTU5LjIgNDQuODMtNjYuNjQgMi0uNTEgNS4yMS0uMzEgNS4yMS0xLjYzIDAtMi4xMy4xNC0yLjEzLjE0LTUuNTcgMC0uODktMS4zLTEuNDYtMi4yMi0yLjMxLTYuNzMtNi4yMy03LjY3LTEzLjQxLTEtMjAuMTggNS40LTUuNTIgMTEuODctNS40IDE3LjgtLjU5IDYuNDkgNS4yNiA2LjMxIDEzLjA4LS44NiAyMS0uNjguNzQtMS43OCAxLjYtMS43OCAyLjY3djQuMjFjMCAxLjM1IDIuMiAxLjYyIDQuNzkgMi4zNSAzMS4wOSA4LjY1IDQ4LjE3IDM0LjEzIDQ1IDY2LjM3LTEuNzYgMTguMTUtMTQuNTYgMzAuMjMtMzIuNyAzMC42My04LjAyLjE5LTE2LjA3LS4wMS0yNC4xMy0uMDF6IiBmaWxsPSIjMDI5OWZlIi8+PHBhdGggZD0iTTMxLjQ2IDExOC4zOGMtMTAuNS0uNjktMTYuOC02Ljg2LTE4LjM4LTE3LjI3LTMtMTkuNDIgMi43OC0zNS44NiAxOC40Ni00Ny44MyAxNC4xNi0xMC44IDI5Ljg3LTEyIDQ1LjM4LTMuMTkgMTcuMjUgOS44NCAyNC41OSAyNS44MSAyNCA0NS4yOS0uNDkgMTUuOS04LjQyIDIzLjE0LTI0LjM4IDIzLjUtNi41OS4xNC0xMy4xOSAwLTE5Ljc5IDAiIGZpbGw9IiNmZWZlZmUiLz48cGF0aCBkPSJNNDYuMDUgNzkuNThjLjA5IDUgLjIzIDkuODItNyA5Ljc3LTcuODItLjA2LTYuMS01LjY5LTYuMjQtMTAuMTktLjE1LTQuODItLjczLTEwIDYuNzMtOS44NHM2LjM3IDUuNTUgNi41MSAxMC4yNnoiIGZpbGw9IiMxMDlmZmUiLz48cGF0aCBkPSJNODAuMjcgNzkuMjdjLS41MyAzLjkxIDEuNzUgOS42NC01Ljg4IDEwLTcuNDcuMzctNi44MS00LjgyLTYuNjEtOS41LjItNC4zMi0xLjgzLTEwIDUuNzgtMTAuNDJzNi41OSA0Ljg5IDYuNzEgOS45MnoiIGZpbGw9IiMwODljZmUiLz48L2c+PC9nPjwvc3ZnPg==" alt="QQ频道">
|
<img src="https://img.shields.io/badge/QQ%E9%A2%91%E9%81%93-Bot-lightgrey?style=social&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMTIuODIgMTMwLjg5Ij48ZyBkYXRhLW5hbWU9IuWbvuWxgiAyIj48ZyBkYXRhLW5hbWU9IuWbvuWxgiAxIj48cGF0aCBkPSJNNTUuNjMgMTMwLjhjLTcgMC0xMy45LjA4LTIwLjg2IDAtMTkuMTUtLjI1LTMxLjcxLTExLjQtMzQuMjItMzAuMy00LjA3LTMwLjY2IDE0LjkzLTU5LjIgNDQuODMtNjYuNjQgMi0uNTEgNS4yMS0uMzEgNS4yMS0xLjYzIDAtMi4xMy4xNC0yLjEzLjE0LTUuNTcgMC0uODktMS4zLTEuNDYtMi4yMi0yLjMxLTYuNzMtNi4yMy03LjY3LTEzLjQxLTEtMjAuMTggNS40LTUuNTIgMTEuODctNS40IDE3LjgtLjU5IDYuNDkgNS4yNiA2LjMxIDEzLjA4LS44NiAyMS0uNjguNzQtMS43OCAxLjYtMS43OCAyLjY3djQuMjFjMCAxLjM1IDIuMiAxLjYyIDQuNzkgMi4zNSAzMS4wOSA4LjY1IDQ4LjE3IDM0LjEzIDQ1IDY2LjM3LTEuNzYgMTguMTUtMTQuNTYgMzAuMjMtMzIuNyAzMC42My04LjAyLjE5LTE2LjA3LS4wMS0yNC4xMy0uMDF6IiBmaWxsPSIjMDI5OWZlIi8+PHBhdGggZD0iTTMxLjQ2IDExOC4zOGMtMTAuNS0uNjktMTYuOC02Ljg2LTE4LjM4LTE3LjI3LTMtMTkuNDIgMi43OC0zNS44NiAxOC40Ni00Ny44MyAxNC4xNi0xMC44IDI5Ljg3LTEyIDQ1LjM4LTMuMTkgMTcuMjUgOS44NCAyNC41OSAyNS44MSAyNCA0NS4yOS0uNDkgMTUuOS04LjQyIDIzLjE0LTI0LjM4IDIzLjUtNi41OS4xNC0xMy4xOSAwLTE5Ljc5IDAiIGZpbGw9IiNmZWZlZmUiLz48cGF0aCBkPSJNNDYuMDUgNzkuNThjLjA5IDUgLjIzIDkuODItNyA5Ljc3LTcuODItLjA2LTYuMS01LjY5LTYuMjQtMTAuMTktLjE1LTQuODItLjczLTEwIDYuNzMtOS44NHM2LjM3IDUuNTUgNi41MSAxMC4yNnoiIGZpbGw9IiMxMDlmZmUiLz48cGF0aCBkPSJNODAuMjcgNzkuMjdjLS41MyAzLjkxIDEuNzUgOS42NC01Ljg4IDEwLTcuNDcuMzctNi44MS00LjgyLTYuNjEtOS41LjItNC4zMi0xLjgzLTEwIDUuNzgtMTAuNDJzNi41OSA0Ljg5IDYuNzEgOS45MnoiIGZpbGw9IiMwODljZmUiLz48L2c+PC9nPjwvc3ZnPg==" alt="QQ频道">
|
||||||
|
<a href="https://ding-doc.dingtalk.com/document#/org-dev-guide/elzz1p">
|
||||||
|
<img src="https://img.shields.io/badge/%E9%92%89%E9%92%89-Bot-lightgrey?style=social&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAnFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4jUzeAAAAM3RSTlMAQKSRaA+/f0YyFevh29R3cyklIfrlyrGsn41tVUs48c/HqJm9uZdhX1otGwkF9IN8V1CX0Q+IAAABY0lEQVRYw+3V2W7CMBAF0JuNQAhhX9OEfYdu9///rUVWpagE27Ef2gfO+0zGozsKnv6bMGzAhkNytIe5gDdzrwtTCwrbI8x4/NF668NAxgI3Q3UtFi3TyPwNQtPLUUmDd8YfqGLNe4v22XwEYb5zoOuF5baHq2UHtsKe5ivWfGAwrWu2mC34QM0PoCAuqZdOmiwV+5BLyMRtZ7dTSEcs48rzWfzwptMLyzpApka1SJ5FtR4kfCqNIBPEVDmqoqgwUYY5plQOlf6UEjNoOPnuKB6wzDyCrks///TDza8+PnR109WQdxLo8RKWq0PPnuXG0OXKQ6wWLFnCg75uYYbhmMIVVdQ709q33aHbGIj6Duz+2k1HQFX9VwqmY8xYsEJll2ahvhWgsjYLHFRXvIi2Qb0jzMQCzC3FAoydxCma88UCzE3JCWwkjCNYyMUCzHX4DiuTMawEwwhW6hnshPhjZzzJfAH0YacpbmRd7QAAAABJRU5ErkJggg==" alt="dingtalk">
|
||||||
|
</a>
|
||||||
</a>
|
</a>
|
||||||
<br />
|
<br />
|
||||||
<a href="https://jq.qq.com/?_wv=1027&k=5OFifDh">
|
<a href="https://jq.qq.com/?_wv=1027&k=5OFifDh">
|
||||||
@@ -92,17 +95,28 @@ NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架
|
|||||||
- 生而可靠:100% 类型注解覆盖,配合编辑器的类型推导功能,能将绝大多数的 Bug 杜绝在编辑器中 ([编辑器支持](https://v2.nonebot.dev/docs/start/editor-support))
|
- 生而可靠:100% 类型注解覆盖,配合编辑器的类型推导功能,能将绝大多数的 Bug 杜绝在编辑器中 ([编辑器支持](https://v2.nonebot.dev/docs/start/editor-support))
|
||||||
- 社区丰富:社区用户众多,直接和间接用户超过十万人,每天都有大量的活跃用户 ([社区资源](#社区资源))
|
- 社区丰富:社区用户众多,直接和间接用户超过十万人,每天都有大量的活跃用户 ([社区资源](#社区资源))
|
||||||
- 海纳百川:一个框架,支持多个聊天软件平台,可自定义通信协议
|
- 海纳百川:一个框架,支持多个聊天软件平台,可自定义通信协议
|
||||||
- [OneBot 协议](https://onebot.dev/) (QQ 等)
|
|
||||||
- [钉钉](https://ding-doc.dingtalk.com/document#/org-dev-guide/elzz1p)
|
| 协议名称 | 状态 | 注释 |
|
||||||
- [Telegram](https://core.telegram.org/bots/api)
|
| :---------------------------------------------------: | :--: | :----------------------------------------------------------------: |
|
||||||
- [飞书](https://open.feishu.cn/document/home/index)
|
| [OneBot 协议](https://onebot.dev/) | ✅ | 支持 QQ、TG、微信公众号等[平台](https://onebot.dev/ecosystem.html) |
|
||||||
- [QQ 频道](https://bot.q.qq.com/wiki/)
|
| [Telegram](https://core.telegram.org/bots/api) | ✅ | |
|
||||||
- 坚实后盾:支持多种 web 框架,可自定义替换
|
| [飞书](https://open.feishu.cn/document/home/index) | ✅ | |
|
||||||
- [FastAPI](https://fastapi.tiangolo.com/)
|
| [GitHub](https://docs.github.com/en/developers/apps) | ✅ | GitHub APP & OAuth APP |
|
||||||
- [Quart](https://pgjones.gitlab.io/quart/) (异步 Flask)
|
| [QQ 频道](https://bot.q.qq.com/wiki/) | ✅ | 官方接口调整较多 |
|
||||||
- [aiohttp](https://docs.aiohttp.org/en/stable/)
|
| [钉钉](https://open.dingtalk.com/document/) | 🤗 | 寻找 Maintainer |
|
||||||
- [httpx](https://www.python-httpx.org/)
|
| Console | ✅ | 控制台交互 |
|
||||||
- [websockets](https://websockets.readthedocs.io/en/stable/)
|
| [开黑啦](https://developer.kookapp.cn/) | ↗️ | 由社区贡献 |
|
||||||
|
| [Mirai](https://docs.mirai.mamoe.net/mirai-api-http/) | ↗️ | 由社区贡献 |
|
||||||
|
|
||||||
|
- 坚实后盾:支持多种 web 框架,可自定义替换、组合
|
||||||
|
|
||||||
|
| 驱动框架 | 类型 |
|
||||||
|
| :--------------------------------------------------------: | :----: |
|
||||||
|
| [FastAPI](https://fastapi.tiangolo.com/) | 服务端 |
|
||||||
|
| [Quart](https://pgjones.gitlab.io/quart/) (异步 Flask) | 服务端 |
|
||||||
|
| [aiohttp](https://docs.aiohttp.org/en/stable/) | 客户端 |
|
||||||
|
| [httpx](https://www.python-httpx.org/) | 客户端 |
|
||||||
|
| [websockets](https://websockets.readthedocs.io/en/stable/) | 客户端 |
|
||||||
|
|
||||||
更多:[概览](https://v2.nonebot.dev/docs/)
|
更多:[概览](https://v2.nonebot.dev/docs/)
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
- `on_command` => {ref}``on_command` <nonebot.plugin.on.on_command>`
|
- `on_command` => {ref}``on_command` <nonebot.plugin.on.on_command>`
|
||||||
- `on_shell_command` => {ref}``on_shell_command` <nonebot.plugin.on.on_shell_command>`
|
- `on_shell_command` => {ref}``on_shell_command` <nonebot.plugin.on.on_shell_command>`
|
||||||
- `on_regex` => {ref}``on_regex` <nonebot.plugin.on.on_regex>`
|
- `on_regex` => {ref}``on_regex` <nonebot.plugin.on.on_regex>`
|
||||||
|
- `on_type` => {ref}``on_type` <nonebot.plugin.on.on_type>`
|
||||||
- `CommandGroup` => {ref}``CommandGroup` <nonebot.plugin.on.CommandGroup>`
|
- `CommandGroup` => {ref}``CommandGroup` <nonebot.plugin.on.CommandGroup>`
|
||||||
- `Matchergroup` => {ref}``MatcherGroup` <nonebot.plugin.on.MatcherGroup>`
|
- `Matchergroup` => {ref}``MatcherGroup` <nonebot.plugin.on.MatcherGroup>`
|
||||||
- `load_plugin` => {ref}``load_plugin` <nonebot.plugin.load.load_plugin>`
|
- `load_plugin` => {ref}``load_plugin` <nonebot.plugin.load.load_plugin>`
|
||||||
@@ -29,7 +30,6 @@
|
|||||||
- `get_plugin_by_module_name` => {ref}``get_plugin_by_module_name` <nonebot.plugin.get_plugin_by_module_name>`
|
- `get_plugin_by_module_name` => {ref}``get_plugin_by_module_name` <nonebot.plugin.get_plugin_by_module_name>`
|
||||||
- `get_loaded_plugins` => {ref}``get_loaded_plugins` <nonebot.plugin.get_loaded_plugins>`
|
- `get_loaded_plugins` => {ref}``get_loaded_plugins` <nonebot.plugin.get_loaded_plugins>`
|
||||||
- `get_available_plugin_names` => {ref}``get_available_plugin_names` <nonebot.plugin.get_available_plugin_names>`
|
- `get_available_plugin_names` => {ref}``get_available_plugin_names` <nonebot.plugin.get_available_plugin_names>`
|
||||||
- `export` => {ref}``export` <nonebot.plugin.export.export>`
|
|
||||||
- `require` => {ref}``require` <nonebot.plugin.load.require>`
|
- `require` => {ref}``require` <nonebot.plugin.load.require>`
|
||||||
|
|
||||||
FrontMatter:
|
FrontMatter:
|
||||||
@@ -40,10 +40,12 @@ FrontMatter:
|
|||||||
import importlib
|
import importlib
|
||||||
from typing import Any, Dict, Type, Optional
|
from typing import Any, Dict, Type, Optional
|
||||||
|
|
||||||
|
import loguru
|
||||||
|
|
||||||
|
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, default_filter
|
|
||||||
from nonebot.drivers import Driver, ReverseDriver, combine_driver
|
from nonebot.drivers import Driver, ReverseDriver, combine_driver
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -172,8 +174,7 @@ def get_bots() -> Dict[str, Bot]:
|
|||||||
bots = nonebot.get_bots()
|
bots = nonebot.get_bots()
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
driver = get_driver()
|
return get_driver().bots
|
||||||
return driver.bots
|
|
||||||
|
|
||||||
|
|
||||||
def _resolve_dot_notation(
|
def _resolve_dot_notation(
|
||||||
@@ -207,6 +208,15 @@ def _resolve_combine_expr(obj_str: str) -> Type[Driver]:
|
|||||||
return combine_driver(DriverClass, *mixins)
|
return combine_driver(DriverClass, *mixins)
|
||||||
|
|
||||||
|
|
||||||
|
def _log_patcher(record: "loguru.Record"):
|
||||||
|
record["name"] = (
|
||||||
|
plugin.name
|
||||||
|
if (module_name := record["name"])
|
||||||
|
and (plugin := get_plugin_by_module_name(module_name))
|
||||||
|
else (module_name and module_name.split(".")[0])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def init(*, _env_file: Optional[str] = None, **kwargs: Any) -> None:
|
def init(*, _env_file: Optional[str] = None, **kwargs: Any) -> None:
|
||||||
"""初始化 NoneBot 以及 全局 {ref}`nonebot.drivers.Driver` 对象。
|
"""初始化 NoneBot 以及 全局 {ref}`nonebot.drivers.Driver` 对象。
|
||||||
|
|
||||||
@@ -233,7 +243,9 @@ def init(*, _env_file: Optional[str] = None, **kwargs: Any) -> None:
|
|||||||
_env_file=_env_file or f".env.{env.environment}",
|
_env_file=_env_file or f".env.{env.environment}",
|
||||||
)
|
)
|
||||||
|
|
||||||
default_filter.level = config.log_level
|
logger.configure(
|
||||||
|
extra={"nonebot_log_level": config.log_level}, patcher=_log_patcher
|
||||||
|
)
|
||||||
logger.opt(colors=True).info(
|
logger.opt(colors=True).info(
|
||||||
f"Current <y><b>Env: {escape_tag(env.environment)}</b></y>"
|
f"Current <y><b>Env: {escape_tag(env.environment)}</b></y>"
|
||||||
)
|
)
|
||||||
@@ -241,7 +253,7 @@ def init(*, _env_file: Optional[str] = None, **kwargs: Any) -> None:
|
|||||||
f"Loaded <y><b>Config</b></y>: {escape_tag(str(config.dict()))}"
|
f"Loaded <y><b>Config</b></y>: {escape_tag(str(config.dict()))}"
|
||||||
)
|
)
|
||||||
|
|
||||||
DriverClass: Type[Driver] = _resolve_combine_expr(config.driver)
|
DriverClass = _resolve_combine_expr(config.driver)
|
||||||
_driver = DriverClass(env, config)
|
_driver = DriverClass(env, config)
|
||||||
|
|
||||||
|
|
||||||
@@ -262,7 +274,7 @@ def run(*args: Any, **kwargs: Any) -> None:
|
|||||||
|
|
||||||
|
|
||||||
from nonebot.plugin import on as on
|
from nonebot.plugin import on as on
|
||||||
from nonebot.plugin import export as export
|
from nonebot.plugin import on_type as on_type
|
||||||
from nonebot.plugin import require as require
|
from nonebot.plugin import require as require
|
||||||
from nonebot.plugin import on_regex as on_regex
|
from nonebot.plugin import on_regex as on_regex
|
||||||
from nonebot.plugin import on_notice as on_notice
|
from nonebot.plugin import on_notice as on_notice
|
||||||
|
@@ -25,7 +25,6 @@ from pydantic.env_settings import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.utils import escape_tag
|
|
||||||
|
|
||||||
|
|
||||||
class CustomEnvSettings(EnvSettingsSource):
|
class CustomEnvSettings(EnvSettingsSource):
|
||||||
@@ -85,13 +84,15 @@ class CustomEnvSettings(EnvSettingsSource):
|
|||||||
if env_file_vars:
|
if env_file_vars:
|
||||||
for env_name in env_file_vars.keys():
|
for env_name in env_file_vars.keys():
|
||||||
env_val = env_vars[env_name]
|
env_val = env_vars[env_name]
|
||||||
try:
|
if env_val and (val_striped := env_val.strip()):
|
||||||
if env_val:
|
try:
|
||||||
env_val = settings.__config__.json_loads(env_val.strip())
|
env_val = settings.__config__.json_loads(val_striped)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.opt(colors=True, exception=e).trace(
|
logger.trace(
|
||||||
f"Error while parsing JSON for {escape_tag(env_name)}. Assumed as string."
|
"Error while parsing JSON for "
|
||||||
)
|
f"{env_name!r}={val_striped!r}. "
|
||||||
|
"Assumed as string."
|
||||||
|
)
|
||||||
|
|
||||||
d[env_name] = env_val
|
d[env_name] = env_val
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ FrontMatter:
|
|||||||
sidebar_position: 9
|
sidebar_position: 9
|
||||||
description: nonebot.consts 模块
|
description: nonebot.consts 模块
|
||||||
"""
|
"""
|
||||||
from typing_extensions import Literal
|
from typing import Literal
|
||||||
|
|
||||||
# used by Matcher
|
# used by Matcher
|
||||||
RECEIVE_KEY: Literal["_receive_{id}"] = "_receive_{id}"
|
RECEIVE_KEY: Literal["_receive_{id}"] = "_receive_{id}"
|
||||||
|
@@ -6,15 +6,31 @@ FrontMatter:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import abc
|
import abc
|
||||||
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
from typing import Any, Dict, List, Type, Generic, TypeVar, Callable, Optional
|
from dataclasses import field, dataclass
|
||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Dict,
|
||||||
|
List,
|
||||||
|
Type,
|
||||||
|
Tuple,
|
||||||
|
Generic,
|
||||||
|
TypeVar,
|
||||||
|
Callable,
|
||||||
|
Iterable,
|
||||||
|
Optional,
|
||||||
|
Awaitable,
|
||||||
|
cast,
|
||||||
|
)
|
||||||
|
|
||||||
from pydantic import BaseConfig
|
from pydantic import BaseConfig
|
||||||
from pydantic.schema import get_annotation_from_field_info
|
from pydantic.schema import get_annotation_from_field_info
|
||||||
from pydantic.fields import Required, FieldInfo, Undefined, ModelField
|
from pydantic.fields import Required, FieldInfo, Undefined, ModelField
|
||||||
|
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.exception import TypeMisMatch
|
from nonebot.typing import _DependentCallable
|
||||||
|
from nonebot.exception import SkippedException
|
||||||
from nonebot.utils import run_sync, is_coroutine_callable
|
from nonebot.utils import run_sync, is_coroutine_callable
|
||||||
|
|
||||||
from .utils import check_field_type, get_typed_signature
|
from .utils import check_field_type, get_typed_signature
|
||||||
@@ -31,25 +47,29 @@ class Param(abc.ABC, FieldInfo):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_param(
|
def _check_param(
|
||||||
cls, dependent: "Dependent", name: str, param: inspect.Parameter
|
cls, param: inspect.Parameter, allow_types: Tuple[Type["Param"], ...]
|
||||||
) -> Optional["Param"]:
|
) -> Optional["Param"]:
|
||||||
return None
|
return
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_parameterless(
|
def _check_parameterless(
|
||||||
cls, dependent: "Dependent", value: Any
|
cls, value: Any, allow_types: Tuple[Type["Param"], ...]
|
||||||
) -> Optional["Param"]:
|
) -> Optional["Param"]:
|
||||||
return None
|
return
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
async def _solve(self, **kwargs: Any) -> Any:
|
async def _solve(self, **kwargs: Any) -> Any:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def _check(self, **kwargs: Any) -> None:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class CustomConfig(BaseConfig):
|
class CustomConfig(BaseConfig):
|
||||||
arbitrary_types_allowed = True
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
class Dependent(Generic[R]):
|
class Dependent(Generic[R]):
|
||||||
"""依赖注入容器
|
"""依赖注入容器
|
||||||
|
|
||||||
@@ -61,101 +81,70 @@ class Dependent(Generic[R]):
|
|||||||
allow_types: 允许的参数类型
|
allow_types: 允许的参数类型
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
call: _DependentCallable[R]
|
||||||
self,
|
params: Tuple[ModelField] = field(default_factory=tuple)
|
||||||
*,
|
parameterless: Tuple[Param] = field(default_factory=tuple)
|
||||||
call: Callable[..., Any],
|
|
||||||
pre_checkers: Optional[List[Param]] = None,
|
|
||||||
params: Optional[List[ModelField]] = None,
|
|
||||||
parameterless: Optional[List[Param]] = None,
|
|
||||||
allow_types: Optional[List[Type[Param]]] = None,
|
|
||||||
) -> None:
|
|
||||||
self.call = call
|
|
||||||
self.pre_checkers = pre_checkers or []
|
|
||||||
self.params = params or []
|
|
||||||
self.parameterless = parameterless or []
|
|
||||||
self.allow_types = allow_types or []
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
if inspect.isfunction(self.call) or inspect.isclass(self.call):
|
||||||
|
call_str = self.call.__name__
|
||||||
|
else:
|
||||||
|
call_str = repr(self.call)
|
||||||
return (
|
return (
|
||||||
f"<Dependent call={self.call}, params={self.params},"
|
f"Dependent(call={call_str}"
|
||||||
f" parameterless={self.parameterless}>"
|
+ (f", parameterless={self.parameterless}" if self.parameterless else "")
|
||||||
|
+ ")"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return self.__repr__()
|
|
||||||
|
|
||||||
async def __call__(self, **kwargs: Any) -> R:
|
async def __call__(self, **kwargs: Any) -> R:
|
||||||
|
# do pre-check
|
||||||
|
await self.check(**kwargs)
|
||||||
|
|
||||||
|
# solve param values
|
||||||
values = await self.solve(**kwargs)
|
values = await self.solve(**kwargs)
|
||||||
|
|
||||||
|
# call function
|
||||||
if is_coroutine_callable(self.call):
|
if is_coroutine_callable(self.call):
|
||||||
return await self.call(**values)
|
return await cast(Callable[..., Awaitable[R]], self.call)(**values)
|
||||||
else:
|
else:
|
||||||
return await run_sync(self.call)(**values)
|
return await run_sync(cast(Callable[..., R], self.call))(**values)
|
||||||
|
|
||||||
def parse_param(self, name: str, param: inspect.Parameter) -> Param:
|
@staticmethod
|
||||||
for allow_type in self.allow_types:
|
def parse_params(
|
||||||
field_info = allow_type._check_param(self, name, param)
|
call: _DependentCallable[R], allow_types: Tuple[Type[Param], ...]
|
||||||
if field_info:
|
) -> Tuple[ModelField]:
|
||||||
return field_info
|
fields: List[ModelField] = []
|
||||||
else:
|
params = get_typed_signature(call).parameters.values()
|
||||||
raise ValueError(
|
|
||||||
f"Unknown parameter {name} for function {self.call} with type {param.annotation}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def parse_parameterless(self, value: Any) -> Param:
|
for param in params:
|
||||||
for allow_type in self.allow_types:
|
|
||||||
field_info = allow_type._check_parameterless(self, value)
|
|
||||||
if field_info:
|
|
||||||
return field_info
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
f"Unknown parameterless {value} for function {self.call} with type {type(value)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def prepend_parameterless(self, value: Any) -> None:
|
|
||||||
self.parameterless.insert(0, self.parse_parameterless(value))
|
|
||||||
|
|
||||||
def append_parameterless(self, value: Any) -> None:
|
|
||||||
self.parameterless.append(self.parse_parameterless(value))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse(
|
|
||||||
cls: Type[T],
|
|
||||||
*,
|
|
||||||
call: Callable[..., Any],
|
|
||||||
parameterless: Optional[List[Any]] = None,
|
|
||||||
allow_types: Optional[List[Type[Param]]] = None,
|
|
||||||
) -> T:
|
|
||||||
signature = get_typed_signature(call)
|
|
||||||
params = signature.parameters
|
|
||||||
dependent = cls(
|
|
||||||
call=call,
|
|
||||||
allow_types=allow_types,
|
|
||||||
)
|
|
||||||
|
|
||||||
for param_name, param in params.items():
|
|
||||||
default_value = Required
|
default_value = Required
|
||||||
if param.default != param.empty:
|
if param.default != param.empty:
|
||||||
default_value = param.default
|
default_value = param.default
|
||||||
|
|
||||||
if isinstance(default_value, Param):
|
if isinstance(default_value, Param):
|
||||||
field_info = default_value
|
field_info = default_value
|
||||||
default_value = field_info.default
|
|
||||||
else:
|
else:
|
||||||
field_info = dependent.parse_param(param_name, param)
|
for allow_type in allow_types:
|
||||||
default_value = field_info.default
|
if field_info := allow_type._check_param(param, allow_types):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Unknown parameter {param.name} for function {call} with type {param.annotation}"
|
||||||
|
)
|
||||||
|
|
||||||
|
default_value = field_info.default
|
||||||
|
|
||||||
annotation: Any = Any
|
annotation: Any = Any
|
||||||
required = default_value == Required
|
required = default_value == Required
|
||||||
if param.annotation != param.empty:
|
if param.annotation != param.empty:
|
||||||
annotation = param.annotation
|
annotation = param.annotation
|
||||||
annotation = get_annotation_from_field_info(
|
annotation = get_annotation_from_field_info(
|
||||||
annotation, field_info, param_name
|
annotation, field_info, param.name
|
||||||
)
|
)
|
||||||
dependent.params.append(
|
|
||||||
|
fields.append(
|
||||||
ModelField(
|
ModelField(
|
||||||
name=param_name,
|
name=param.name,
|
||||||
type_=annotation,
|
type_=annotation,
|
||||||
class_validators=None,
|
class_validators=None,
|
||||||
model_config=CustomConfig,
|
model_config=CustomConfig,
|
||||||
@@ -165,49 +154,72 @@ class Dependent(Generic[R]):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
parameterless_params = [
|
return tuple(fields)
|
||||||
dependent.parse_parameterless(param) for param in (parameterless or [])
|
|
||||||
]
|
|
||||||
dependent.parameterless.extend(parameterless_params)
|
|
||||||
|
|
||||||
logger.trace(
|
@staticmethod
|
||||||
f"Parsed dependent with call={call}, "
|
def parse_parameterless(
|
||||||
f"params={[param.field_info for param in dependent.params]}, "
|
parameterless: Tuple[Any, ...], allow_types: Tuple[Type[Param], ...]
|
||||||
f"parameterless={dependent.parameterless}"
|
) -> Tuple[Param, ...]:
|
||||||
|
parameterless_params: List[Param] = []
|
||||||
|
for value in parameterless:
|
||||||
|
for allow_type in allow_types:
|
||||||
|
if param := allow_type._check_parameterless(value, allow_types):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown parameterless {value}")
|
||||||
|
parameterless_params.append(param)
|
||||||
|
return tuple(parameterless_params)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(
|
||||||
|
cls,
|
||||||
|
*,
|
||||||
|
call: _DependentCallable[R],
|
||||||
|
parameterless: Optional[Iterable[Any]] = None,
|
||||||
|
allow_types: Iterable[Type[Param]],
|
||||||
|
) -> "Dependent[R]":
|
||||||
|
allow_types = tuple(allow_types)
|
||||||
|
|
||||||
|
params = cls.parse_params(call, allow_types)
|
||||||
|
parameterless_params = (
|
||||||
|
tuple()
|
||||||
|
if parameterless is None
|
||||||
|
else cls.parse_parameterless(tuple(parameterless), allow_types)
|
||||||
)
|
)
|
||||||
|
|
||||||
return dependent
|
return cls(call, params, parameterless_params)
|
||||||
|
|
||||||
async def solve(
|
async def check(self, **params: Any) -> None:
|
||||||
self,
|
try:
|
||||||
**params: Any,
|
await asyncio.gather(
|
||||||
) -> Dict[str, Any]:
|
*(param._check(**params) for param in self.parameterless)
|
||||||
values: Dict[str, Any] = {}
|
)
|
||||||
|
await asyncio.gather(
|
||||||
|
*(
|
||||||
|
cast(Param, param.field_info)._check(**params)
|
||||||
|
for param in self.params
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except SkippedException as e:
|
||||||
|
logger.trace(f"{self} skipped due to {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
for checker in self.pre_checkers:
|
async def _solve_field(self, field: ModelField, params: Dict[str, Any]) -> Any:
|
||||||
await checker._solve(**params)
|
value = await cast(Param, field.field_info)._solve(**params)
|
||||||
|
if value is Undefined:
|
||||||
|
value = field.get_default()
|
||||||
|
return check_field_type(field, value)
|
||||||
|
|
||||||
|
async def solve(self, **params: Any) -> Dict[str, Any]:
|
||||||
|
# solve parameterless
|
||||||
for param in self.parameterless:
|
for param in self.parameterless:
|
||||||
await param._solve(**params)
|
await param._solve(**params)
|
||||||
|
|
||||||
for field in self.params:
|
# solve param values
|
||||||
field_info = field.field_info
|
values = await asyncio.gather(
|
||||||
assert isinstance(field_info, Param), "Params must be subclasses of Param"
|
*(self._solve_field(field, params) for field in self.params)
|
||||||
value = await field_info._solve(**params)
|
)
|
||||||
if value is Undefined:
|
return {field.name: value for field, value in zip(self.params, values)}
|
||||||
value = field.get_default()
|
|
||||||
|
|
||||||
try:
|
|
||||||
values[field.name] = check_field_type(field, value)
|
|
||||||
except TypeMisMatch:
|
|
||||||
logger.debug(
|
|
||||||
f"{field_info} "
|
|
||||||
f"type {type(value)} not match depends {self.call} "
|
|
||||||
f"annotation {field._type_display()}, ignored"
|
|
||||||
)
|
|
||||||
raise
|
|
||||||
|
|
||||||
return values
|
|
||||||
|
|
||||||
|
|
||||||
__autodoc__ = {"CustomConfig": False}
|
__autodoc__ = {"CustomConfig": False}
|
||||||
|
@@ -28,8 +28,7 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
|
|||||||
)
|
)
|
||||||
for param in signature.parameters.values()
|
for param in signature.parameters.values()
|
||||||
]
|
]
|
||||||
typed_signature = inspect.Signature(typed_params)
|
return inspect.Signature(typed_params)
|
||||||
return typed_signature
|
|
||||||
|
|
||||||
|
|
||||||
def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, Any]) -> Any:
|
def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str, Any]) -> Any:
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import signal
|
import signal
|
||||||
import asyncio
|
import asyncio
|
||||||
import threading
|
import threading
|
||||||
from typing import Set, Union, Callable, Awaitable
|
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.drivers import Driver
|
||||||
@@ -9,8 +9,7 @@ from nonebot.typing import overrides
|
|||||||
from nonebot.config import Env, Config
|
from nonebot.config import Env, Config
|
||||||
from nonebot.utils import run_sync, is_coroutine_callable
|
from nonebot.utils import run_sync, is_coroutine_callable
|
||||||
|
|
||||||
STARTUP_FUNC = Callable[[], Union[None, Awaitable[None]]]
|
HOOK_FUNC = Union[Callable[[], None], Callable[[], Awaitable[None]]]
|
||||||
SHUTDOWN_FUNC = Callable[[], Union[None, Awaitable[None]]]
|
|
||||||
HANDLED_SIGNALS = (
|
HANDLED_SIGNALS = (
|
||||||
signal.SIGINT, # Unix signal 2. Sent by Ctrl+C.
|
signal.SIGINT, # Unix signal 2. Sent by Ctrl+C.
|
||||||
signal.SIGTERM, # Unix signal 15. Sent by `kill <pid>`.
|
signal.SIGTERM, # Unix signal 15. Sent by `kill <pid>`.
|
||||||
@@ -20,8 +19,8 @@ HANDLED_SIGNALS = (
|
|||||||
class BlockDriver(Driver):
|
class BlockDriver(Driver):
|
||||||
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[STARTUP_FUNC] = set()
|
self.startup_funcs: Set[HOOK_FUNC] = set()
|
||||||
self.shutdown_funcs: Set[SHUTDOWN_FUNC] = set()
|
self.shutdown_funcs: Set[HOOK_FUNC] = set()
|
||||||
self.should_exit: asyncio.Event = asyncio.Event()
|
self.should_exit: asyncio.Event = asyncio.Event()
|
||||||
self.force_exit: bool = False
|
self.force_exit: bool = False
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ class BlockDriver(Driver):
|
|||||||
return logger
|
return logger
|
||||||
|
|
||||||
@overrides(Driver)
|
@overrides(Driver)
|
||||||
def on_startup(self, func: STARTUP_FUNC) -> STARTUP_FUNC:
|
def on_startup(self, func: HOOK_FUNC) -> HOOK_FUNC:
|
||||||
"""
|
"""
|
||||||
注册一个启动时执行的函数
|
注册一个启动时执行的函数
|
||||||
"""
|
"""
|
||||||
@@ -46,7 +45,7 @@ class BlockDriver(Driver):
|
|||||||
return func
|
return func
|
||||||
|
|
||||||
@overrides(Driver)
|
@overrides(Driver)
|
||||||
def on_shutdown(self, func: SHUTDOWN_FUNC) -> SHUTDOWN_FUNC:
|
def on_shutdown(self, func: HOOK_FUNC) -> HOOK_FUNC:
|
||||||
"""
|
"""
|
||||||
注册一个停止时执行的函数
|
注册一个停止时执行的函数
|
||||||
"""
|
"""
|
||||||
@@ -71,7 +70,9 @@ class BlockDriver(Driver):
|
|||||||
async def startup(self):
|
async def startup(self):
|
||||||
# run startup
|
# run startup
|
||||||
cors = [
|
cors = [
|
||||||
startup() if is_coroutine_callable(startup) else run_sync(startup)()
|
cast(Callable[..., Awaitable[None]], startup)()
|
||||||
|
if is_coroutine_callable(startup)
|
||||||
|
else run_sync(startup)()
|
||||||
for startup in self.startup_funcs
|
for startup in self.startup_funcs
|
||||||
]
|
]
|
||||||
if cors:
|
if cors:
|
||||||
@@ -94,7 +95,9 @@ class BlockDriver(Driver):
|
|||||||
logger.info("Waiting for application shutdown.")
|
logger.info("Waiting for application shutdown.")
|
||||||
# run shutdown
|
# run shutdown
|
||||||
cors = [
|
cors = [
|
||||||
shutdown() if is_coroutine_callable(shutdown) else run_sync(shutdown)()
|
cast(Callable[..., Awaitable[None]], shutdown)()
|
||||||
|
if is_coroutine_callable(shutdown)
|
||||||
|
else run_sync(shutdown)()
|
||||||
for shutdown in self.shutdown_funcs
|
for shutdown in self.shutdown_funcs
|
||||||
]
|
]
|
||||||
if cors:
|
if cors:
|
||||||
|
@@ -27,7 +27,7 @@ from nonebot.drivers import HTTPVersion, ForwardMixin, ForwardDriver, combine_dr
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
import aiohttp
|
import aiohttp
|
||||||
except ImportError:
|
except ImportError: # pragma: no cover
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
"Please install aiohttp first to use this driver. `pip install nonebot2[aiohttp]`"
|
"Please install aiohttp first to use this driver. `pip install nonebot2[aiohttp]`"
|
||||||
) from None
|
) from None
|
||||||
|
@@ -9,7 +9,9 @@ FrontMatter:
|
|||||||
description: nonebot.drivers.fastapi 模块
|
description: nonebot.drivers.fastapi 模块
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import contextlib
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any, List, Tuple, Union, Callable, Optional
|
from typing import Any, List, Tuple, Union, Callable, Optional
|
||||||
|
|
||||||
@@ -186,14 +188,12 @@ class Driver(ReverseDriver):
|
|||||||
setup: HTTPServerSetup,
|
setup: HTTPServerSetup,
|
||||||
) -> Response:
|
) -> Response:
|
||||||
json: Any = None
|
json: Any = None
|
||||||
try:
|
with contextlib.suppress(Exception):
|
||||||
json = await request.json()
|
json = await request.json()
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
data: Optional[dict] = None
|
data: Optional[dict] = None
|
||||||
files: Optional[List[Tuple[str, FileTypes]]] = None
|
files: Optional[List[Tuple[str, FileTypes]]] = None
|
||||||
try:
|
with contextlib.suppress(Exception):
|
||||||
form = await request.form()
|
form = await request.form()
|
||||||
data = {}
|
data = {}
|
||||||
files = []
|
files = []
|
||||||
@@ -204,8 +204,7 @@ class Driver(ReverseDriver):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
data[key] = value
|
data[key] = value
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
http_request = BaseRequest(
|
http_request = BaseRequest(
|
||||||
request.method,
|
request.method,
|
||||||
str(request.url),
|
str(request.url),
|
||||||
@@ -219,7 +218,9 @@ class Driver(ReverseDriver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
response = await setup.handle_func(http_request)
|
response = await setup.handle_func(http_request)
|
||||||
return Response(response.content, response.status_code, dict(response.headers))
|
return Response(
|
||||||
|
response.content, response.status_code, dict(response.headers.items())
|
||||||
|
)
|
||||||
|
|
||||||
async def _handle_ws(self, websocket: WebSocket, setup: WebSocketServerSetup):
|
async def _handle_ws(self, websocket: WebSocket, setup: WebSocketServerSetup):
|
||||||
request = BaseRequest(
|
request = BaseRequest(
|
||||||
|
@@ -31,7 +31,7 @@ from nonebot.drivers import (
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
import httpx
|
import httpx
|
||||||
except ImportError:
|
except ImportError: # pragma: no cover
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
"Please install httpx by using `pip install nonebot2[httpx]`"
|
"Please install httpx by using `pip install nonebot2[httpx]`"
|
||||||
) from None
|
) from None
|
||||||
|
@@ -37,7 +37,7 @@ try:
|
|||||||
from quart import Quart, Request, Response
|
from quart import Quart, Request, Response
|
||||||
from quart.datastructures import FileStorage
|
from quart.datastructures import FileStorage
|
||||||
from quart import Websocket as QuartWebSocket
|
from quart import Websocket as QuartWebSocket
|
||||||
except ImportError:
|
except ImportError: # pragma: no cover
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
"Please install Quart by using `pip install nonebot2[quart]`"
|
"Please install Quart by using `pip install nonebot2[quart]`"
|
||||||
) from None
|
) from None
|
||||||
|
@@ -30,10 +30,10 @@ from nonebot.drivers import ForwardMixin, ForwardDriver, combine_driver
|
|||||||
try:
|
try:
|
||||||
from websockets.exceptions import ConnectionClosed
|
from websockets.exceptions import ConnectionClosed
|
||||||
from websockets.legacy.client import Connect, WebSocketClientProtocol
|
from websockets.legacy.client import Connect, WebSocketClientProtocol
|
||||||
except ImportError:
|
except ImportError: # pragma: no cover
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
"Please install websockets by using `pip install nonebot2[websockets]`"
|
"Please install websockets by using `pip install nonebot2[websockets]`"
|
||||||
)
|
) from None
|
||||||
|
|
||||||
logger = logging.Logger("websockets.client", "INFO")
|
logger = logging.Logger("websockets.client", "INFO")
|
||||||
logger.addHandler(LoguruHandler())
|
logger.addHandler(LoguruHandler())
|
||||||
|
@@ -46,10 +46,14 @@ class ParserExit(NoneBotException):
|
|||||||
self.status = status
|
self.status = status
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return f"<ParserExit status={self.status} message={self.message}>"
|
return (
|
||||||
|
f"ParserExit(status={self.status}"
|
||||||
|
+ (f", message={self.message!r}" if self.message else "")
|
||||||
|
+ ")"
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return self.__repr__()
|
return self.__repr__()
|
||||||
|
|
||||||
|
|
||||||
@@ -68,10 +72,10 @@ class IgnoredException(ProcessException):
|
|||||||
def __init__(self, reason: Any):
|
def __init__(self, reason: Any):
|
||||||
self.reason: Any = reason
|
self.reason: Any = reason
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return f"<IgnoredException, reason={self.reason}>"
|
return f"IgnoredException(reason={self.reason!r})"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return self.__repr__()
|
return self.__repr__()
|
||||||
|
|
||||||
|
|
||||||
@@ -99,11 +103,14 @@ class TypeMisMatch(SkippedException):
|
|||||||
self.param: ModelField = param
|
self.param: ModelField = param
|
||||||
self.value: Any = value
|
self.value: Any = value
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return f"<TypeMisMatch, param={self.param}, value={self.value}>"
|
return (
|
||||||
|
f"TypeMisMatch(param={self.param.name}, "
|
||||||
|
f"type={self.param._type_display()}, value={self.value!r}>"
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
self.__repr__()
|
return self.__repr__()
|
||||||
|
|
||||||
|
|
||||||
class MockApiException(ProcessException):
|
class MockApiException(ProcessException):
|
||||||
@@ -116,10 +123,10 @@ class MockApiException(ProcessException):
|
|||||||
def __init__(self, result: Any):
|
def __init__(self, result: Any):
|
||||||
self.result = result
|
self.result = result
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return f"<ApiCancelledException, result={self.result}>"
|
return f"MockApiException(result={self.result!r})"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return self.__repr__()
|
return self.__repr__()
|
||||||
|
|
||||||
|
|
||||||
@@ -195,7 +202,8 @@ class AdapterException(NoneBotException):
|
|||||||
adapter_name: 标识 adapter
|
adapter_name: 标识 adapter
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, adapter_name: str) -> None:
|
def __init__(self, adapter_name: str, *args: object) -> None:
|
||||||
|
super().__init__(*args)
|
||||||
self.adapter_name: str = adapter_name
|
self.adapter_name: str = adapter_name
|
||||||
|
|
||||||
|
|
||||||
@@ -231,4 +239,11 @@ class WebSocketClosed(DriverException):
|
|||||||
self.reason = reason
|
self.reason = reason
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<WebSocketClosed code={self.code} reason={self.reason}>"
|
return (
|
||||||
|
f"WebSocketClosed(code={self.code}"
|
||||||
|
+ (f", reason={self.reason!r}" if self.reason else "")
|
||||||
|
+ ")"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.__repr__()
|
||||||
|
@@ -33,6 +33,9 @@ class Adapter(abc.ABC):
|
|||||||
self.bots: Dict[str, Bot] = {}
|
self.bots: Dict[str, Bot] = {}
|
||||||
"""本协议适配器已建立连接的 {ref}`nonebot.adapters.Bot` 实例"""
|
"""本协议适配器已建立连接的 {ref}`nonebot.adapters.Bot` 实例"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"Adapter(name={self.get_name()!r})"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_name(cls) -> str:
|
def get_name(cls) -> str:
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import abc
|
import abc
|
||||||
import asyncio
|
import asyncio
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing_extensions import Protocol
|
from typing import TYPE_CHECKING, Any, Set, Union, Optional, Protocol
|
||||||
from typing import TYPE_CHECKING, Any, Set, Union, Optional
|
|
||||||
|
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.config import Config
|
from nonebot.config import Config
|
||||||
@@ -14,10 +13,9 @@ if TYPE_CHECKING:
|
|||||||
from .adapter import Adapter
|
from .adapter import Adapter
|
||||||
from .message import Message, MessageSegment
|
from .message import Message, MessageSegment
|
||||||
|
|
||||||
|
class _ApiCall(Protocol):
|
||||||
class _ApiCall(Protocol):
|
async def __call__(self, **kwargs: Any) -> Any:
|
||||||
async def __call__(self, **kwargs: Any) -> Any:
|
...
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Bot(abc.ABC):
|
class Bot(abc.ABC):
|
||||||
@@ -41,7 +39,10 @@ class Bot(abc.ABC):
|
|||||||
self.self_id: str = self_id
|
self.self_id: str = self_id
|
||||||
"""机器人 ID"""
|
"""机器人 ID"""
|
||||||
|
|
||||||
def __getattr__(self, name: str) -> _ApiCall:
|
def __repr__(self) -> str:
|
||||||
|
return f"Bot(type={self.type!r}, self_id={self.self_id!r})"
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> "_ApiCall":
|
||||||
return partial(self.call_api, name)
|
return partial(self.call_api, name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -72,8 +73,7 @@ class Bot(abc.ABC):
|
|||||||
skip_calling_api: bool = False
|
skip_calling_api: bool = False
|
||||||
exception: Optional[Exception] = None
|
exception: Optional[Exception] = None
|
||||||
|
|
||||||
coros = list(map(lambda x: x(self, api, data), self._calling_api_hook))
|
if coros := [hook(self, api, data) for hook in self._calling_api_hook]:
|
||||||
if coros:
|
|
||||||
try:
|
try:
|
||||||
logger.debug("Running CallingAPI hooks...")
|
logger.debug("Running CallingAPI hooks...")
|
||||||
await asyncio.gather(*coros)
|
await asyncio.gather(*coros)
|
||||||
@@ -95,10 +95,9 @@ class Bot(abc.ABC):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
exception = e
|
exception = e
|
||||||
|
|
||||||
coros = list(
|
if coros := [
|
||||||
map(lambda x: x(self, exception, api, data, result), self._called_api_hook)
|
hook(self, exception, api, data, result) for hook in self._called_api_hook
|
||||||
)
|
]:
|
||||||
if coros:
|
|
||||||
try:
|
try:
|
||||||
logger.debug("Running CalledAPI hooks...")
|
logger.debug("Running CalledAPI hooks...")
|
||||||
await asyncio.gather(*coros)
|
await asyncio.gather(*coros)
|
||||||
|
@@ -186,7 +186,7 @@ class Message(List[TMS], abc.ABC):
|
|||||||
elif isinstance(other, Iterable):
|
elif isinstance(other, Iterable):
|
||||||
self.extend(other)
|
self.extend(other)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported type: {type(other)}") # pragma: no cover
|
raise TypeError(f"Unsupported type {type(other)!r}")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
|
@@ -56,6 +56,9 @@ class MessageTemplate(Formatter, Generic[TF]):
|
|||||||
self.factory: Type[TF] = factory
|
self.factory: Type[TF] = factory
|
||||||
self.format_specs: Dict[str, FormatSpecFunc] = {}
|
self.format_specs: Dict[str, FormatSpecFunc] = {}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"MessageTemplate({self.template!r}, factory={self.factory!r})"
|
||||||
|
|
||||||
def add_format_spec(
|
def add_format_spec(
|
||||||
self, spec: FormatSpecFunc_T, name: Optional[str] = None
|
self, spec: FormatSpecFunc_T, name: Optional[str] = None
|
||||||
) -> FormatSpecFunc_T:
|
) -> FormatSpecFunc_T:
|
||||||
|
@@ -40,12 +40,18 @@ class Driver(abc.ABC):
|
|||||||
"""环境名称"""
|
"""环境名称"""
|
||||||
self.config: Config = config
|
self.config: Config = config
|
||||||
"""全局配置对象"""
|
"""全局配置对象"""
|
||||||
self._clients: Dict[str, "Bot"] = {}
|
self._bots: Dict[str, "Bot"] = {}
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f"Driver(type={self.type!r}, "
|
||||||
|
f"adapters={len(self._adapters)}, bots={len(self._bots)})"
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bots(self) -> Dict[str, "Bot"]:
|
def bots(self) -> Dict[str, "Bot"]:
|
||||||
"""获取当前所有已连接的 Bot"""
|
"""获取当前所有已连接的 Bot"""
|
||||||
return self._clients
|
return self._bots
|
||||||
|
|
||||||
def register_adapter(self, adapter: Type["Adapter"], **kwargs) -> None:
|
def register_adapter(self, adapter: Type["Adapter"], **kwargs) -> None:
|
||||||
"""注册一个协议适配器
|
"""注册一个协议适配器
|
||||||
@@ -124,9 +130,9 @@ class Driver(abc.ABC):
|
|||||||
|
|
||||||
def _bot_connect(self, bot: "Bot") -> None:
|
def _bot_connect(self, bot: "Bot") -> None:
|
||||||
"""在连接成功后,调用该函数来注册 bot 对象"""
|
"""在连接成功后,调用该函数来注册 bot 对象"""
|
||||||
if bot.self_id in self._clients:
|
if bot.self_id in self._bots:
|
||||||
raise RuntimeError(f"Duplicate bot connection with id {bot.self_id}")
|
raise RuntimeError(f"Duplicate bot connection with id {bot.self_id}")
|
||||||
self._clients[bot.self_id] = bot
|
self._bots[bot.self_id] = bot
|
||||||
|
|
||||||
async def _run_hook(bot: "Bot") -> None:
|
async def _run_hook(bot: "Bot") -> None:
|
||||||
coros = list(
|
coros = list(
|
||||||
@@ -148,8 +154,8 @@ class Driver(abc.ABC):
|
|||||||
|
|
||||||
def _bot_disconnect(self, bot: "Bot") -> None:
|
def _bot_disconnect(self, bot: "Bot") -> None:
|
||||||
"""在连接断开后,调用该函数来注销 bot 对象"""
|
"""在连接断开后,调用该函数来注销 bot 对象"""
|
||||||
if bot.self_id in self._clients:
|
if bot.self_id in self._bots:
|
||||||
del self._clients[bot.self_id]
|
del self._bots[bot.self_id]
|
||||||
|
|
||||||
async def _run_hook(bot: "Bot") -> None:
|
async def _run_hook(bot: "Bot") -> None:
|
||||||
coros = list(
|
coros = list(
|
||||||
@@ -233,13 +239,11 @@ def combine_driver(driver: Type[Driver], *mixins: Type[ForwardMixin]) -> Type[Dr
|
|||||||
if not mixins:
|
if not mixins:
|
||||||
return driver
|
return driver
|
||||||
|
|
||||||
class CombinedDriver(*mixins, driver, ForwardDriver): # type: ignore
|
def type_(self: ForwardDriver) -> str:
|
||||||
@property
|
return (
|
||||||
def type(self) -> str:
|
driver.type.__get__(self)
|
||||||
return (
|
+ "+"
|
||||||
driver.type.__get__(self)
|
+ "+".join(map(lambda x: x.type.__get__(self), mixins))
|
||||||
+ "+"
|
)
|
||||||
+ "+".join(map(lambda x: x.type.__get__(self), mixins))
|
|
||||||
)
|
|
||||||
|
|
||||||
return CombinedDriver
|
return type("CombinedDriver", (*mixins, driver, ForwardDriver), {"type": property(type_)}) # type: ignore
|
||||||
|
@@ -131,9 +131,7 @@ class Request:
|
|||||||
self.files.append((name, file_info)) # type: ignore
|
self.files.append((name, file_info)) # type: ignore
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
class_name = self.__class__.__name__
|
return f"{self.__class__.__name__}(method={self.method!r}, url='{self.url!s}')"
|
||||||
url = str(self.url)
|
|
||||||
return f"<{class_name}({self.method!r}, {url!r})>"
|
|
||||||
|
|
||||||
|
|
||||||
class Response:
|
class Response:
|
||||||
@@ -161,12 +159,18 @@ class Response:
|
|||||||
# request
|
# request
|
||||||
self.request: Optional[Request] = request
|
self.request: Optional[Request] = request
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.__class__.__name__}(status_code={self.status_code!r})"
|
||||||
|
|
||||||
|
|
||||||
class WebSocket(abc.ABC):
|
class WebSocket(abc.ABC):
|
||||||
def __init__(self, *, request: Request):
|
def __init__(self, *, request: Request):
|
||||||
# request
|
# request
|
||||||
self.request: Request = request
|
self.request: Request = request
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self.__class__.__name__}('{self.request.url!s}')"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def closed(self) -> bool:
|
def closed(self) -> bool:
|
||||||
@@ -320,17 +324,14 @@ class Cookies(MutableMapping):
|
|||||||
return len(self.jar)
|
return len(self.jar)
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[Cookie]:
|
def __iter__(self) -> Iterator[Cookie]:
|
||||||
return (cookie for cookie in self.jar)
|
return iter(self.jar)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
cookies_repr = ", ".join(
|
cookies_repr = ", ".join(
|
||||||
[
|
f"Cookie({cookie.name}={cookie.value} for {cookie.domain})"
|
||||||
f"<Cookie {cookie.name}={cookie.value} for {cookie.domain} />"
|
for cookie in self.jar
|
||||||
for cookie in self.jar
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
return f"{self.__class__.__name__}({cookies_repr})"
|
||||||
return f"<Cookies [{cookies_repr}]>"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@@ -12,8 +12,10 @@ from typing import (
|
|||||||
Union,
|
Union,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Callable,
|
Callable,
|
||||||
|
Iterable,
|
||||||
NoReturn,
|
NoReturn,
|
||||||
Optional,
|
Optional,
|
||||||
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
@@ -34,7 +36,6 @@ from nonebot.typing import (
|
|||||||
T_PermissionUpdater,
|
T_PermissionUpdater,
|
||||||
)
|
)
|
||||||
from nonebot.exception import (
|
from nonebot.exception import (
|
||||||
TypeMisMatch,
|
|
||||||
PausedException,
|
PausedException,
|
||||||
StopPropagation,
|
StopPropagation,
|
||||||
SkippedException,
|
SkippedException,
|
||||||
@@ -43,7 +44,7 @@ from nonebot.exception import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .rule import Rule
|
from .rule import Rule
|
||||||
from .permission import USER, Permission
|
from .permission import USER, User, Permission
|
||||||
from .adapter import Bot, Event, Message, MessageSegment, MessageTemplate
|
from .adapter import Bot, Event, Message, MessageSegment, MessageTemplate
|
||||||
from .params import (
|
from .params import (
|
||||||
Depends,
|
Depends,
|
||||||
@@ -71,29 +72,16 @@ current_handler: ContextVar[Dependent] = ContextVar("current_handler")
|
|||||||
|
|
||||||
class MatcherMeta(type):
|
class MatcherMeta(type):
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
module: Optional[str]
|
|
||||||
plugin_name: Optional[str]
|
|
||||||
module_name: Optional[str]
|
module_name: Optional[str]
|
||||||
module_prefix: Optional[str]
|
|
||||||
type: str
|
type: str
|
||||||
rule: Rule
|
|
||||||
permission: Permission
|
|
||||||
handlers: List[T_Handler]
|
|
||||||
priority: int
|
|
||||||
block: bool
|
|
||||||
temp: bool
|
|
||||||
expire_time: Optional[datetime]
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f"<Matcher from {self.module_name or 'unknown'}, "
|
f"Matcher(type={self.type!r}"
|
||||||
f"type={self.type}, priority={self.priority}, "
|
+ (f", module={self.module_name}" if self.module_name else "")
|
||||||
f"temp={self.temp}>"
|
+ ")"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return repr(self)
|
|
||||||
|
|
||||||
|
|
||||||
class Matcher(metaclass=MatcherMeta):
|
class Matcher(metaclass=MatcherMeta):
|
||||||
"""事件响应器类"""
|
"""事件响应器类"""
|
||||||
@@ -132,7 +120,7 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
_default_permission_updater: Optional[Dependent[Permission]] = None
|
_default_permission_updater: Optional[Dependent[Permission]] = None
|
||||||
"""事件响应器权限更新函数"""
|
"""事件响应器权限更新函数"""
|
||||||
|
|
||||||
HANDLER_PARAM_TYPES = [
|
HANDLER_PARAM_TYPES = (
|
||||||
DependParam,
|
DependParam,
|
||||||
BotParam,
|
BotParam,
|
||||||
EventParam,
|
EventParam,
|
||||||
@@ -140,7 +128,7 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
ArgParam,
|
ArgParam,
|
||||||
MatcherParam,
|
MatcherParam,
|
||||||
DefaultParam,
|
DefaultParam,
|
||||||
]
|
)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.handlers = self.handlers.copy()
|
self.handlers = self.handlers.copy()
|
||||||
@@ -148,13 +136,11 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f"<Matcher from {self.module_name or 'unknown'}, type={self.type}, "
|
f"Matcher(type={self.type!r}"
|
||||||
f"priority={self.priority}, temp={self.temp}>"
|
+ (f", module={self.module_name}" if self.module_name else "")
|
||||||
|
+ ")"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return repr(self)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def new(
|
def new(
|
||||||
cls,
|
cls,
|
||||||
@@ -218,27 +204,35 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
"temp": temp,
|
"temp": temp,
|
||||||
"expire_time": (
|
"expire_time": (
|
||||||
expire_time
|
expire_time
|
||||||
if isinstance(expire_time, datetime)
|
and (
|
||||||
else expire_time and datetime.now() + expire_time
|
expire_time
|
||||||
|
if isinstance(expire_time, datetime)
|
||||||
|
else datetime.now() + expire_time
|
||||||
|
)
|
||||||
),
|
),
|
||||||
"priority": priority,
|
"priority": priority,
|
||||||
"block": block,
|
"block": block,
|
||||||
"_default_state": default_state or {},
|
"_default_state": default_state or {},
|
||||||
"_default_type_updater": (
|
"_default_type_updater": (
|
||||||
default_type_updater
|
default_type_updater
|
||||||
if isinstance(default_type_updater, Dependent)
|
and (
|
||||||
else default_type_updater
|
default_type_updater
|
||||||
and Dependent[str].parse(
|
if isinstance(default_type_updater, Dependent)
|
||||||
call=default_type_updater, allow_types=cls.HANDLER_PARAM_TYPES
|
else Dependent[str].parse(
|
||||||
|
call=default_type_updater,
|
||||||
|
allow_types=cls.HANDLER_PARAM_TYPES,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"_default_permission_updater": (
|
"_default_permission_updater": (
|
||||||
default_permission_updater
|
default_permission_updater
|
||||||
if isinstance(default_permission_updater, Dependent)
|
and (
|
||||||
else default_permission_updater
|
default_permission_updater
|
||||||
and Dependent[Permission].parse(
|
if isinstance(default_permission_updater, Dependent)
|
||||||
call=default_permission_updater,
|
else Dependent[Permission].parse(
|
||||||
allow_types=cls.HANDLER_PARAM_TYPES,
|
call=default_permission_updater,
|
||||||
|
allow_types=cls.HANDLER_PARAM_TYPES,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -326,7 +320,7 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def append_handler(
|
def append_handler(
|
||||||
cls, handler: T_Handler, parameterless: Optional[List[Any]] = None
|
cls, handler: T_Handler, parameterless: Optional[Iterable[Any]] = None
|
||||||
) -> Dependent[Any]:
|
) -> Dependent[Any]:
|
||||||
handler_ = Dependent[Any].parse(
|
handler_ = Dependent[Any].parse(
|
||||||
call=handler,
|
call=handler,
|
||||||
@@ -338,7 +332,7 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def handle(
|
def handle(
|
||||||
cls, parameterless: Optional[List[Any]] = None
|
cls, parameterless: Optional[Iterable[Any]] = None
|
||||||
) -> Callable[[T_Handler], T_Handler]:
|
) -> Callable[[T_Handler], T_Handler]:
|
||||||
"""装饰一个函数来向事件响应器直接添加一个处理函数
|
"""装饰一个函数来向事件响应器直接添加一个处理函数
|
||||||
|
|
||||||
@@ -354,7 +348,7 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def receive(
|
def receive(
|
||||||
cls, id: str = "", parameterless: Optional[List[Any]] = None
|
cls, id: str = "", parameterless: Optional[Iterable[Any]] = None
|
||||||
) -> Callable[[T_Handler], T_Handler]:
|
) -> Callable[[T_Handler], T_Handler]:
|
||||||
"""装饰一个函数来指示 NoneBot 在接收用户新的一条消息后继续运行该函数
|
"""装饰一个函数来指示 NoneBot 在接收用户新的一条消息后继续运行该函数
|
||||||
|
|
||||||
@@ -372,14 +366,21 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
return
|
return
|
||||||
await matcher.reject()
|
await matcher.reject()
|
||||||
|
|
||||||
_parameterless = [Depends(_receive), *(parameterless or [])]
|
_parameterless = (Depends(_receive), *(parameterless or tuple()))
|
||||||
|
|
||||||
def _decorator(func: T_Handler) -> T_Handler:
|
def _decorator(func: T_Handler) -> T_Handler:
|
||||||
|
|
||||||
if cls.handlers and cls.handlers[-1].call is func:
|
if cls.handlers and cls.handlers[-1].call is func:
|
||||||
func_handler = cls.handlers[-1]
|
func_handler = cls.handlers[-1]
|
||||||
for depend in reversed(_parameterless):
|
new_handler = Dependent(
|
||||||
func_handler.prepend_parameterless(depend)
|
call=func_handler.call,
|
||||||
|
params=func_handler.params,
|
||||||
|
parameterless=Dependent.parse_parameterless(
|
||||||
|
tuple(_parameterless), cls.HANDLER_PARAM_TYPES
|
||||||
|
)
|
||||||
|
+ func_handler.parameterless,
|
||||||
|
)
|
||||||
|
cls.handlers[-1] = new_handler
|
||||||
else:
|
else:
|
||||||
cls.append_handler(func, parameterless=_parameterless)
|
cls.append_handler(func, parameterless=_parameterless)
|
||||||
|
|
||||||
@@ -392,7 +393,7 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
cls,
|
cls,
|
||||||
key: str,
|
key: str,
|
||||||
prompt: Optional[Union[str, Message, MessageSegment, MessageTemplate]] = None,
|
prompt: Optional[Union[str, Message, MessageSegment, MessageTemplate]] = None,
|
||||||
parameterless: Optional[List[Any]] = None,
|
parameterless: Optional[Iterable[Any]] = None,
|
||||||
) -> Callable[[T_Handler], T_Handler]:
|
) -> Callable[[T_Handler], T_Handler]:
|
||||||
"""装饰一个函数来指示 NoneBot 获取一个参数 `key`
|
"""装饰一个函数来指示 NoneBot 获取一个参数 `key`
|
||||||
|
|
||||||
@@ -413,17 +414,21 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
return
|
return
|
||||||
await matcher.reject(prompt)
|
await matcher.reject(prompt)
|
||||||
|
|
||||||
_parameterless = [
|
_parameterless = (Depends(_key_getter), *(parameterless or tuple()))
|
||||||
Depends(_key_getter),
|
|
||||||
*(parameterless or []),
|
|
||||||
]
|
|
||||||
|
|
||||||
def _decorator(func: T_Handler) -> T_Handler:
|
def _decorator(func: T_Handler) -> T_Handler:
|
||||||
|
|
||||||
if cls.handlers and cls.handlers[-1].call is func:
|
if cls.handlers and cls.handlers[-1].call is func:
|
||||||
func_handler = cls.handlers[-1]
|
func_handler = cls.handlers[-1]
|
||||||
for depend in reversed(_parameterless):
|
new_handler = Dependent(
|
||||||
func_handler.prepend_parameterless(depend)
|
call=func_handler.call,
|
||||||
|
params=func_handler.params,
|
||||||
|
parameterless=Dependent.parse_parameterless(
|
||||||
|
tuple(_parameterless), cls.HANDLER_PARAM_TYPES
|
||||||
|
)
|
||||||
|
+ func_handler.parameterless,
|
||||||
|
)
|
||||||
|
cls.handlers[-1] = new_handler
|
||||||
else:
|
else:
|
||||||
cls.append_handler(func, parameterless=_parameterless)
|
cls.append_handler(func, parameterless=_parameterless)
|
||||||
|
|
||||||
@@ -551,7 +556,17 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
"""
|
"""
|
||||||
raise SkippedException
|
raise SkippedException
|
||||||
|
|
||||||
def get_receive(self, id: str, default: T = None) -> Union[Event, T]:
|
@overload
|
||||||
|
def get_receive(self, id: str) -> Union[Event, None]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def get_receive(self, id: str, default: T) -> Union[Event, T]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_receive(
|
||||||
|
self, id: str, default: Optional[T] = None
|
||||||
|
) -> Optional[Union[Event, T]]:
|
||||||
"""获取一个 `receive` 事件
|
"""获取一个 `receive` 事件
|
||||||
|
|
||||||
如果没有找到对应的事件,返回 `default` 值
|
如果没有找到对应的事件,返回 `default` 值
|
||||||
@@ -563,14 +578,34 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
self.state[RECEIVE_KEY.format(id=id)] = event
|
self.state[RECEIVE_KEY.format(id=id)] = event
|
||||||
self.state[LAST_RECEIVE_KEY] = event
|
self.state[LAST_RECEIVE_KEY] = event
|
||||||
|
|
||||||
def get_last_receive(self, default: T = None) -> Union[Event, T]:
|
@overload
|
||||||
|
def get_last_receive(self) -> Union[Event, None]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def get_last_receive(self, default: T) -> Union[Event, T]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_last_receive(
|
||||||
|
self, default: Optional[T] = None
|
||||||
|
) -> Optional[Union[Event, T]]:
|
||||||
"""获取最近一次 `receive` 事件
|
"""获取最近一次 `receive` 事件
|
||||||
|
|
||||||
如果没有事件,返回 `default` 值
|
如果没有事件,返回 `default` 值
|
||||||
"""
|
"""
|
||||||
return self.state.get(LAST_RECEIVE_KEY, default)
|
return self.state.get(LAST_RECEIVE_KEY, default)
|
||||||
|
|
||||||
def get_arg(self, key: str, default: T = None) -> Union[Message, T]:
|
@overload
|
||||||
|
def get_arg(self, key: str) -> Union[Message, None]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def get_arg(self, key: str, default: T) -> Union[Message, T]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_arg(
|
||||||
|
self, key: str, default: Optional[T] = None
|
||||||
|
) -> Optional[Union[Message, T]]:
|
||||||
"""获取一个 `got` 消息
|
"""获取一个 `got` 消息
|
||||||
|
|
||||||
如果没有找到对应的消息,返回 `default` 值
|
如果没有找到对应的消息,返回 `default` 值
|
||||||
@@ -587,7 +622,15 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
else:
|
else:
|
||||||
self.state[REJECT_TARGET] = target
|
self.state[REJECT_TARGET] = target
|
||||||
|
|
||||||
def get_target(self, default: T = None) -> Union[str, T]:
|
@overload
|
||||||
|
def get_target(self) -> Union[str, None]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def get_target(self, default: T) -> Union[str, T]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_target(self, default: Optional[T] = None) -> Optional[Union[str, T]]:
|
||||||
return self.state.get(REJECT_TARGET, default)
|
return self.state.get(REJECT_TARGET, default)
|
||||||
|
|
||||||
def stop_propagation(self):
|
def stop_propagation(self):
|
||||||
@@ -596,15 +639,21 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
|
|
||||||
async def update_type(self, bot: Bot, event: Event) -> str:
|
async def update_type(self, bot: Bot, event: Event) -> str:
|
||||||
updater = self.__class__._default_type_updater
|
updater = self.__class__._default_type_updater
|
||||||
if not updater:
|
return (
|
||||||
return "message"
|
await updater(bot=bot, event=event, state=self.state, matcher=self)
|
||||||
return await updater(bot=bot, event=event, state=self.state, matcher=self)
|
if updater
|
||||||
|
else "message"
|
||||||
|
)
|
||||||
|
|
||||||
async def update_permission(self, bot: Bot, event: Event) -> Permission:
|
async def update_permission(self, bot: Bot, event: Event) -> Permission:
|
||||||
updater = self.__class__._default_permission_updater
|
if updater := self.__class__._default_permission_updater:
|
||||||
if not updater:
|
return await updater(bot=bot, event=event, state=self.state, matcher=self)
|
||||||
return USER(event.get_session_id(), perm=self.permission)
|
permission = self.permission
|
||||||
return await updater(bot=bot, event=event, state=self.state, matcher=self)
|
if len(permission.checkers) == 1 and isinstance(
|
||||||
|
user_perm := tuple(permission.checkers)[0].call, User
|
||||||
|
):
|
||||||
|
permission = user_perm.perm
|
||||||
|
return USER(event.get_session_id(), perm=permission)
|
||||||
|
|
||||||
async def resolve_reject(self):
|
async def resolve_reject(self):
|
||||||
handler = current_handler.get()
|
handler = current_handler.get()
|
||||||
@@ -621,8 +670,8 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
dependency_cache: Optional[T_DependencyCache] = None,
|
dependency_cache: Optional[T_DependencyCache] = None,
|
||||||
):
|
):
|
||||||
logger.trace(
|
logger.trace(
|
||||||
f"Matcher {self} run with incoming args: "
|
f"{self} run with incoming args: "
|
||||||
f"bot={bot}, event={event}, state={state}"
|
f"bot={bot}, event={event!r}, state={state!r}"
|
||||||
)
|
)
|
||||||
b_t = current_bot.set(bot)
|
b_t = current_bot.set(bot)
|
||||||
e_t = current_event.set(event)
|
e_t = current_event.set(event)
|
||||||
@@ -644,17 +693,12 @@ class Matcher(metaclass=MatcherMeta):
|
|||||||
stack=stack,
|
stack=stack,
|
||||||
dependency_cache=dependency_cache,
|
dependency_cache=dependency_cache,
|
||||||
)
|
)
|
||||||
except TypeMisMatch as e:
|
except SkippedException:
|
||||||
logger.debug(
|
|
||||||
f"Handler {handler} param {e.param.name} value {e.value} "
|
|
||||||
f"mismatch type {e.param._type_display()}, skipped"
|
|
||||||
)
|
|
||||||
except SkippedException as e:
|
|
||||||
logger.debug(f"Handler {handler} skipped")
|
logger.debug(f"Handler {handler} skipped")
|
||||||
except StopPropagation:
|
except StopPropagation:
|
||||||
self.block = True
|
self.block = True
|
||||||
finally:
|
finally:
|
||||||
logger.info(f"Matcher {self} running complete")
|
logger.info(f"{self} running complete")
|
||||||
current_bot.reset(b_t)
|
current_bot.reset(b_t)
|
||||||
current_event.reset(e_t)
|
current_event.reset(e_t)
|
||||||
current_matcher.reset(m_t)
|
current_matcher.reset(m_t)
|
||||||
|
@@ -1,14 +1,10 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
import warnings
|
|
||||||
from typing_extensions import Literal
|
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Optional, cast
|
|
||||||
from contextlib import AsyncExitStack, contextmanager, asynccontextmanager
|
from contextlib import AsyncExitStack, contextmanager, asynccontextmanager
|
||||||
|
from typing import TYPE_CHECKING, Any, Type, Tuple, Literal, Callable, Optional, cast
|
||||||
|
|
||||||
from pydantic.fields import Required, Undefined, ModelField
|
from pydantic.fields import Required, Undefined, ModelField
|
||||||
|
|
||||||
from nonebot.log import logger
|
|
||||||
from nonebot.exception import TypeMisMatch
|
|
||||||
from nonebot.dependencies.utils import check_field_type
|
from nonebot.dependencies.utils import check_field_type
|
||||||
from nonebot.dependencies import Param, Dependent, CustomConfig
|
from nonebot.dependencies import Param, Dependent, CustomConfig
|
||||||
from nonebot.typing import T_State, T_Handler, T_DependencyCache
|
from nonebot.typing import T_State, T_Handler, T_DependencyCache
|
||||||
@@ -40,7 +36,7 @@ class DependsInner:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
dep = get_name(self.dependency)
|
dep = get_name(self.dependency)
|
||||||
cache = "" if self.use_cache else ", use_cache=False"
|
cache = "" if self.use_cache else ", use_cache=False"
|
||||||
return f"{self.__class__.__name__}({dep}{cache})"
|
return f"DependsInner({dep}{cache})"
|
||||||
|
|
||||||
|
|
||||||
def Depends(
|
def Depends(
|
||||||
@@ -75,12 +71,12 @@ def Depends(
|
|||||||
class DependParam(Param):
|
class DependParam(Param):
|
||||||
"""子依赖参数"""
|
"""子依赖参数"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"Depends({self.extra['dependent']})"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_param(
|
def _check_param(
|
||||||
cls,
|
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
|
||||||
dependent: Dependent,
|
|
||||||
name: str,
|
|
||||||
param: inspect.Parameter,
|
|
||||||
) -> Optional["DependParam"]:
|
) -> Optional["DependParam"]:
|
||||||
if isinstance(param.default, DependsInner):
|
if isinstance(param.default, DependsInner):
|
||||||
dependency: T_Handler
|
dependency: T_Handler
|
||||||
@@ -91,22 +87,20 @@ class DependParam(Param):
|
|||||||
dependency = param.default.dependency
|
dependency = param.default.dependency
|
||||||
sub_dependent = Dependent[Any].parse(
|
sub_dependent = Dependent[Any].parse(
|
||||||
call=dependency,
|
call=dependency,
|
||||||
allow_types=dependent.allow_types,
|
allow_types=allow_types,
|
||||||
)
|
)
|
||||||
dependent.pre_checkers.extend(sub_dependent.pre_checkers)
|
|
||||||
sub_dependent.pre_checkers.clear()
|
|
||||||
return cls(
|
return cls(
|
||||||
Required, use_cache=param.default.use_cache, dependent=sub_dependent
|
Required, use_cache=param.default.use_cache, dependent=sub_dependent
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_parameterless(
|
def _check_parameterless(
|
||||||
cls, dependent: "Dependent", value: Any
|
cls, value: Any, allow_types: Tuple[Type[Param], ...]
|
||||||
) -> Optional["Param"]:
|
) -> Optional["Param"]:
|
||||||
if isinstance(value, DependsInner):
|
if isinstance(value, DependsInner):
|
||||||
assert value.dependency, "Dependency cannot be empty"
|
assert value.dependency, "Dependency cannot be empty"
|
||||||
dependent = Dependent[Any].parse(
|
dependent = Dependent[Any].parse(
|
||||||
call=value.dependency, allow_types=dependent.allow_types
|
call=value.dependency, allow_types=allow_types
|
||||||
)
|
)
|
||||||
return cls(Required, use_cache=value.use_cache, dependent=dependent)
|
return cls(Required, use_cache=value.use_cache, dependent=dependent)
|
||||||
|
|
||||||
@@ -120,8 +114,7 @@ class DependParam(Param):
|
|||||||
dependency_cache = {} if dependency_cache is None else dependency_cache
|
dependency_cache = {} if dependency_cache is None else dependency_cache
|
||||||
|
|
||||||
sub_dependent: Dependent = self.extra["dependent"]
|
sub_dependent: Dependent = self.extra["dependent"]
|
||||||
sub_dependent.call = cast(Callable[..., Any], sub_dependent.call)
|
call = cast(Callable[..., Any], sub_dependent.call)
|
||||||
call = sub_dependent.call
|
|
||||||
|
|
||||||
# solve sub dependency with current cache
|
# solve sub dependency with current cache
|
||||||
sub_values = await sub_dependent.solve(
|
sub_values = await sub_dependent.solve(
|
||||||
@@ -133,7 +126,7 @@ class DependParam(Param):
|
|||||||
# run dependency function
|
# run dependency function
|
||||||
task: asyncio.Task[Any]
|
task: asyncio.Task[Any]
|
||||||
if use_cache and call in dependency_cache:
|
if use_cache and call in dependency_cache:
|
||||||
solved = await dependency_cache[call]
|
return await dependency_cache[call]
|
||||||
elif is_gen_callable(call) or is_async_gen_callable(call):
|
elif is_gen_callable(call) or is_async_gen_callable(call):
|
||||||
assert isinstance(
|
assert isinstance(
|
||||||
stack, AsyncExitStack
|
stack, AsyncExitStack
|
||||||
@@ -144,134 +137,124 @@ class DependParam(Param):
|
|||||||
cm = asynccontextmanager(call)(**sub_values)
|
cm = asynccontextmanager(call)(**sub_values)
|
||||||
task = asyncio.create_task(stack.enter_async_context(cm))
|
task = asyncio.create_task(stack.enter_async_context(cm))
|
||||||
dependency_cache[call] = task
|
dependency_cache[call] = task
|
||||||
solved = await task
|
return await task
|
||||||
elif is_coroutine_callable(call):
|
elif is_coroutine_callable(call):
|
||||||
task = asyncio.create_task(call(**sub_values))
|
task = asyncio.create_task(call(**sub_values))
|
||||||
dependency_cache[call] = task
|
dependency_cache[call] = task
|
||||||
solved = await task
|
return await task
|
||||||
else:
|
else:
|
||||||
task = asyncio.create_task(run_sync(call)(**sub_values))
|
task = asyncio.create_task(run_sync(call)(**sub_values))
|
||||||
dependency_cache[call] = task
|
dependency_cache[call] = task
|
||||||
solved = await task
|
return await task
|
||||||
|
|
||||||
return solved
|
async def _check(self, **kwargs: Any) -> None:
|
||||||
|
# run sub dependent pre-checkers
|
||||||
|
sub_dependent: Dependent = self.extra["dependent"]
|
||||||
class _BotChecker(Param):
|
await sub_dependent.check(**kwargs)
|
||||||
async def _solve(self, bot: "Bot", **kwargs: Any) -> Any:
|
|
||||||
field: ModelField = self.extra["field"]
|
|
||||||
try:
|
|
||||||
return check_field_type(field, bot)
|
|
||||||
except TypeMisMatch:
|
|
||||||
logger.debug(
|
|
||||||
f"Bot type {type(bot)} not match "
|
|
||||||
f"annotation {field._type_display()}, ignored"
|
|
||||||
)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
class BotParam(Param):
|
class BotParam(Param):
|
||||||
"""{ref}`nonebot.adapters.Bot` 参数"""
|
"""{ref}`nonebot.adapters.Bot` 参数"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
"BotParam("
|
||||||
|
+ (
|
||||||
|
repr(cast(ModelField, checker).type_)
|
||||||
|
if (checker := self.extra.get("checker"))
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
+ ")"
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_param(
|
def _check_param(
|
||||||
cls, dependent: Dependent, name: str, param: inspect.Parameter
|
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
|
||||||
) -> Optional["BotParam"]:
|
) -> Optional["BotParam"]:
|
||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
|
|
||||||
if param.default == param.empty:
|
if param.default == param.empty:
|
||||||
if generic_check_issubclass(param.annotation, Bot):
|
if generic_check_issubclass(param.annotation, Bot):
|
||||||
|
checker: Optional[ModelField] = None
|
||||||
if param.annotation is not Bot:
|
if param.annotation is not Bot:
|
||||||
dependent.pre_checkers.append(
|
checker = ModelField(
|
||||||
_BotChecker(
|
name=param.name,
|
||||||
Required,
|
type_=param.annotation,
|
||||||
field=ModelField(
|
class_validators=None,
|
||||||
name=name,
|
model_config=CustomConfig,
|
||||||
type_=param.annotation,
|
default=None,
|
||||||
class_validators=None,
|
required=True,
|
||||||
model_config=CustomConfig,
|
|
||||||
default=None,
|
|
||||||
required=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return cls(Required)
|
return cls(Required, checker=checker)
|
||||||
elif param.annotation == param.empty and name == "bot":
|
elif param.annotation == param.empty and param.name == "bot":
|
||||||
return cls(Required)
|
return cls(Required)
|
||||||
|
|
||||||
async def _solve(self, bot: "Bot", **kwargs: Any) -> Any:
|
async def _solve(self, bot: "Bot", **kwargs: Any) -> Any:
|
||||||
return bot
|
return bot
|
||||||
|
|
||||||
|
async def _check(self, bot: "Bot", **kwargs: Any) -> None:
|
||||||
class _EventChecker(Param):
|
if checker := self.extra.get("checker"):
|
||||||
async def _solve(self, event: "Event", **kwargs: Any) -> Any:
|
check_field_type(checker, bot)
|
||||||
field: ModelField = self.extra["field"]
|
|
||||||
try:
|
|
||||||
return check_field_type(field, event)
|
|
||||||
except TypeMisMatch:
|
|
||||||
logger.debug(
|
|
||||||
f"Event type {type(event)} not match "
|
|
||||||
f"annotation {field._type_display()}, ignored"
|
|
||||||
)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
class EventParam(Param):
|
class EventParam(Param):
|
||||||
"""{ref}`nonebot.adapters.Event` 参数"""
|
"""{ref}`nonebot.adapters.Event` 参数"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
"EventParam("
|
||||||
|
+ (
|
||||||
|
repr(cast(ModelField, checker).type_)
|
||||||
|
if (checker := self.extra.get("checker"))
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
+ ")"
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_param(
|
def _check_param(
|
||||||
cls, dependent: Dependent, name: str, param: inspect.Parameter
|
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
|
||||||
) -> Optional["EventParam"]:
|
) -> Optional["EventParam"]:
|
||||||
from nonebot.adapters import Event
|
from nonebot.adapters import Event
|
||||||
|
|
||||||
if param.default == param.empty:
|
if param.default == param.empty:
|
||||||
if generic_check_issubclass(param.annotation, Event):
|
if generic_check_issubclass(param.annotation, Event):
|
||||||
|
checker: Optional[ModelField] = None
|
||||||
if param.annotation is not Event:
|
if param.annotation is not Event:
|
||||||
dependent.pre_checkers.append(
|
checker = ModelField(
|
||||||
_EventChecker(
|
name=param.name,
|
||||||
Required,
|
type_=param.annotation,
|
||||||
field=ModelField(
|
class_validators=None,
|
||||||
name=name,
|
model_config=CustomConfig,
|
||||||
type_=param.annotation,
|
default=None,
|
||||||
class_validators=None,
|
required=True,
|
||||||
model_config=CustomConfig,
|
|
||||||
default=None,
|
|
||||||
required=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return cls(Required)
|
return cls(Required, checker=checker)
|
||||||
elif param.annotation == param.empty and name == "event":
|
elif param.annotation == param.empty and param.name == "event":
|
||||||
return cls(Required)
|
return cls(Required)
|
||||||
|
|
||||||
async def _solve(self, event: "Event", **kwargs: Any) -> Any:
|
async def _solve(self, event: "Event", **kwargs: Any) -> Any:
|
||||||
return event
|
return event
|
||||||
|
|
||||||
|
async def _check(self, event: "Event", **kwargs: Any) -> Any:
|
||||||
class StateInner(T_State):
|
if checker := self.extra.get("checker", None):
|
||||||
...
|
check_field_type(checker, event)
|
||||||
|
|
||||||
|
|
||||||
def State() -> T_State:
|
|
||||||
"""**Deprecated**: 事件处理状态参数,请直接使用 {ref}`nonebot.typing.T_State`"""
|
|
||||||
warnings.warn("State() is deprecated, use `T_State` instead", DeprecationWarning)
|
|
||||||
return StateInner()
|
|
||||||
|
|
||||||
|
|
||||||
class StateParam(Param):
|
class StateParam(Param):
|
||||||
"""事件处理状态参数"""
|
"""事件处理状态参数"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "StateParam()"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_param(
|
def _check_param(
|
||||||
cls, dependent: Dependent, name: str, param: inspect.Parameter
|
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
|
||||||
) -> Optional["StateParam"]:
|
) -> Optional["StateParam"]:
|
||||||
if isinstance(param.default, StateInner):
|
if param.default == param.empty:
|
||||||
return cls(Required)
|
|
||||||
elif param.default == param.empty:
|
|
||||||
if param.annotation is T_State:
|
if param.annotation is T_State:
|
||||||
return cls(Required)
|
return cls(Required)
|
||||||
elif param.annotation == param.empty and name == "state":
|
elif param.annotation == param.empty and param.name == "state":
|
||||||
return cls(Required)
|
return cls(Required)
|
||||||
|
|
||||||
async def _solve(self, state: T_State, **kwargs: Any) -> Any:
|
async def _solve(self, state: T_State, **kwargs: Any) -> Any:
|
||||||
@@ -281,14 +264,17 @@ class StateParam(Param):
|
|||||||
class MatcherParam(Param):
|
class MatcherParam(Param):
|
||||||
"""事件响应器实例参数"""
|
"""事件响应器实例参数"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "MatcherParam()"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_param(
|
def _check_param(
|
||||||
cls, dependent: Dependent, name: str, param: inspect.Parameter
|
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
|
||||||
) -> Optional["MatcherParam"]:
|
) -> Optional["MatcherParam"]:
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
|
|
||||||
if generic_check_issubclass(param.annotation, Matcher) or (
|
if generic_check_issubclass(param.annotation, Matcher) or (
|
||||||
param.annotation == param.empty and name == "matcher"
|
param.annotation == param.empty and param.name == "matcher"
|
||||||
):
|
):
|
||||||
return cls(Required)
|
return cls(Required)
|
||||||
|
|
||||||
@@ -303,6 +289,9 @@ class ArgInner:
|
|||||||
self.key = key
|
self.key = key
|
||||||
self.type = type
|
self.type = type
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"ArgInner(key={self.key!r}, type={self.type!r})"
|
||||||
|
|
||||||
|
|
||||||
def Arg(key: Optional[str] = None) -> Any:
|
def Arg(key: Optional[str] = None) -> Any:
|
||||||
"""`got` 的 Arg 参数消息"""
|
"""`got` 的 Arg 参数消息"""
|
||||||
@@ -322,12 +311,17 @@ def ArgPlainText(key: Optional[str] = None) -> str:
|
|||||||
class ArgParam(Param):
|
class ArgParam(Param):
|
||||||
"""`got` 的 Arg 参数"""
|
"""`got` 的 Arg 参数"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"ArgParam(key={self.extra['key']!r}, type={self.extra['type']!r})"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_param(
|
def _check_param(
|
||||||
cls, dependent: Dependent, name: str, param: inspect.Parameter
|
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
|
||||||
) -> Optional["ArgParam"]:
|
) -> Optional["ArgParam"]:
|
||||||
if isinstance(param.default, ArgInner):
|
if isinstance(param.default, ArgInner):
|
||||||
return cls(Required, key=param.default.key or name, type=param.default.type)
|
return cls(
|
||||||
|
Required, key=param.default.key or param.name, type=param.default.type
|
||||||
|
)
|
||||||
|
|
||||||
async def _solve(self, matcher: "Matcher", **kwargs: Any) -> Any:
|
async def _solve(self, matcher: "Matcher", **kwargs: Any) -> Any:
|
||||||
message = matcher.get_arg(self.extra["key"])
|
message = matcher.get_arg(self.extra["key"])
|
||||||
@@ -344,12 +338,15 @@ class ArgParam(Param):
|
|||||||
class ExceptionParam(Param):
|
class ExceptionParam(Param):
|
||||||
"""`run_postprocessor` 的异常参数"""
|
"""`run_postprocessor` 的异常参数"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "ExceptionParam()"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_param(
|
def _check_param(
|
||||||
cls, dependent: Dependent, name: str, param: inspect.Parameter
|
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
|
||||||
) -> Optional["ExceptionParam"]:
|
) -> Optional["ExceptionParam"]:
|
||||||
if generic_check_issubclass(param.annotation, Exception) or (
|
if generic_check_issubclass(param.annotation, Exception) or (
|
||||||
param.annotation == param.empty and name == "exception"
|
param.annotation == param.empty and param.name == "exception"
|
||||||
):
|
):
|
||||||
return cls(Required)
|
return cls(Required)
|
||||||
|
|
||||||
@@ -360,9 +357,12 @@ class ExceptionParam(Param):
|
|||||||
class DefaultParam(Param):
|
class DefaultParam(Param):
|
||||||
"""默认值参数"""
|
"""默认值参数"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"DefaultParam(default={self.default!r})"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_param(
|
def _check_param(
|
||||||
cls, dependent: Dependent, name: str, param: inspect.Parameter
|
cls, param: inspect.Parameter, allow_types: Tuple[Type[Param], ...]
|
||||||
) -> Optional["DefaultParam"]:
|
) -> Optional["DefaultParam"]:
|
||||||
if param.default != param.empty:
|
if param.default != param.empty:
|
||||||
return cls(param.default)
|
return cls(param.default)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from contextlib import AsyncExitStack
|
from contextlib import AsyncExitStack
|
||||||
from typing import Any, Set, Tuple, Union, NoReturn, Optional, Coroutine
|
from typing import Set, Tuple, Union, NoReturn, Optional
|
||||||
|
|
||||||
from nonebot.dependencies import Dependent
|
from nonebot.dependencies import Dependent
|
||||||
from nonebot.utils import run_coro_with_catch
|
from nonebot.utils import run_coro_with_catch
|
||||||
@@ -37,16 +37,19 @@ class Permission:
|
|||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, *checkers: Union[T_PermissionChecker, Dependent[bool]]) -> None:
|
def __init__(self, *checkers: Union[T_PermissionChecker, Dependent[bool]]) -> None:
|
||||||
self.checkers: Set[Dependent[bool]] = set(
|
self.checkers: Set[Dependent[bool]] = {
|
||||||
checker
|
checker
|
||||||
if isinstance(checker, Dependent)
|
if isinstance(checker, Dependent)
|
||||||
else Dependent[bool].parse(
|
else Dependent[bool].parse(
|
||||||
call=checker, allow_types=self.HANDLER_PARAM_TYPES
|
call=checker, allow_types=self.HANDLER_PARAM_TYPES
|
||||||
)
|
)
|
||||||
for checker in checkers
|
for checker in checkers
|
||||||
)
|
}
|
||||||
"""存储 `PermissionChecker`"""
|
"""存储 `PermissionChecker`"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"Permission({', '.join(repr(checker) for checker in self.checkers)})"
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
bot: Bot,
|
bot: Bot,
|
||||||
@@ -121,10 +124,20 @@ class User:
|
|||||||
self.users = users
|
self.users = users
|
||||||
self.perm = perm
|
self.perm = perm
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return (
|
||||||
|
f"User(users={self.users}"
|
||||||
|
+ (f", permission={self.perm})" if self.perm else "")
|
||||||
|
+ ")"
|
||||||
|
)
|
||||||
|
|
||||||
async def __call__(self, bot: Bot, event: Event) -> bool:
|
async def __call__(self, bot: Bot, event: Event) -> bool:
|
||||||
|
try:
|
||||||
|
session = event.get_session_id()
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
return bool(
|
return bool(
|
||||||
event.get_session_id() in self.users
|
session in self.users and (self.perm is None or await self.perm(bot, event))
|
||||||
and (self.perm is None or await self.perm(bot, event))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -37,16 +37,19 @@ class Rule:
|
|||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, *checkers: Union[T_RuleChecker, Dependent[bool]]) -> None:
|
def __init__(self, *checkers: Union[T_RuleChecker, Dependent[bool]]) -> None:
|
||||||
self.checkers: Set[Dependent[bool]] = set(
|
self.checkers: Set[Dependent[bool]] = {
|
||||||
checker
|
checker
|
||||||
if isinstance(checker, Dependent)
|
if isinstance(checker, Dependent)
|
||||||
else Dependent[bool].parse(
|
else Dependent[bool].parse(
|
||||||
call=checker, allow_types=self.HANDLER_PARAM_TYPES
|
call=checker, allow_types=self.HANDLER_PARAM_TYPES
|
||||||
)
|
)
|
||||||
for checker in checkers
|
for checker in checkers
|
||||||
)
|
}
|
||||||
"""存储 `RuleChecker`"""
|
"""存储 `RuleChecker`"""
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"Rule({', '.join(repr(checker) for checker in self.checkers)})"
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
bot: Bot,
|
bot: Bot,
|
||||||
|
@@ -14,16 +14,14 @@ FrontMatter:
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Union
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import loguru
|
import loguru
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
# avoid sphinx autodoc resolve annotation failed
|
# avoid sphinx autodoc resolve annotation failed
|
||||||
# because loguru module do not have `Logger` class actually
|
# because loguru module do not have `Logger` class actually
|
||||||
from loguru import Logger
|
from loguru import Logger, Record
|
||||||
|
|
||||||
from nonebot.plugin import Plugin
|
|
||||||
|
|
||||||
# logger = logging.getLogger("nonebot")
|
# logger = logging.getLogger("nonebot")
|
||||||
logger: "Logger" = loguru.logger
|
logger: "Logger" = loguru.logger
|
||||||
@@ -47,26 +45,10 @@ logger: "Logger" = loguru.logger
|
|||||||
# logger.addHandler(default_handler)
|
# logger.addHandler(default_handler)
|
||||||
|
|
||||||
|
|
||||||
class Filter:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.level: Union[int, str] = "INFO"
|
|
||||||
|
|
||||||
def __call__(self, record):
|
|
||||||
module_name: str = record["name"]
|
|
||||||
# TODO: get plugin name instead of module name
|
|
||||||
# module = sys.modules.get(module_name)
|
|
||||||
# if module and hasattr(module, "__plugin__"):
|
|
||||||
# plugin: "Plugin" = getattr(module, "__plugin__")
|
|
||||||
# module_name = plugin.module_name
|
|
||||||
record["name"] = module_name.split(".")[0]
|
|
||||||
levelno = (
|
|
||||||
logger.level(self.level).no if isinstance(self.level, str) else self.level
|
|
||||||
)
|
|
||||||
return record["level"].no >= levelno
|
|
||||||
|
|
||||||
|
|
||||||
class LoguruHandler(logging.Handler): # pragma: no cover
|
class LoguruHandler(logging.Handler): # pragma: no cover
|
||||||
def emit(self, record):
|
"""logging 与 loguru 之间的桥梁,将 logging 的日志转发到 loguru。"""
|
||||||
|
|
||||||
|
def emit(self, record: logging.LogRecord):
|
||||||
try:
|
try:
|
||||||
level = logger.level(record.levelname).name
|
level = logger.level(record.levelname).name
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@@ -82,9 +64,13 @@ class LoguruHandler(logging.Handler): # pragma: no cover
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
logger.remove()
|
def default_filter(record: "Record"):
|
||||||
default_filter: Filter = Filter()
|
"""默认的日志过滤器,根据 `config.log_level` 配置改变日志等级。"""
|
||||||
"""默认日志等级过滤器"""
|
log_level = record["extra"].get("nonebot_log_level", "INFO")
|
||||||
|
levelno = logger.level(log_level).no if isinstance(log_level, str) else log_level
|
||||||
|
return record["level"].no >= levelno
|
||||||
|
|
||||||
|
|
||||||
default_format: str = (
|
default_format: str = (
|
||||||
"<g>{time:MM-DD HH:mm:ss}</g> "
|
"<g>{time:MM-DD HH:mm:ss}</g> "
|
||||||
"[<lvl>{level}</lvl>] "
|
"[<lvl>{level}</lvl>] "
|
||||||
@@ -93,6 +79,8 @@ default_format: str = (
|
|||||||
"{message}"
|
"{message}"
|
||||||
)
|
)
|
||||||
"""默认日志格式"""
|
"""默认日志格式"""
|
||||||
|
|
||||||
|
logger.remove()
|
||||||
logger_id = logger.add(
|
logger_id = logger.add(
|
||||||
sys.stdout,
|
sys.stdout,
|
||||||
level=0,
|
level=0,
|
||||||
@@ -101,4 +89,4 @@ logger_id = logger.add(
|
|||||||
format=default_format,
|
format=default_format,
|
||||||
)
|
)
|
||||||
|
|
||||||
__autodoc__ = {"Filter": False, "LoguruHandler": False}
|
__autodoc__ = {"logger_id": False}
|
||||||
|
@@ -8,9 +8,10 @@ FrontMatter:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import contextlib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from contextlib import AsyncExitStack
|
from contextlib import AsyncExitStack
|
||||||
from typing import TYPE_CHECKING, Any, Set, Dict, Type, Optional, Coroutine
|
from typing import TYPE_CHECKING, Any, Set, Dict, Type, Optional
|
||||||
|
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.rule import TrieRule
|
from nonebot.rule import TrieRule
|
||||||
@@ -50,14 +51,14 @@ _event_postprocessors: Set[Dependent[Any]] = set()
|
|||||||
_run_preprocessors: Set[Dependent[Any]] = set()
|
_run_preprocessors: Set[Dependent[Any]] = set()
|
||||||
_run_postprocessors: Set[Dependent[Any]] = set()
|
_run_postprocessors: Set[Dependent[Any]] = set()
|
||||||
|
|
||||||
EVENT_PCS_PARAMS = [
|
EVENT_PCS_PARAMS = (
|
||||||
DependParam,
|
DependParam,
|
||||||
BotParam,
|
BotParam,
|
||||||
EventParam,
|
EventParam,
|
||||||
StateParam,
|
StateParam,
|
||||||
DefaultParam,
|
DefaultParam,
|
||||||
]
|
)
|
||||||
RUN_PREPCS_PARAMS = [
|
RUN_PREPCS_PARAMS = (
|
||||||
DependParam,
|
DependParam,
|
||||||
BotParam,
|
BotParam,
|
||||||
EventParam,
|
EventParam,
|
||||||
@@ -65,8 +66,8 @@ RUN_PREPCS_PARAMS = [
|
|||||||
ArgParam,
|
ArgParam,
|
||||||
MatcherParam,
|
MatcherParam,
|
||||||
DefaultParam,
|
DefaultParam,
|
||||||
]
|
)
|
||||||
RUN_POSTPCS_PARAMS = [
|
RUN_POSTPCS_PARAMS = (
|
||||||
DependParam,
|
DependParam,
|
||||||
ExceptionParam,
|
ExceptionParam,
|
||||||
BotParam,
|
BotParam,
|
||||||
@@ -75,7 +76,7 @@ RUN_POSTPCS_PARAMS = [
|
|||||||
ArgParam,
|
ArgParam,
|
||||||
MatcherParam,
|
MatcherParam,
|
||||||
DefaultParam,
|
DefaultParam,
|
||||||
]
|
)
|
||||||
|
|
||||||
|
|
||||||
def event_preprocessor(func: T_EventPreProcessor) -> T_EventPreProcessor:
|
def event_preprocessor(func: T_EventPreProcessor) -> T_EventPreProcessor:
|
||||||
@@ -120,10 +121,8 @@ async def _check_matcher(
|
|||||||
dependency_cache: Optional[T_DependencyCache] = None,
|
dependency_cache: Optional[T_DependencyCache] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if Matcher.expire_time and datetime.now() > Matcher.expire_time:
|
if Matcher.expire_time and datetime.now() > Matcher.expire_time:
|
||||||
try:
|
with contextlib.suppress(Exception):
|
||||||
matchers[priority].remove(Matcher)
|
matchers[priority].remove(Matcher)
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -138,11 +137,8 @@ async def _check_matcher(
|
|||||||
return
|
return
|
||||||
|
|
||||||
if Matcher.temp:
|
if Matcher.temp:
|
||||||
try:
|
with contextlib.suppress(Exception):
|
||||||
matchers[priority].remove(Matcher)
|
matchers[priority].remove(Matcher)
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
await _run_matcher(Matcher, bot, event, state, stack, dependency_cache)
|
await _run_matcher(Matcher, bot, event, state, stack, dependency_cache)
|
||||||
|
|
||||||
|
|
||||||
@@ -157,67 +153,58 @@ async def _run_matcher(
|
|||||||
logger.info(f"Event will be handled by {Matcher}")
|
logger.info(f"Event will be handled by {Matcher}")
|
||||||
|
|
||||||
matcher = Matcher()
|
matcher = Matcher()
|
||||||
|
if coros := [
|
||||||
coros = list(
|
run_coro_with_catch(
|
||||||
map(
|
proc(
|
||||||
lambda x: run_coro_with_catch(
|
matcher=matcher,
|
||||||
x(
|
bot=bot,
|
||||||
matcher=matcher,
|
event=event,
|
||||||
bot=bot,
|
state=state,
|
||||||
event=event,
|
stack=stack,
|
||||||
state=state,
|
dependency_cache=dependency_cache,
|
||||||
stack=stack,
|
|
||||||
dependency_cache=dependency_cache,
|
|
||||||
),
|
|
||||||
(SkippedException,),
|
|
||||||
),
|
),
|
||||||
_run_preprocessors,
|
(SkippedException,),
|
||||||
)
|
)
|
||||||
)
|
for proc in _run_preprocessors
|
||||||
if coros:
|
]:
|
||||||
try:
|
try:
|
||||||
await asyncio.gather(*coros)
|
await asyncio.gather(*coros)
|
||||||
except IgnoredException:
|
except IgnoredException:
|
||||||
logger.opt(colors=True).info(
|
logger.opt(colors=True).info(f"{matcher} running is <b>cancelled</b>")
|
||||||
f"Matcher {matcher} running is <b>cancelled</b>"
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.opt(colors=True, exception=e).error(
|
logger.opt(colors=True, exception=e).error(
|
||||||
"<r><bg #f8bbd0>Error when running RunPreProcessors. "
|
"<r><bg #f8bbd0>Error when running RunPreProcessors. Running cancelled!</bg #f8bbd0></r>"
|
||||||
"Running cancelled!</bg #f8bbd0></r>"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
exception = None
|
exception = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.debug(f"Running matcher {matcher}")
|
logger.debug(f"Running {matcher}")
|
||||||
await matcher.run(bot, event, state, stack, dependency_cache)
|
await matcher.run(bot, event, state, stack, dependency_cache)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.opt(colors=True, exception=e).error(
|
logger.opt(colors=True, exception=e).error(
|
||||||
f"<r><bg #f8bbd0>Running matcher {matcher} failed.</bg #f8bbd0></r>"
|
f"<r><bg #f8bbd0>Running {matcher} failed.</bg #f8bbd0></r>"
|
||||||
)
|
)
|
||||||
exception = e
|
exception = e
|
||||||
|
|
||||||
coros = list(
|
if coros := [
|
||||||
map(
|
run_coro_with_catch(
|
||||||
lambda x: run_coro_with_catch(
|
proc(
|
||||||
x(
|
matcher=matcher,
|
||||||
matcher=matcher,
|
exception=exception,
|
||||||
exception=exception,
|
bot=bot,
|
||||||
bot=bot,
|
event=event,
|
||||||
event=event,
|
state=matcher.state,
|
||||||
state=matcher.state,
|
stack=stack,
|
||||||
stack=stack,
|
dependency_cache=dependency_cache,
|
||||||
dependency_cache=dependency_cache,
|
|
||||||
),
|
|
||||||
(SkippedException,),
|
|
||||||
),
|
),
|
||||||
_run_postprocessors,
|
(SkippedException,),
|
||||||
)
|
)
|
||||||
)
|
for proc in _run_postprocessors
|
||||||
if coros:
|
]:
|
||||||
try:
|
try:
|
||||||
await asyncio.gather(*coros)
|
await asyncio.gather(*coros)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -244,7 +231,7 @@ async def handle_event(bot: "Bot", event: "Event") -> None:
|
|||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
show_log = True
|
show_log = True
|
||||||
log_msg = f"<m>{escape_tag(bot.type.upper())} {escape_tag(bot.self_id)}</m> | "
|
log_msg = f"<m>{escape_tag(bot.type)} {escape_tag(bot.self_id)}</m> | "
|
||||||
try:
|
try:
|
||||||
log_msg += event.get_log_string()
|
log_msg += event.get_log_string()
|
||||||
except NoLogException:
|
except NoLogException:
|
||||||
@@ -256,22 +243,19 @@ async def handle_event(bot: "Bot", event: "Event") -> None:
|
|||||||
dependency_cache: T_DependencyCache = {}
|
dependency_cache: T_DependencyCache = {}
|
||||||
|
|
||||||
async with AsyncExitStack() as stack:
|
async with AsyncExitStack() as stack:
|
||||||
coros = list(
|
if coros := [
|
||||||
map(
|
run_coro_with_catch(
|
||||||
lambda x: run_coro_with_catch(
|
proc(
|
||||||
x(
|
bot=bot,
|
||||||
bot=bot,
|
event=event,
|
||||||
event=event,
|
state=state,
|
||||||
state=state,
|
stack=stack,
|
||||||
stack=stack,
|
dependency_cache=dependency_cache,
|
||||||
dependency_cache=dependency_cache,
|
|
||||||
),
|
|
||||||
(SkippedException,),
|
|
||||||
),
|
),
|
||||||
_event_preprocessors,
|
(SkippedException,),
|
||||||
)
|
)
|
||||||
)
|
for proc in _event_preprocessors
|
||||||
if coros:
|
]:
|
||||||
try:
|
try:
|
||||||
if show_log:
|
if show_log:
|
||||||
logger.debug("Running PreProcessors...")
|
logger.debug("Running PreProcessors...")
|
||||||
@@ -324,22 +308,19 @@ 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>"
|
||||||
)
|
)
|
||||||
|
|
||||||
coros = list(
|
if coros := [
|
||||||
map(
|
run_coro_with_catch(
|
||||||
lambda x: run_coro_with_catch(
|
proc(
|
||||||
x(
|
bot=bot,
|
||||||
bot=bot,
|
event=event,
|
||||||
event=event,
|
state=state,
|
||||||
state=state,
|
stack=stack,
|
||||||
stack=stack,
|
dependency_cache=dependency_cache,
|
||||||
dependency_cache=dependency_cache,
|
|
||||||
),
|
|
||||||
(SkippedException,),
|
|
||||||
),
|
),
|
||||||
_event_postprocessors,
|
(SkippedException,),
|
||||||
)
|
)
|
||||||
)
|
for proc in _event_postprocessors
|
||||||
if coros:
|
]:
|
||||||
try:
|
try:
|
||||||
if show_log:
|
if show_log:
|
||||||
logger.debug("Running PostProcessors...")
|
logger.debug("Running PostProcessors...")
|
||||||
|
@@ -5,17 +5,16 @@ FrontMatter:
|
|||||||
description: nonebot.params 模块
|
description: nonebot.params 模块
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Any, Dict, List, Tuple, Optional
|
from typing import Any, Dict, List, Tuple, Union, Optional
|
||||||
|
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
from nonebot.adapters import Event, Message
|
|
||||||
from nonebot.internal.params import Arg as Arg
|
from nonebot.internal.params import Arg as Arg
|
||||||
from nonebot.internal.params import State as State
|
|
||||||
from nonebot.internal.params import ArgStr as ArgStr
|
from nonebot.internal.params import ArgStr as ArgStr
|
||||||
from nonebot.internal.params import Depends as Depends
|
from nonebot.internal.params import Depends as Depends
|
||||||
from nonebot.internal.params import ArgParam as ArgParam
|
from nonebot.internal.params import ArgParam as ArgParam
|
||||||
from nonebot.internal.params import BotParam as BotParam
|
from nonebot.internal.params import BotParam as BotParam
|
||||||
|
from nonebot.adapters import Event, Message, MessageSegment
|
||||||
from nonebot.internal.params import EventParam as EventParam
|
from nonebot.internal.params import EventParam as EventParam
|
||||||
from nonebot.internal.params import StateParam as StateParam
|
from nonebot.internal.params import StateParam as StateParam
|
||||||
from nonebot.internal.params import DependParam as DependParam
|
from nonebot.internal.params import DependParam as DependParam
|
||||||
@@ -110,15 +109,15 @@ def CommandStart() -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _shell_command_args(state: T_State) -> Any:
|
def _shell_command_args(state: T_State) -> Any:
|
||||||
return state[SHELL_ARGS]
|
return state[SHELL_ARGS] # Namespace or ParserExit
|
||||||
|
|
||||||
|
|
||||||
def ShellCommandArgs():
|
def ShellCommandArgs() -> Any:
|
||||||
"""shell 命令解析后的参数字典"""
|
"""shell 命令解析后的参数字典"""
|
||||||
return Depends(_shell_command_args, use_cache=False)
|
return Depends(_shell_command_args, use_cache=False)
|
||||||
|
|
||||||
|
|
||||||
def _shell_command_argv(state: T_State) -> List[str]:
|
def _shell_command_argv(state: T_State) -> List[Union[str, MessageSegment]]:
|
||||||
return state[SHELL_ARGV]
|
return state[SHELL_ARGV]
|
||||||
|
|
||||||
|
|
||||||
@@ -174,7 +173,6 @@ def LastReceived(default: Any = None) -> Any:
|
|||||||
|
|
||||||
__autodoc__ = {
|
__autodoc__ = {
|
||||||
"Arg": True,
|
"Arg": True,
|
||||||
"State": True,
|
|
||||||
"ArgStr": True,
|
"ArgStr": True,
|
||||||
"Depends": True,
|
"Depends": True,
|
||||||
"ArgParam": True,
|
"ArgParam": True,
|
||||||
|
@@ -20,6 +20,9 @@ class Message:
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "Message()"
|
||||||
|
|
||||||
async def __call__(self, type: str = EventType()) -> bool:
|
async def __call__(self, type: str = EventType()) -> bool:
|
||||||
return type == "message"
|
return type == "message"
|
||||||
|
|
||||||
@@ -29,6 +32,9 @@ class Notice:
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "Notice()"
|
||||||
|
|
||||||
async def __call__(self, type: str = EventType()) -> bool:
|
async def __call__(self, type: str = EventType()) -> bool:
|
||||||
return type == "notice"
|
return type == "notice"
|
||||||
|
|
||||||
@@ -38,6 +44,9 @@ class Request:
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "Request()"
|
||||||
|
|
||||||
async def __call__(self, type: str = EventType()) -> bool:
|
async def __call__(self, type: str = EventType()) -> bool:
|
||||||
return type == "request"
|
return type == "request"
|
||||||
|
|
||||||
@@ -47,6 +56,9 @@ class MetaEvent:
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "MetaEvent()"
|
||||||
|
|
||||||
async def __call__(self, type: str = EventType()) -> bool:
|
async def __call__(self, type: str = EventType()) -> bool:
|
||||||
return type == "meta_event"
|
return type == "meta_event"
|
||||||
|
|
||||||
@@ -78,16 +90,23 @@ class SuperUser:
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "Superuser()"
|
||||||
|
|
||||||
async def __call__(self, bot: Bot, event: Event) -> bool:
|
async def __call__(self, bot: Bot, event: Event) -> bool:
|
||||||
return event.get_type() == "message" and (
|
try:
|
||||||
f"{bot.adapter.get_name().split(maxsplit=1)[0].lower()}:{event.get_user_id()}"
|
user_id = event.get_user_id()
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return (
|
||||||
|
f"{bot.adapter.get_name().split(maxsplit=1)[0].lower()}:{user_id}"
|
||||||
in bot.config.superusers
|
in bot.config.superusers
|
||||||
or event.get_user_id() in bot.config.superusers # 兼容旧配置
|
or user_id in bot.config.superusers # 兼容旧配置
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
SUPERUSER: Permission = Permission(SuperUser())
|
SUPERUSER: Permission = Permission(SuperUser())
|
||||||
"""匹配任意超级用户消息类型事件"""
|
"""匹配任意超级用户事件"""
|
||||||
|
|
||||||
__autodoc__ = {
|
__autodoc__ = {
|
||||||
"Permission": True,
|
"Permission": True,
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
- `on_command` => {ref}``on_command` <nonebot.plugin.on.on_command>`
|
- `on_command` => {ref}``on_command` <nonebot.plugin.on.on_command>`
|
||||||
- `on_shell_command` => {ref}``on_shell_command` <nonebot.plugin.on.on_shell_command>`
|
- `on_shell_command` => {ref}``on_shell_command` <nonebot.plugin.on.on_shell_command>`
|
||||||
- `on_regex` => {ref}``on_regex` <nonebot.plugin.on.on_regex>`
|
- `on_regex` => {ref}``on_regex` <nonebot.plugin.on.on_regex>`
|
||||||
|
- `on_type` => {ref}``on_type` <nonebot.plugin.on.on_type>`
|
||||||
- `CommandGroup` => {ref}``CommandGroup` <nonebot.plugin.on.CommandGroup>`
|
- `CommandGroup` => {ref}``CommandGroup` <nonebot.plugin.on.CommandGroup>`
|
||||||
- `Matchergroup` => {ref}``MatcherGroup` <nonebot.plugin.on.MatcherGroup>`
|
- `Matchergroup` => {ref}``MatcherGroup` <nonebot.plugin.on.MatcherGroup>`
|
||||||
- `load_plugin` => {ref}``load_plugin` <nonebot.plugin.load.load_plugin>`
|
- `load_plugin` => {ref}``load_plugin` <nonebot.plugin.load.load_plugin>`
|
||||||
@@ -25,8 +26,8 @@
|
|||||||
- `load_from_toml` => {ref}``load_from_toml` <nonebot.plugin.load.load_from_toml>`
|
- `load_from_toml` => {ref}``load_from_toml` <nonebot.plugin.load.load_from_toml>`
|
||||||
- `load_builtin_plugin` => {ref}``load_builtin_plugin` <nonebot.plugin.load.load_builtin_plugin>`
|
- `load_builtin_plugin` => {ref}``load_builtin_plugin` <nonebot.plugin.load.load_builtin_plugin>`
|
||||||
- `load_builtin_plugins` => {ref}``load_builtin_plugins` <nonebot.plugin.load.load_builtin_plugins>`
|
- `load_builtin_plugins` => {ref}``load_builtin_plugins` <nonebot.plugin.load.load_builtin_plugins>`
|
||||||
- `export` => {ref}``export` <nonebot.plugin.export.export>`
|
|
||||||
- `require` => {ref}``require` <nonebot.plugin.load.require>`
|
- `require` => {ref}``require` <nonebot.plugin.load.require>`
|
||||||
|
- `PluginMetadata` => {ref}``PluginMetadata` <nonebot.plugin.plugin.PluginMetadata>`
|
||||||
|
|
||||||
FrontMatter:
|
FrontMatter:
|
||||||
sidebar_position: 0
|
sidebar_position: 0
|
||||||
@@ -85,13 +86,12 @@ def get_plugin_by_module_name(module_name: str) -> Optional["Plugin"]:
|
|||||||
参数:
|
参数:
|
||||||
module_name: 模块名,即 {ref}`nonebot.plugin.plugin.Plugin.module_name`。
|
module_name: 模块名,即 {ref}`nonebot.plugin.plugin.Plugin.module_name`。
|
||||||
"""
|
"""
|
||||||
splits = module_name.split(".")
|
|
||||||
loaded = {plugin.module_name: plugin for plugin in _plugins.values()}
|
loaded = {plugin.module_name: plugin for plugin in _plugins.values()}
|
||||||
while splits:
|
has_parent = True
|
||||||
name = ".".join(splits)
|
while has_parent:
|
||||||
if name in loaded:
|
if module_name in loaded:
|
||||||
return loaded[name]
|
return loaded[module_name]
|
||||||
splits.pop()
|
module_name, *has_parent = module_name.rsplit(".", 1)
|
||||||
|
|
||||||
|
|
||||||
def get_loaded_plugins() -> Set["Plugin"]:
|
def get_loaded_plugins() -> Set["Plugin"]:
|
||||||
@@ -106,8 +106,7 @@ def get_available_plugin_names() -> Set[str]:
|
|||||||
|
|
||||||
from .on import on as on
|
from .on import on as on
|
||||||
from .manager import PluginManager
|
from .manager import PluginManager
|
||||||
from .export import Export as Export
|
from .on import on_type as on_type
|
||||||
from .export import export as export
|
|
||||||
from .load import require as require
|
from .load import require as require
|
||||||
from .on import on_regex as on_regex
|
from .on import on_regex as on_regex
|
||||||
from .plugin import Plugin as Plugin
|
from .plugin import Plugin as Plugin
|
||||||
|
@@ -1,64 +0,0 @@
|
|||||||
"""本模块定义了插件导出的内容对象。
|
|
||||||
|
|
||||||
在新版插件系统中,推荐优先使用直接 import 所需要的插件内容。
|
|
||||||
|
|
||||||
FrontMatter:
|
|
||||||
sidebar_position: 4
|
|
||||||
description: nonebot.plugin.export 模块
|
|
||||||
"""
|
|
||||||
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
from . import _current_plugin_chain
|
|
||||||
|
|
||||||
|
|
||||||
class Export(dict):
|
|
||||||
"""插件导出内容以使得其他插件可以获得。
|
|
||||||
|
|
||||||
用法:
|
|
||||||
```python
|
|
||||||
nonebot.export().default = "bar"
|
|
||||||
|
|
||||||
@nonebot.export()
|
|
||||||
def some_function():
|
|
||||||
pass
|
|
||||||
|
|
||||||
# this doesn't work before python 3.9
|
|
||||||
# use
|
|
||||||
# export = nonebot.export(); @export.sub
|
|
||||||
# instead
|
|
||||||
# See also PEP-614: https://www.python.org/dev/peps/pep-0614/
|
|
||||||
@nonebot.export().sub
|
|
||||||
def something_else():
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __call__(self, func, **kwargs):
|
|
||||||
self[func.__name__] = func
|
|
||||||
self.update(kwargs)
|
|
||||||
return func
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
super().__setitem__(key, Export(value) if isinstance(value, dict) else value)
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
self[name] = Export(value) if isinstance(value, dict) else value
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name not in self:
|
|
||||||
self[name] = Export()
|
|
||||||
return self[name]
|
|
||||||
|
|
||||||
|
|
||||||
def export() -> Export:
|
|
||||||
"""获取当前插件的导出内容对象"""
|
|
||||||
warnings.warn(
|
|
||||||
"nonebot.export() is deprecated. "
|
|
||||||
"See https://github.com/nonebot/nonebot2/issues/935.",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
plugins = _current_plugin_chain.get()
|
|
||||||
if not plugins:
|
|
||||||
raise RuntimeError("Export outside of the plugin!")
|
|
||||||
return plugins[-1].export
|
|
@@ -5,24 +5,30 @@ FrontMatter:
|
|||||||
description: nonebot.plugin.load 模块
|
description: nonebot.plugin.load 模块
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import warnings
|
from pathlib import Path
|
||||||
from typing import Set, Iterable, Optional
|
from types import ModuleType
|
||||||
|
from typing import Set, Union, Iterable, Optional
|
||||||
|
|
||||||
import tomlkit
|
import tomlkit
|
||||||
|
|
||||||
from .export import Export
|
from nonebot.utils import path_to_module_name
|
||||||
|
|
||||||
from .plugin import Plugin
|
from .plugin import Plugin
|
||||||
from .manager import PluginManager
|
from .manager import PluginManager
|
||||||
from . import _managers, get_plugin, _module_name_to_plugin_name
|
from . import _managers, get_plugin, _module_name_to_plugin_name
|
||||||
|
|
||||||
|
|
||||||
def load_plugin(module_path: str) -> Optional[Plugin]:
|
def load_plugin(module_path: Union[str, Path]) -> Optional[Plugin]:
|
||||||
"""加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
|
"""加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
module_path: 插件名称 `path.to.your.plugin`
|
module_path: 插件名称 `path.to.your.plugin` 或插件路径 `pathlib.Path(path/to/your/plugin)`
|
||||||
"""
|
"""
|
||||||
|
module_path = (
|
||||||
|
path_to_module_name(module_path)
|
||||||
|
if isinstance(module_path, Path)
|
||||||
|
else module_path
|
||||||
|
)
|
||||||
manager = PluginManager([module_path])
|
manager = PluginManager([module_path])
|
||||||
_managers.append(manager)
|
_managers.append(manager)
|
||||||
return manager.load_plugin(module_path)
|
return manager.load_plugin(module_path)
|
||||||
@@ -74,6 +80,8 @@ def load_from_json(file_path: str, encoding: str = "utf-8") -> Set[Plugin]:
|
|||||||
"""
|
"""
|
||||||
with open(file_path, "r", encoding=encoding) as f:
|
with open(file_path, "r", encoding=encoding) as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise TypeError("json file must contains a dict!")
|
||||||
plugins = data.get("plugins")
|
plugins = data.get("plugins")
|
||||||
plugin_dirs = data.get("plugin_dirs")
|
plugin_dirs = data.get("plugin_dirs")
|
||||||
assert isinstance(plugins, list), "plugins must be a list of plugin name"
|
assert isinstance(plugins, list), "plugins must be a list of plugin name"
|
||||||
@@ -103,15 +111,10 @@ def load_from_toml(file_path: str, encoding: str = "utf-8") -> Set[Plugin]:
|
|||||||
data = tomlkit.parse(f.read()) # type: ignore
|
data = tomlkit.parse(f.read()) # type: ignore
|
||||||
|
|
||||||
nonebot_data = data.get("tool", {}).get("nonebot")
|
nonebot_data = data.get("tool", {}).get("nonebot")
|
||||||
if not nonebot_data:
|
if nonebot_data is None:
|
||||||
nonebot_data = data.get("nonebot", {}).get("plugins")
|
raise ValueError("Cannot find '[tool.nonebot]' in given toml file!")
|
||||||
if nonebot_data:
|
if not isinstance(nonebot_data, dict):
|
||||||
warnings.warn(
|
raise TypeError("'[tool.nonebot]' must be a Table!")
|
||||||
"[nonebot.plugins] table is deprecated. Use [tool.nonebot] instead.",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise ValueError("Cannot find '[tool.nonebot]' in given toml file!")
|
|
||||||
plugins = nonebot_data.get("plugins", [])
|
plugins = nonebot_data.get("plugins", [])
|
||||||
plugin_dirs = nonebot_data.get("plugin_dirs", [])
|
plugin_dirs = nonebot_data.get("plugin_dirs", [])
|
||||||
assert isinstance(plugins, list), "plugins must be a list of plugin name"
|
assert isinstance(plugins, list), "plugins must be a list of plugin name"
|
||||||
@@ -143,7 +146,7 @@ def _find_manager_by_name(name: str) -> Optional[PluginManager]:
|
|||||||
return manager
|
return manager
|
||||||
|
|
||||||
|
|
||||||
def require(name: str) -> Export:
|
def require(name: str) -> ModuleType:
|
||||||
"""获取一个插件的导出内容。
|
"""获取一个插件的导出内容。
|
||||||
|
|
||||||
如果为 `load_plugins` 文件夹导入的插件,则为文件(夹)名。
|
如果为 `load_plugins` 文件夹导入的插件,则为文件(夹)名。
|
||||||
@@ -156,11 +159,10 @@ def require(name: str) -> Export:
|
|||||||
"""
|
"""
|
||||||
plugin = get_plugin(_module_name_to_plugin_name(name))
|
plugin = get_plugin(_module_name_to_plugin_name(name))
|
||||||
if not plugin:
|
if not plugin:
|
||||||
manager = _find_manager_by_name(name)
|
if manager := _find_manager_by_name(name):
|
||||||
if manager:
|
|
||||||
plugin = manager.load_plugin(name)
|
plugin = manager.load_plugin(name)
|
||||||
else:
|
else:
|
||||||
plugin = load_plugin(name)
|
plugin = load_plugin(name)
|
||||||
if not plugin:
|
if not plugin:
|
||||||
raise RuntimeError(f'Cannot load plugin "{name}"!')
|
raise RuntimeError(f'Cannot load plugin "{name}"!')
|
||||||
return plugin.export
|
return plugin.module
|
||||||
|
@@ -17,7 +17,7 @@ from importlib.machinery import PathFinder, SourceFileLoader
|
|||||||
from typing import Set, Dict, List, Union, Iterable, Optional, Sequence
|
from typing import Set, Dict, List, Union, Iterable, Optional, Sequence
|
||||||
|
|
||||||
from nonebot.log import logger
|
from nonebot.log import logger
|
||||||
from nonebot.utils import escape_tag
|
from nonebot.utils import escape_tag, path_to_module_name
|
||||||
|
|
||||||
from .plugin import Plugin, PluginMetadata
|
from .plugin import Plugin, PluginMetadata
|
||||||
from . import (
|
from . import (
|
||||||
@@ -51,6 +51,9 @@ class PluginManager:
|
|||||||
self._searched_plugin_names: Dict[str, Path] = {}
|
self._searched_plugin_names: Dict[str, Path] = {}
|
||||||
self.prepare_plugins()
|
self.prepare_plugins()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"PluginManager(plugins={self.plugins}, search_path={self.search_path})"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def third_party_plugins(self) -> Set[str]:
|
def third_party_plugins(self) -> Set[str]:
|
||||||
"""返回所有独立插件名称。"""
|
"""返回所有独立插件名称。"""
|
||||||
@@ -66,13 +69,6 @@ class PluginManager:
|
|||||||
"""返回当前插件管理器中可用的插件名称。"""
|
"""返回当前插件管理器中可用的插件名称。"""
|
||||||
return self.third_party_plugins | self.searched_plugins
|
return self.third_party_plugins | self.searched_plugins
|
||||||
|
|
||||||
def _path_to_module_name(self, path: Path) -> str:
|
|
||||||
rel_path = path.resolve().relative_to(Path(".").resolve())
|
|
||||||
if rel_path.stem == "__init__":
|
|
||||||
return ".".join(rel_path.parts[:-1])
|
|
||||||
else:
|
|
||||||
return ".".join(rel_path.parts[:-1] + (rel_path.stem,))
|
|
||||||
|
|
||||||
def _previous_plugins(self) -> Set[str]:
|
def _previous_plugins(self) -> Set[str]:
|
||||||
_pre_managers: List[PluginManager]
|
_pre_managers: List[PluginManager]
|
||||||
if self in _managers:
|
if self in _managers:
|
||||||
@@ -86,7 +82,6 @@ class PluginManager:
|
|||||||
|
|
||||||
def prepare_plugins(self) -> Set[str]:
|
def prepare_plugins(self) -> Set[str]:
|
||||||
"""搜索插件并缓存插件名称。"""
|
"""搜索插件并缓存插件名称。"""
|
||||||
|
|
||||||
# get all previous ready to load plugins
|
# get all previous ready to load plugins
|
||||||
previous_plugins = self._previous_plugins()
|
previous_plugins = self._previous_plugins()
|
||||||
searched_plugins: Dict[str, Path] = {}
|
searched_plugins: Dict[str, Path] = {}
|
||||||
@@ -118,11 +113,13 @@ class PluginManager:
|
|||||||
f"Plugin already exists: {module_info.name}! Check your plugin name"
|
f"Plugin already exists: {module_info.name}! Check your plugin name"
|
||||||
)
|
)
|
||||||
|
|
||||||
module_spec = module_info.module_finder.find_spec(module_info.name, None)
|
if not (
|
||||||
if not module_spec:
|
module_spec := module_info.module_finder.find_spec(
|
||||||
|
module_info.name, None
|
||||||
|
)
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
module_path = module_spec.origin
|
if not (module_path := module_spec.origin):
|
||||||
if not module_path:
|
|
||||||
continue
|
continue
|
||||||
searched_plugins[module_info.name] = Path(module_path).resolve()
|
searched_plugins[module_info.name] = Path(module_path).resolve()
|
||||||
|
|
||||||
@@ -146,7 +143,7 @@ class PluginManager:
|
|||||||
module = importlib.import_module(self._third_party_plugin_names[name])
|
module = importlib.import_module(self._third_party_plugin_names[name])
|
||||||
elif name in self._searched_plugin_names:
|
elif name in self._searched_plugin_names:
|
||||||
module = importlib.import_module(
|
module = importlib.import_module(
|
||||||
self._path_to_module_name(self._searched_plugin_names[name])
|
path_to_module_name(self._searched_plugin_names[name])
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(f"Plugin not found: {name}! Check your plugin name")
|
raise RuntimeError(f"Plugin not found: {name}! Check your plugin name")
|
||||||
@@ -154,8 +151,7 @@ class PluginManager:
|
|||||||
logger.opt(colors=True).success(
|
logger.opt(colors=True).success(
|
||||||
f'Succeeded to import "<y>{escape_tag(name)}</y>"'
|
f'Succeeded to import "<y>{escape_tag(name)}</y>"'
|
||||||
)
|
)
|
||||||
plugin = getattr(module, "__plugin__", None)
|
if (plugin := getattr(module, "__plugin__", None)) is None:
|
||||||
if plugin is None:
|
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"Module {module.__name__} is not loaded as a plugin! "
|
f"Module {module.__name__} is not loaded as a plugin! "
|
||||||
"Make sure not to import it before loading."
|
"Make sure not to import it before loading."
|
||||||
|
@@ -10,6 +10,7 @@ from types import ModuleType
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Set, Dict, List, Type, Tuple, Union, Optional
|
from typing import Any, Set, Dict, List, Type, Tuple, Union, Optional
|
||||||
|
|
||||||
|
from nonebot.adapters import Event
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
from nonebot.permission import Permission
|
from nonebot.permission import Permission
|
||||||
from nonebot.dependencies import Dependent
|
from nonebot.dependencies import Dependent
|
||||||
@@ -19,6 +20,7 @@ from nonebot.rule import (
|
|||||||
ArgumentParser,
|
ArgumentParser,
|
||||||
regex,
|
regex,
|
||||||
command,
|
command,
|
||||||
|
is_type,
|
||||||
keyword,
|
keyword,
|
||||||
endswith,
|
endswith,
|
||||||
fullmatch,
|
fullmatch,
|
||||||
@@ -30,9 +32,8 @@ from .manager import _current_plugin_chain
|
|||||||
|
|
||||||
|
|
||||||
def _store_matcher(matcher: Type[Matcher]) -> None:
|
def _store_matcher(matcher: Type[Matcher]) -> None:
|
||||||
plugins = _current_plugin_chain.get()
|
|
||||||
# only store the matcher defined in the plugin
|
# only store the matcher defined in the plugin
|
||||||
if plugins:
|
if plugins := _current_plugin_chain.get():
|
||||||
plugins[-1].matcher.add(matcher)
|
plugins[-1].matcher.add(matcher)
|
||||||
|
|
||||||
|
|
||||||
@@ -368,7 +369,7 @@ def on_command(
|
|||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
|
|
||||||
commands = set([cmd]) | (aliases or set())
|
commands = {cmd} | (aliases or set())
|
||||||
block = kwargs.pop("block", False)
|
block = kwargs.pop("block", False)
|
||||||
return on_message(
|
return on_message(
|
||||||
command(*commands) & rule, block=block, **kwargs, _depth=_depth + 1
|
command(*commands) & rule, block=block, **kwargs, _depth=_depth + 1
|
||||||
@@ -403,7 +404,7 @@ def on_shell_command(
|
|||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
|
|
||||||
commands = set([cmd]) | (aliases or set())
|
commands = {cmd} | (aliases or set())
|
||||||
return on_message(
|
return on_message(
|
||||||
shell_command(*commands, parser=parser) & rule,
|
shell_command(*commands, parser=parser) & rule,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
@@ -437,7 +438,57 @@ def on_regex(
|
|||||||
return on_message(regex(pattern, flags) & rule, **kwargs, _depth=_depth + 1)
|
return on_message(regex(pattern, flags) & rule, **kwargs, _depth=_depth + 1)
|
||||||
|
|
||||||
|
|
||||||
class CommandGroup:
|
def on_type(
|
||||||
|
types: Union[Type[Event], Tuple[Type[Event]]],
|
||||||
|
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||||
|
*,
|
||||||
|
_depth: int = 0,
|
||||||
|
**kwargs,
|
||||||
|
) -> Type[Matcher]:
|
||||||
|
"""注册一个事件响应器,并且当事件为指定类型时响应。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
types: 事件类型
|
||||||
|
rule: 事件响应规则
|
||||||
|
permission: 事件响应权限
|
||||||
|
handlers: 事件处理函数列表
|
||||||
|
temp: 是否为临时事件响应器(仅执行一次)
|
||||||
|
expire_time: 事件响应器最终有效时间点,过时即被删除
|
||||||
|
priority: 事件响应器优先级
|
||||||
|
block: 是否阻止事件向更低优先级传递
|
||||||
|
state: 默认 state
|
||||||
|
"""
|
||||||
|
event_types = types if isinstance(types, tuple) else (types,)
|
||||||
|
return on(rule=is_type(*event_types) & rule, **kwargs, _depth=_depth + 1)
|
||||||
|
|
||||||
|
|
||||||
|
class _Group:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
"""创建一个事件响应器组合,参数为默认值,与 `on` 一致"""
|
||||||
|
self.matchers: List[Type[Matcher]] = []
|
||||||
|
"""组内事件响应器列表"""
|
||||||
|
self.base_kwargs: Dict[str, Any] = kwargs
|
||||||
|
"""其他传递给 `on` 的参数默认值"""
|
||||||
|
|
||||||
|
def _get_final_kwargs(
|
||||||
|
self, update: Dict[str, Any], *, exclude: Optional[Set[str]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""获取最终传递给 `on` 的参数
|
||||||
|
|
||||||
|
参数:
|
||||||
|
update: 更新的关键字参数
|
||||||
|
exclude: 需要排除的参数
|
||||||
|
"""
|
||||||
|
final_kwargs = self.base_kwargs.copy()
|
||||||
|
final_kwargs.update(update)
|
||||||
|
if exclude:
|
||||||
|
for key in exclude:
|
||||||
|
final_kwargs.pop(key, None)
|
||||||
|
final_kwargs["_depth"] = 1
|
||||||
|
return final_kwargs
|
||||||
|
|
||||||
|
|
||||||
|
class CommandGroup(_Group):
|
||||||
"""命令组,用于声明一组有相同名称前缀的命令。
|
"""命令组,用于声明一组有相同名称前缀的命令。
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
@@ -453,12 +504,13 @@ class CommandGroup:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, cmd: Union[str, Tuple[str, ...]], **kwargs):
|
def __init__(self, cmd: Union[str, Tuple[str, ...]], **kwargs):
|
||||||
self.basecmd: Tuple[str, ...] = (cmd,) if isinstance(cmd, str) else cmd
|
|
||||||
"""命令前缀"""
|
"""命令前缀"""
|
||||||
if "aliases" in kwargs:
|
super().__init__(**kwargs)
|
||||||
del kwargs["aliases"]
|
self.basecmd: Tuple[str, ...] = (cmd,) if isinstance(cmd, str) else cmd
|
||||||
self.base_kwargs: Dict[str, Any] = kwargs
|
self.base_kwargs.pop("aliases", None)
|
||||||
"""其他传递给 `on_command` 的参数默认值"""
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"CommandGroup(cmd={self.basecmd}, matchers={len(self.matchers)})"
|
||||||
|
|
||||||
def command(self, cmd: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]:
|
def command(self, cmd: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]:
|
||||||
"""注册一个新的命令。新参数将会覆盖命令组默认值
|
"""注册一个新的命令。新参数将会覆盖命令组默认值
|
||||||
@@ -477,10 +529,9 @@ class CommandGroup:
|
|||||||
"""
|
"""
|
||||||
sub_cmd = (cmd,) if isinstance(cmd, str) else cmd
|
sub_cmd = (cmd,) if isinstance(cmd, str) else cmd
|
||||||
cmd = self.basecmd + sub_cmd
|
cmd = self.basecmd + sub_cmd
|
||||||
|
matcher = on_command(cmd, **self._get_final_kwargs(kwargs))
|
||||||
final_kwargs = self.base_kwargs.copy()
|
self.matchers.append(matcher)
|
||||||
final_kwargs.update(kwargs)
|
return matcher
|
||||||
return on_command(cmd, **final_kwargs, _depth=1)
|
|
||||||
|
|
||||||
def shell_command(
|
def shell_command(
|
||||||
self, cmd: Union[str, Tuple[str, ...]], **kwargs
|
self, cmd: Union[str, Tuple[str, ...]], **kwargs
|
||||||
@@ -502,21 +553,16 @@ class CommandGroup:
|
|||||||
"""
|
"""
|
||||||
sub_cmd = (cmd,) if isinstance(cmd, str) else cmd
|
sub_cmd = (cmd,) if isinstance(cmd, str) else cmd
|
||||||
cmd = self.basecmd + sub_cmd
|
cmd = self.basecmd + sub_cmd
|
||||||
|
matcher = on_shell_command(cmd, **self._get_final_kwargs(kwargs))
|
||||||
final_kwargs = self.base_kwargs.copy()
|
self.matchers.append(matcher)
|
||||||
final_kwargs.update(kwargs)
|
return matcher
|
||||||
return on_shell_command(cmd, **final_kwargs, _depth=1)
|
|
||||||
|
|
||||||
|
|
||||||
class MatcherGroup:
|
class MatcherGroup(_Group):
|
||||||
"""事件响应器组合,统一管理。为 `Matcher` 创建提供默认属性。"""
|
"""事件响应器组合,统一管理。为 `Matcher` 创建提供默认属性。"""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __repr__(self) -> str:
|
||||||
"""创建一个事件响应器组合,参数为默认值,与 `on` 一致"""
|
return f"MatcherGroup(matchers={len(self.matchers)})"
|
||||||
self.matchers: List[Type[Matcher]] = []
|
|
||||||
"""组内事件响应器列表"""
|
|
||||||
self.base_kwargs: Dict[str, Any] = kwargs
|
|
||||||
"""其他传递给 `on` 的参数默认值"""
|
|
||||||
|
|
||||||
def on(self, **kwargs) -> Type[Matcher]:
|
def on(self, **kwargs) -> Type[Matcher]:
|
||||||
"""注册一个基础事件响应器,可自定义类型。
|
"""注册一个基础事件响应器,可自定义类型。
|
||||||
@@ -532,9 +578,7 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
matcher = on(**self._get_final_kwargs(kwargs))
|
||||||
final_kwargs.update(kwargs)
|
|
||||||
matcher = on(**final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -550,11 +594,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type", "permission"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_metaevent(**final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
final_kwargs.pop("permission", None)
|
|
||||||
matcher = on_metaevent(**final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -571,10 +612,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_message(**final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
matcher = on_message(**final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -590,10 +629,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type", "permission"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_notice(**final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
matcher = on_notice(**final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -609,10 +646,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type", "permission"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_request(**final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
matcher = on_request(**final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -633,10 +668,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_startswith(msg, **final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
matcher = on_startswith(msg, **final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -655,10 +688,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_endswith(msg, **final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
matcher = on_endswith(msg, **final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -677,10 +708,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_fullmatch(msg, **final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
matcher = on_fullmatch(msg, **final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -698,10 +727,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_keyword(keywords, **final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
matcher = on_keyword(keywords, **final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -727,10 +754,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_command(cmd, aliases=aliases, **final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
matcher = on_command(cmd, aliases=aliases, **final_kwargs, _depth=1)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -760,12 +785,8 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_shell_command(cmd, aliases=aliases, parser=parser, **final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
|
||||||
matcher = on_shell_command(
|
|
||||||
cmd, aliases=aliases, parser=parser, **final_kwargs, _depth=1
|
|
||||||
)
|
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
@@ -788,9 +809,28 @@ class MatcherGroup:
|
|||||||
block: 是否阻止事件向更低优先级传递
|
block: 是否阻止事件向更低优先级传递
|
||||||
state: 默认 state
|
state: 默认 state
|
||||||
"""
|
"""
|
||||||
final_kwargs = self.base_kwargs.copy()
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type"})
|
||||||
final_kwargs.update(kwargs)
|
matcher = on_regex(pattern, flags=flags, **final_kwargs)
|
||||||
final_kwargs.pop("type", None)
|
self.matchers.append(matcher)
|
||||||
matcher = on_regex(pattern, flags=flags, **final_kwargs, _depth=1)
|
return matcher
|
||||||
|
|
||||||
|
def on_type(
|
||||||
|
self, types: Union[Type[Event], Tuple[Type[Event]]], **kwargs
|
||||||
|
) -> Type[Matcher]:
|
||||||
|
"""注册一个事件响应器,并且当事件为指定类型时响应。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
types: 事件类型
|
||||||
|
rule: 事件响应规则
|
||||||
|
permission: 事件响应权限
|
||||||
|
handlers: 事件处理函数列表
|
||||||
|
temp: 是否为临时事件响应器(仅执行一次)
|
||||||
|
expire_time: 事件响应器最终有效时间点,过时即被删除
|
||||||
|
priority: 事件响应器优先级
|
||||||
|
block: 是否阻止事件向更低优先级传递
|
||||||
|
state: 默认 state
|
||||||
|
"""
|
||||||
|
final_kwargs = self._get_final_kwargs(kwargs, exclude={"type"})
|
||||||
|
matcher = on_type(types, **final_kwargs)
|
||||||
self.matchers.append(matcher)
|
self.matchers.append(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
@@ -2,6 +2,7 @@ import re
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Set, List, Type, Tuple, Union, Optional
|
from typing import Set, List, Type, Tuple, Union, Optional
|
||||||
|
|
||||||
|
from nonebot.adapters import Event
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
from nonebot.permission import Permission
|
from nonebot.permission import Permission
|
||||||
from nonebot.dependencies import Dependent
|
from nonebot.dependencies import Dependent
|
||||||
@@ -152,6 +153,18 @@ def on_regex(
|
|||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
state: Optional[T_State] = ...,
|
state: Optional[T_State] = ...,
|
||||||
) -> Type[Matcher]: ...
|
) -> Type[Matcher]: ...
|
||||||
|
def on_type(
|
||||||
|
types: Union[Type[Event], Tuple[Type[Event]]],
|
||||||
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
|
*,
|
||||||
|
permission: Optional[Union[Permission, T_PermissionChecker]] = ...,
|
||||||
|
handlers: Optional[List[Union[T_Handler, Dependent]]] = ...,
|
||||||
|
temp: bool = ...,
|
||||||
|
expire_time: Optional[Union[datetime, timedelta]] = ...,
|
||||||
|
priority: int = ...,
|
||||||
|
block: bool = ...,
|
||||||
|
state: Optional[T_State] = ...,
|
||||||
|
) -> Type[Matcher]: ...
|
||||||
|
|
||||||
class CommandGroup:
|
class CommandGroup:
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -171,8 +184,8 @@ class CommandGroup:
|
|||||||
self,
|
self,
|
||||||
cmd: Union[str, Tuple[str, ...]],
|
cmd: Union[str, Tuple[str, ...]],
|
||||||
*,
|
*,
|
||||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]],
|
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
|
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
||||||
permission: Optional[Union[Permission, T_PermissionChecker]] = ...,
|
permission: Optional[Union[Permission, T_PermissionChecker]] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Dependent]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Dependent]]] = ...,
|
||||||
temp: bool = ...,
|
temp: bool = ...,
|
||||||
@@ -186,7 +199,7 @@ class CommandGroup:
|
|||||||
cmd: Union[str, Tuple[str, ...]],
|
cmd: Union[str, Tuple[str, ...]],
|
||||||
*,
|
*,
|
||||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
aliases: Optional[Set[Union[str, Tuple[str, ...]]]],
|
aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = ...,
|
||||||
parser: Optional[ArgumentParser] = ...,
|
parser: Optional[ArgumentParser] = ...,
|
||||||
permission: Optional[Union[Permission, T_PermissionChecker]] = ...,
|
permission: Optional[Union[Permission, T_PermissionChecker]] = ...,
|
||||||
handlers: Optional[List[Union[T_Handler, Dependent]]] = ...,
|
handlers: Optional[List[Union[T_Handler, Dependent]]] = ...,
|
||||||
@@ -367,3 +380,16 @@ class MatcherGroup:
|
|||||||
block: bool = ...,
|
block: bool = ...,
|
||||||
state: Optional[T_State] = ...,
|
state: Optional[T_State] = ...,
|
||||||
) -> Type[Matcher]: ...
|
) -> Type[Matcher]: ...
|
||||||
|
def on_type(
|
||||||
|
self,
|
||||||
|
types: Union[Type[Event], Tuple[Type[Event]]],
|
||||||
|
*,
|
||||||
|
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||||
|
permission: Optional[Union[Permission, T_PermissionChecker]] = ...,
|
||||||
|
handlers: Optional[List[Union[T_Handler, Dependent]]] = ...,
|
||||||
|
temp: bool = ...,
|
||||||
|
expire_time: Optional[Union[datetime, timedelta]] = ...,
|
||||||
|
priority: int = ...,
|
||||||
|
block: bool = ...,
|
||||||
|
state: Optional[T_State] = ...,
|
||||||
|
) -> Type[Matcher]: ...
|
||||||
|
@@ -12,7 +12,6 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
|
|
||||||
from .export import Export
|
|
||||||
from . import _plugins as plugins # FIXME: backport for nonebug
|
from . import _plugins as plugins # FIXME: backport for nonebug
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -46,8 +45,6 @@ class Plugin:
|
|||||||
"""点分割模块路径"""
|
"""点分割模块路径"""
|
||||||
manager: "PluginManager"
|
manager: "PluginManager"
|
||||||
"""导入该插件的插件管理器"""
|
"""导入该插件的插件管理器"""
|
||||||
export: Export = field(default_factory=Export)
|
|
||||||
"""**Deprecated:** 插件内定义的导出内容"""
|
|
||||||
matcher: Set[Type[Matcher]] = field(default_factory=set)
|
matcher: Set[Type[Matcher]] = field(default_factory=set)
|
||||||
"""插件内定义的 `Matcher`"""
|
"""插件内定义的 `Matcher`"""
|
||||||
parent_plugin: Optional["Plugin"] = None
|
parent_plugin: Optional["Plugin"] = None
|
||||||
|
@@ -7,5 +7,5 @@ echo = on_command("echo", to_me())
|
|||||||
|
|
||||||
|
|
||||||
@echo.handle()
|
@echo.handle()
|
||||||
async def echo_escape(message: Message = CommandArg()):
|
async def handle_echo(message: Message = CommandArg()):
|
||||||
await echo.send(message=message)
|
await echo.send(message=message)
|
||||||
|
@@ -15,8 +15,7 @@ async def matcher_mutex(event: Event) -> AsyncGenerator[bool, None]:
|
|||||||
yield result
|
yield result
|
||||||
else:
|
else:
|
||||||
current_event_id = id(event)
|
current_event_id = id(event)
|
||||||
event_id = _running_matcher.get(session_id, None)
|
if event_id := _running_matcher.get(session_id, None):
|
||||||
if event_id:
|
|
||||||
result = event_id != current_event_id
|
result = event_id != current_event_id
|
||||||
else:
|
else:
|
||||||
_running_matcher[session_id] = current_event_id
|
_running_matcher[session_id] = current_event_id
|
||||||
|
315
nonebot/rule.py
315
nonebot/rule.py
@@ -10,11 +10,27 @@ FrontMatter:
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
from itertools import product
|
from argparse import Action
|
||||||
from argparse import Namespace
|
from argparse import ArgumentError
|
||||||
from typing_extensions import TypedDict
|
from itertools import chain, product
|
||||||
|
from argparse import Namespace as Namespace
|
||||||
from argparse import ArgumentParser as ArgParser
|
from argparse import ArgumentParser as ArgParser
|
||||||
from typing import Any, List, Tuple, Union, Optional, Sequence, NamedTuple
|
from typing import (
|
||||||
|
IO,
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
List,
|
||||||
|
Type,
|
||||||
|
Tuple,
|
||||||
|
Union,
|
||||||
|
TypeVar,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
TypedDict,
|
||||||
|
NamedTuple,
|
||||||
|
cast,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
|
|
||||||
from pygtrie import CharTrie
|
from pygtrie import CharTrie
|
||||||
|
|
||||||
@@ -23,15 +39,8 @@ from nonebot.log import logger
|
|||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
from nonebot.exception import ParserExit
|
from nonebot.exception import ParserExit
|
||||||
from nonebot.internal.rule import Rule as Rule
|
from nonebot.internal.rule import Rule as Rule
|
||||||
|
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.params import (
|
|
||||||
Command,
|
|
||||||
EventToMe,
|
|
||||||
EventType,
|
|
||||||
CommandArg,
|
|
||||||
EventMessage,
|
|
||||||
EventPlainText,
|
|
||||||
)
|
|
||||||
from nonebot.consts import (
|
from nonebot.consts import (
|
||||||
CMD_KEY,
|
CMD_KEY,
|
||||||
PREFIX_KEY,
|
PREFIX_KEY,
|
||||||
@@ -45,6 +54,8 @@ from nonebot.consts import (
|
|||||||
REGEX_MATCHED,
|
REGEX_MATCHED,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
CMD_RESULT = TypedDict(
|
CMD_RESULT = TypedDict(
|
||||||
"CMD_RESULT",
|
"CMD_RESULT",
|
||||||
{
|
{
|
||||||
@@ -83,8 +94,7 @@ class TrieRule:
|
|||||||
message_seg: MessageSegment = message[0]
|
message_seg: MessageSegment = message[0]
|
||||||
if message_seg.is_text():
|
if message_seg.is_text():
|
||||||
segment_text = str(message_seg).lstrip()
|
segment_text = str(message_seg).lstrip()
|
||||||
pf = cls.prefix.longest_prefix(segment_text)
|
if pf := cls.prefix.longest_prefix(segment_text):
|
||||||
if pf:
|
|
||||||
value: TRIE_VALUE = pf.value
|
value: TRIE_VALUE = pf.value
|
||||||
prefix[RAW_CMD_KEY] = pf.key
|
prefix[RAW_CMD_KEY] = pf.key
|
||||||
prefix[CMD_START_KEY] = value.command_start
|
prefix[CMD_START_KEY] = value.command_start
|
||||||
@@ -113,10 +123,25 @@ class StartswithRule:
|
|||||||
self.msg = msg
|
self.msg = msg
|
||||||
self.ignorecase = ignorecase
|
self.ignorecase = ignorecase
|
||||||
|
|
||||||
async def __call__(
|
def __repr__(self) -> str:
|
||||||
self, type: str = EventType(), text: str = EventPlainText()
|
return f"Startswith(msg={self.msg}, ignorecase={self.ignorecase})"
|
||||||
) -> Any:
|
|
||||||
if type != "message":
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return (
|
||||||
|
isinstance(other, StartswithRule)
|
||||||
|
and frozenset(self.msg) == frozenset(other.msg)
|
||||||
|
and self.ignorecase == other.ignorecase
|
||||||
|
)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((frozenset(self.msg), self.ignorecase))
|
||||||
|
|
||||||
|
async def __call__(self, event: Event) -> bool:
|
||||||
|
if event.get_type() != "message":
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
text = event.get_plaintext()
|
||||||
|
except Exception:
|
||||||
return False
|
return False
|
||||||
return bool(
|
return bool(
|
||||||
re.match(
|
re.match(
|
||||||
@@ -154,10 +179,25 @@ class EndswithRule:
|
|||||||
self.msg = msg
|
self.msg = msg
|
||||||
self.ignorecase = ignorecase
|
self.ignorecase = ignorecase
|
||||||
|
|
||||||
async def __call__(
|
def __repr__(self) -> str:
|
||||||
self, type: str = EventType(), text: str = EventPlainText()
|
return f"Endswith(msg={self.msg}, ignorecase={self.ignorecase})"
|
||||||
) -> Any:
|
|
||||||
if type != "message":
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return (
|
||||||
|
isinstance(other, EndswithRule)
|
||||||
|
and frozenset(self.msg) == frozenset(other.msg)
|
||||||
|
and self.ignorecase == other.ignorecase
|
||||||
|
)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((frozenset(self.msg), self.ignorecase))
|
||||||
|
|
||||||
|
async def __call__(self, event: Event) -> bool:
|
||||||
|
if event.get_type() != "message":
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
text = event.get_plaintext()
|
||||||
|
except Exception:
|
||||||
return False
|
return False
|
||||||
return bool(
|
return bool(
|
||||||
re.search(
|
re.search(
|
||||||
@@ -192,17 +232,31 @@ class FullmatchRule:
|
|||||||
__slots__ = ("msg", "ignorecase")
|
__slots__ = ("msg", "ignorecase")
|
||||||
|
|
||||||
def __init__(self, msg: Tuple[str, ...], ignorecase: bool = False):
|
def __init__(self, msg: Tuple[str, ...], ignorecase: bool = False):
|
||||||
self.msg = frozenset(map(str.casefold, msg) if ignorecase else msg)
|
self.msg = tuple(map(str.casefold, msg) if ignorecase else msg)
|
||||||
self.ignorecase = ignorecase
|
self.ignorecase = ignorecase
|
||||||
|
|
||||||
async def __call__(
|
def __repr__(self) -> str:
|
||||||
self, type_: str = EventType(), text: str = EventPlainText()
|
return f"Fullmatch(msg={self.msg}, ignorecase={self.ignorecase})"
|
||||||
) -> bool:
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
return (
|
return (
|
||||||
type_ == "message"
|
isinstance(other, FullmatchRule)
|
||||||
and (text.casefold() if self.ignorecase else text) in self.msg
|
and frozenset(self.msg) == frozenset(other.msg)
|
||||||
|
and self.ignorecase == other.ignorecase
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((frozenset(self.msg), self.ignorecase))
|
||||||
|
|
||||||
|
async def __call__(self, event: Event) -> bool:
|
||||||
|
if event.get_type() != "message":
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
text = event.get_plaintext()
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return (text.casefold() if self.ignorecase else text) in self.msg
|
||||||
|
|
||||||
|
|
||||||
def fullmatch(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule:
|
def fullmatch(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule:
|
||||||
"""完全匹配消息。
|
"""完全匹配消息。
|
||||||
@@ -229,10 +283,23 @@ class KeywordsRule:
|
|||||||
def __init__(self, *keywords: str):
|
def __init__(self, *keywords: str):
|
||||||
self.keywords = keywords
|
self.keywords = keywords
|
||||||
|
|
||||||
async def __call__(
|
def __repr__(self) -> str:
|
||||||
self, type: str = EventType(), text: str = EventPlainText()
|
return f"Keywords(keywords={self.keywords})"
|
||||||
) -> bool:
|
|
||||||
if type != "message":
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return isinstance(other, KeywordsRule) and frozenset(
|
||||||
|
self.keywords
|
||||||
|
) == frozenset(other.keywords)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(frozenset(self.keywords))
|
||||||
|
|
||||||
|
async def __call__(self, event: Event) -> bool:
|
||||||
|
if event.get_type() != "message":
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
text = event.get_plaintext()
|
||||||
|
except Exception:
|
||||||
return False
|
return False
|
||||||
return bool(text and any(keyword in text for keyword in self.keywords))
|
return bool(text and any(keyword in text for keyword in self.keywords))
|
||||||
|
|
||||||
@@ -257,14 +324,22 @@ class CommandRule:
|
|||||||
__slots__ = ("cmds",)
|
__slots__ = ("cmds",)
|
||||||
|
|
||||||
def __init__(self, cmds: List[Tuple[str, ...]]):
|
def __init__(self, cmds: List[Tuple[str, ...]]):
|
||||||
self.cmds = cmds
|
self.cmds = tuple(cmds)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"Command(cmds={self.cmds})"
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return isinstance(other, CommandRule) and frozenset(self.cmds) == frozenset(
|
||||||
|
other.cmds
|
||||||
|
)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((frozenset(self.cmds),))
|
||||||
|
|
||||||
async def __call__(self, cmd: Optional[Tuple[str, ...]] = Command()) -> bool:
|
async def __call__(self, cmd: Optional[Tuple[str, ...]] = Command()) -> bool:
|
||||||
return cmd in self.cmds
|
return cmd in self.cmds
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"<Command {self.cmds}>"
|
|
||||||
|
|
||||||
|
|
||||||
def command(*cmds: Union[str, Tuple[str, ...]]) -> Rule:
|
def command(*cmds: Union[str, Tuple[str, ...]]) -> Rule:
|
||||||
"""匹配消息命令。
|
"""匹配消息命令。
|
||||||
@@ -320,25 +395,48 @@ class ArgumentParser(ArgParser):
|
|||||||
参考文档: [argparse](https://docs.python.org/3/library/argparse.html)
|
参考文档: [argparse](https://docs.python.org/3/library/argparse.html)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _print_message(self, message, file=None):
|
if TYPE_CHECKING:
|
||||||
old_message: str = getattr(self, "message", "")
|
|
||||||
if old_message:
|
|
||||||
old_message += "\n"
|
|
||||||
old_message += message
|
|
||||||
setattr(self, "message", old_message)
|
|
||||||
|
|
||||||
def exit(self, status: int = 0, message: Optional[str] = None):
|
@overload
|
||||||
raise ParserExit(
|
def parse_args(
|
||||||
status=status, message=message or getattr(self, "message", None)
|
self, args: Optional[Sequence[Union[str, MessageSegment]]] = ...
|
||||||
|
) -> Namespace:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def parse_args(
|
||||||
|
self, args: Optional[Sequence[Union[str, MessageSegment]]], namespace: None
|
||||||
|
) -> Namespace:
|
||||||
|
... # type: ignore[misc]
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def parse_args(
|
||||||
|
self, args: Optional[Sequence[Union[str, MessageSegment]]], namespace: T
|
||||||
|
) -> T:
|
||||||
|
...
|
||||||
|
|
||||||
|
def parse_args(
|
||||||
|
self,
|
||||||
|
args: Optional[Sequence[Union[str, MessageSegment]]] = None,
|
||||||
|
namespace: Optional[T] = None,
|
||||||
|
) -> Union[Namespace, T]:
|
||||||
|
...
|
||||||
|
|
||||||
|
def _parse_optional(
|
||||||
|
self, arg_string: Union[str, MessageSegment]
|
||||||
|
) -> Optional[Tuple[Optional[Action], str, Optional[str]]]:
|
||||||
|
return (
|
||||||
|
super()._parse_optional(arg_string) if isinstance(arg_string, str) else None
|
||||||
)
|
)
|
||||||
|
|
||||||
def parse_args(
|
def _print_message(self, message: str, file: Optional[IO[str]] = None):
|
||||||
self,
|
if message:
|
||||||
args: Optional[Sequence[str]] = None,
|
setattr(self, "_message", getattr(self, "_message", "") + message)
|
||||||
namespace: Optional[Namespace] = None,
|
|
||||||
) -> Namespace:
|
def exit(self, status: int = 0, message: Optional[str] = None):
|
||||||
setattr(self, "message", "")
|
if message:
|
||||||
return super().parse_args(args=args, namespace=namespace) # type: ignore
|
self._print_message(message)
|
||||||
|
raise ParserExit(status=status, message=getattr(self, "_message", None))
|
||||||
|
|
||||||
|
|
||||||
class ShellCommandRule:
|
class ShellCommandRule:
|
||||||
@@ -352,28 +450,48 @@ class ShellCommandRule:
|
|||||||
__slots__ = ("cmds", "parser")
|
__slots__ = ("cmds", "parser")
|
||||||
|
|
||||||
def __init__(self, cmds: List[Tuple[str, ...]], parser: Optional[ArgumentParser]):
|
def __init__(self, cmds: List[Tuple[str, ...]], parser: Optional[ArgumentParser]):
|
||||||
self.cmds = cmds
|
self.cmds = tuple(cmds)
|
||||||
self.parser = parser
|
self.parser = parser
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"ShellCommand(cmds={self.cmds}, parser={self.parser})"
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return (
|
||||||
|
isinstance(other, ShellCommandRule)
|
||||||
|
and frozenset(self.cmds) == frozenset(other.cmds)
|
||||||
|
and self.parser is other.parser
|
||||||
|
)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((frozenset(self.cmds), self.parser))
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
state: T_State,
|
state: T_State,
|
||||||
cmd: Optional[Tuple[str, ...]] = Command(),
|
cmd: Optional[Tuple[str, ...]] = Command(),
|
||||||
msg: Optional[Message] = CommandArg(),
|
msg: Optional[Message] = CommandArg(),
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if cmd in self.cmds and msg is not None:
|
if cmd not in self.cmds or msg is None:
|
||||||
message = str(msg)
|
|
||||||
state[SHELL_ARGV] = shlex.split(message)
|
|
||||||
if self.parser:
|
|
||||||
try:
|
|
||||||
args = self.parser.parse_args(state[SHELL_ARGV])
|
|
||||||
state[SHELL_ARGS] = args
|
|
||||||
except ParserExit as e:
|
|
||||||
state[SHELL_ARGS] = e
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
state[SHELL_ARGV] = list(
|
||||||
|
chain.from_iterable(
|
||||||
|
shlex.split(str(seg)) if cast(MessageSegment, seg).is_text() else (seg,)
|
||||||
|
for seg in msg
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.parser:
|
||||||
|
try:
|
||||||
|
args = self.parser.parse_args(state[SHELL_ARGV])
|
||||||
|
state[SHELL_ARGS] = args
|
||||||
|
except ArgumentError as e:
|
||||||
|
state[SHELL_ARGS] = ParserExit(status=2, message=str(e))
|
||||||
|
except ParserExit as e:
|
||||||
|
state[SHELL_ARGS] = e
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def shell_command(
|
def shell_command(
|
||||||
*cmds: Union[str, Tuple[str, ...]], parser: Optional[ArgumentParser] = None
|
*cmds: Union[str, Tuple[str, ...]], parser: Optional[ArgumentParser] = None
|
||||||
@@ -452,16 +570,27 @@ class RegexRule:
|
|||||||
self.regex = regex
|
self.regex = regex
|
||||||
self.flags = flags
|
self.flags = flags
|
||||||
|
|
||||||
async def __call__(
|
def __repr__(self) -> str:
|
||||||
self,
|
return f"Regex(regex={self.regex!r}, flags={self.flags})"
|
||||||
state: T_State,
|
|
||||||
type: str = EventType(),
|
def __eq__(self, other: object) -> bool:
|
||||||
msg: Message = EventMessage(),
|
return (
|
||||||
) -> bool:
|
isinstance(other, RegexRule)
|
||||||
if type != "message":
|
and self.regex == other.regex
|
||||||
|
and self.flags == other.flags
|
||||||
|
)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((self.regex, self.flags))
|
||||||
|
|
||||||
|
async def __call__(self, event: Event, state: T_State) -> bool:
|
||||||
|
if event.get_type() != "message":
|
||||||
return False
|
return False
|
||||||
matched = re.search(self.regex, str(msg), self.flags)
|
try:
|
||||||
if matched:
|
msg = event.get_message()
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
if matched := re.search(self.regex, str(msg), self.flags):
|
||||||
state[REGEX_MATCHED] = matched.group()
|
state[REGEX_MATCHED] = matched.group()
|
||||||
state[REGEX_GROUP] = matched.groups()
|
state[REGEX_GROUP] = matched.groups()
|
||||||
state[REGEX_DICT] = matched.groupdict()
|
state[REGEX_DICT] = matched.groupdict()
|
||||||
@@ -498,6 +627,15 @@ class ToMeRule:
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "ToMe()"
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return isinstance(other, ToMeRule)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((self.__class__,))
|
||||||
|
|
||||||
async def __call__(self, to_me: bool = EventToMe()) -> bool:
|
async def __call__(self, to_me: bool = EventToMe()) -> bool:
|
||||||
return to_me
|
return to_me
|
||||||
|
|
||||||
@@ -508,6 +646,37 @@ def to_me() -> Rule:
|
|||||||
return Rule(ToMeRule())
|
return Rule(ToMeRule())
|
||||||
|
|
||||||
|
|
||||||
|
class IsTypeRule:
|
||||||
|
"""检查事件类型是否为指定类型。"""
|
||||||
|
|
||||||
|
__slots__ = ("types",)
|
||||||
|
|
||||||
|
def __init__(self, *types: Type[Event]):
|
||||||
|
self.types = types
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"IsType(types={tuple(type.__name__ for type in self.types)})"
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
return isinstance(other, IsTypeRule) and self.types == other.types
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash((self.types,))
|
||||||
|
|
||||||
|
async def __call__(self, event: Event) -> bool:
|
||||||
|
return isinstance(event, self.types)
|
||||||
|
|
||||||
|
|
||||||
|
def is_type(*types: Type[Event]) -> Rule:
|
||||||
|
"""匹配事件类型。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
types: 事件类型
|
||||||
|
"""
|
||||||
|
|
||||||
|
return Rule(IsTypeRule(*types))
|
||||||
|
|
||||||
|
|
||||||
__autodoc__ = {
|
__autodoc__ = {
|
||||||
"Rule": True,
|
"Rule": True,
|
||||||
"Rule.__call__": True,
|
"Rule.__call__": True,
|
||||||
|
@@ -11,6 +11,7 @@ FrontMatter:
|
|||||||
sidebar_position: 11
|
sidebar_position: 11
|
||||||
description: nonebot.typing 模块
|
description: nonebot.typing 模块
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Any,
|
Any,
|
||||||
@@ -28,6 +29,8 @@ if TYPE_CHECKING:
|
|||||||
from nonebot.adapters import Bot
|
from nonebot.adapters import Bot
|
||||||
from nonebot.permission import Permission
|
from nonebot.permission import Permission
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
T_Wrapped = TypeVar("T_Wrapped", bound=Callable)
|
T_Wrapped = TypeVar("T_Wrapped", bound=Callable)
|
||||||
|
|
||||||
|
|
||||||
@@ -41,10 +44,14 @@ def overrides(InterfaceClass: object) -> Callable[[T_Wrapped], T_Wrapped]:
|
|||||||
return overrider
|
return overrider
|
||||||
|
|
||||||
|
|
||||||
|
# state
|
||||||
T_State = Dict[Any, Any]
|
T_State = Dict[Any, Any]
|
||||||
"""事件处理状态 State 类型"""
|
"""事件处理状态 State 类型"""
|
||||||
|
|
||||||
T_BotConnectionHook = Callable[..., Awaitable[Any]]
|
_DependentCallable = Union[Callable[..., T], Callable[..., Awaitable[T]]]
|
||||||
|
|
||||||
|
# driver hooks
|
||||||
|
T_BotConnectionHook = _DependentCallable[Any]
|
||||||
"""Bot 连接建立时钩子函数
|
"""Bot 连接建立时钩子函数
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -53,7 +60,7 @@ T_BotConnectionHook = Callable[..., Awaitable[Any]]
|
|||||||
- BotParam: Bot 对象
|
- BotParam: Bot 对象
|
||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
T_BotDisconnectionHook = Callable[..., Awaitable[Any]]
|
T_BotDisconnectionHook = _DependentCallable[Any]
|
||||||
"""Bot 连接断开时钩子函数
|
"""Bot 连接断开时钩子函数
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -62,6 +69,8 @@ T_BotDisconnectionHook = Callable[..., Awaitable[Any]]
|
|||||||
- BotParam: Bot 对象
|
- BotParam: Bot 对象
|
||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# api hooks
|
||||||
T_CallingAPIHook = Callable[["Bot", str, Dict[str, Any]], Awaitable[Any]]
|
T_CallingAPIHook = Callable[["Bot", str, Dict[str, Any]], Awaitable[Any]]
|
||||||
"""`bot.call_api` 钩子函数"""
|
"""`bot.call_api` 钩子函数"""
|
||||||
T_CalledAPIHook = Callable[
|
T_CalledAPIHook = Callable[
|
||||||
@@ -69,7 +78,8 @@ T_CalledAPIHook = Callable[
|
|||||||
]
|
]
|
||||||
"""`bot.call_api` 后执行的函数,参数分别为 bot, exception, api, data, result"""
|
"""`bot.call_api` 后执行的函数,参数分别为 bot, exception, api, data, result"""
|
||||||
|
|
||||||
T_EventPreProcessor = Callable[..., Union[Any, Awaitable[Any]]]
|
# event hooks
|
||||||
|
T_EventPreProcessor = _DependentCallable[Any]
|
||||||
"""事件预处理函数 EventPreProcessor 类型
|
"""事件预处理函数 EventPreProcessor 类型
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -80,7 +90,7 @@ T_EventPreProcessor = Callable[..., Union[Any, Awaitable[Any]]]
|
|||||||
- StateParam: State 对象
|
- StateParam: State 对象
|
||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
T_EventPostProcessor = Callable[..., Union[Any, Awaitable[Any]]]
|
T_EventPostProcessor = _DependentCallable[Any]
|
||||||
"""事件预处理函数 EventPostProcessor 类型
|
"""事件预处理函数 EventPostProcessor 类型
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -91,7 +101,9 @@ T_EventPostProcessor = Callable[..., Union[Any, Awaitable[Any]]]
|
|||||||
- StateParam: State 对象
|
- StateParam: State 对象
|
||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
T_RunPreProcessor = Callable[..., Union[Any, Awaitable[Any]]]
|
|
||||||
|
# matcher run hooks
|
||||||
|
T_RunPreProcessor = _DependentCallable[Any]
|
||||||
"""事件响应器运行前预处理函数 RunPreProcessor 类型
|
"""事件响应器运行前预处理函数 RunPreProcessor 类型
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -103,7 +115,7 @@ T_RunPreProcessor = Callable[..., Union[Any, Awaitable[Any]]]
|
|||||||
- MatcherParam: Matcher 对象
|
- MatcherParam: Matcher 对象
|
||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
T_RunPostProcessor = Callable[..., Union[Any, Awaitable[Any]]]
|
T_RunPostProcessor = _DependentCallable[Any]
|
||||||
"""事件响应器运行后后处理函数 RunPostProcessor 类型
|
"""事件响应器运行后后处理函数 RunPostProcessor 类型
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -117,7 +129,8 @@ T_RunPostProcessor = Callable[..., Union[Any, Awaitable[Any]]]
|
|||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
|
|
||||||
T_RuleChecker = Callable[..., Union[bool, Awaitable[bool]]]
|
# rule, permission
|
||||||
|
T_RuleChecker = _DependentCallable[bool]
|
||||||
"""RuleChecker 即判断是否响应事件的处理函数。
|
"""RuleChecker 即判断是否响应事件的处理函数。
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -128,7 +141,7 @@ T_RuleChecker = Callable[..., Union[bool, Awaitable[bool]]]
|
|||||||
- StateParam: State 对象
|
- StateParam: State 对象
|
||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
T_PermissionChecker = Callable[..., Union[bool, Awaitable[bool]]]
|
T_PermissionChecker = _DependentCallable[bool]
|
||||||
"""PermissionChecker 即判断事件是否满足权限的处理函数。
|
"""PermissionChecker 即判断事件是否满足权限的处理函数。
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -139,9 +152,9 @@ T_PermissionChecker = Callable[..., Union[bool, Awaitable[bool]]]
|
|||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
|
|
||||||
T_Handler = Callable[..., Any]
|
T_Handler = _DependentCallable[Any]
|
||||||
"""Handler 处理函数。"""
|
"""Handler 处理函数。"""
|
||||||
T_TypeUpdater = Callable[..., Union[str, Awaitable[str]]]
|
T_TypeUpdater = _DependentCallable[str]
|
||||||
"""TypeUpdater 在 Matcher.pause, Matcher.reject 时被运行,用于更新响应的事件类型。默认会更新为 `message`。
|
"""TypeUpdater 在 Matcher.pause, Matcher.reject 时被运行,用于更新响应的事件类型。默认会更新为 `message`。
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -153,7 +166,7 @@ T_TypeUpdater = Callable[..., Union[str, Awaitable[str]]]
|
|||||||
- MatcherParam: Matcher 对象
|
- MatcherParam: Matcher 对象
|
||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
T_PermissionUpdater = Callable[..., Union["Permission", Awaitable["Permission"]]]
|
T_PermissionUpdater = _DependentCallable["Permission"]
|
||||||
"""PermissionUpdater 在 Matcher.pause, Matcher.reject 时被运行,用于更新会话对象权限。默认会更新为当前事件的触发对象。
|
"""PermissionUpdater 在 Matcher.pause, Matcher.reject 时被运行,用于更新会话对象权限。默认会更新为当前事件的触发对象。
|
||||||
|
|
||||||
依赖参数:
|
依赖参数:
|
||||||
@@ -165,5 +178,5 @@ T_PermissionUpdater = Callable[..., Union["Permission", Awaitable["Permission"]]
|
|||||||
- MatcherParam: Matcher 对象
|
- MatcherParam: Matcher 对象
|
||||||
- DefaultParam: 带有默认值的参数
|
- DefaultParam: 带有默认值的参数
|
||||||
"""
|
"""
|
||||||
T_DependencyCache = Dict[Callable[..., Any], "Task[Any]"]
|
T_DependencyCache = Dict[_DependentCallable[Any], "Task[Any]"]
|
||||||
"""依赖缓存, 用于存储依赖函数的返回值"""
|
"""依赖缓存, 用于存储依赖函数的返回值"""
|
||||||
|
@@ -10,6 +10,7 @@ import json
|
|||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
from pathlib import Path
|
||||||
from functools import wraps, partial
|
from functools import wraps, partial
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from typing_extensions import ParamSpec, get_args, get_origin
|
from typing_extensions import ParamSpec, get_args, get_origin
|
||||||
@@ -24,6 +25,7 @@ from typing import (
|
|||||||
Coroutine,
|
Coroutine,
|
||||||
AsyncGenerator,
|
AsyncGenerator,
|
||||||
ContextManager,
|
ContextManager,
|
||||||
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
from pydantic.typing import is_union, is_none_type
|
from pydantic.typing import is_union, is_none_type
|
||||||
@@ -129,11 +131,28 @@ async def run_sync_ctx_manager(
|
|||||||
await run_sync(cm.__exit__)(None, None, None)
|
await run_sync(cm.__exit__)(None, None, None)
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
async def run_coro_with_catch(
|
async def run_coro_with_catch(
|
||||||
coro: Coroutine[Any, Any, T],
|
coro: Coroutine[Any, Any, T],
|
||||||
exc: Tuple[Type[Exception], ...],
|
exc: Tuple[Type[Exception], ...],
|
||||||
return_on_err: R = None,
|
) -> Union[T, None]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def run_coro_with_catch(
|
||||||
|
coro: Coroutine[Any, Any, T],
|
||||||
|
exc: Tuple[Type[Exception], ...],
|
||||||
|
return_on_err: R,
|
||||||
) -> Union[T, R]:
|
) -> Union[T, R]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def run_coro_with_catch(
|
||||||
|
coro: Coroutine[Any, Any, T],
|
||||||
|
exc: Tuple[Type[Exception], ...],
|
||||||
|
return_on_err: Optional[R] = None,
|
||||||
|
) -> Optional[Union[T, R]]:
|
||||||
try:
|
try:
|
||||||
return await coro
|
return await coro
|
||||||
except exc:
|
except exc:
|
||||||
@@ -147,6 +166,14 @@ def get_name(obj: Any) -> str:
|
|||||||
return obj.__class__.__name__
|
return obj.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
|
def path_to_module_name(path: Path) -> str:
|
||||||
|
rel_path = path.resolve().relative_to(Path(".").resolve())
|
||||||
|
if rel_path.stem == "__init__":
|
||||||
|
return ".".join(rel_path.parts[:-1])
|
||||||
|
else:
|
||||||
|
return ".".join(rel_path.parts[:-1] + (rel_path.stem,))
|
||||||
|
|
||||||
|
|
||||||
class DataclassEncoder(json.JSONEncoder):
|
class DataclassEncoder(json.JSONEncoder):
|
||||||
"""在JSON序列化 {re}`nonebot.adapters._message.Message` (List[Dataclass]) 时使用的 `JSONEncoder`"""
|
"""在JSON序列化 {re}`nonebot.adapters._message.Message` (List[Dataclass]) 时使用的 `JSONEncoder`"""
|
||||||
|
|
||||||
@@ -173,7 +200,7 @@ def logger_wrapper(logger_name: str):
|
|||||||
|
|
||||||
def log(level: str, message: str, exception: Optional[Exception] = None):
|
def log(level: str, message: str, exception: Optional[Exception] = None):
|
||||||
logger.opt(colors=True, exception=exception).log(
|
logger.opt(colors=True, exception=exception).log(
|
||||||
level, f"<m>{escape_tag(logger_name)}</m> | " + message
|
level, f"<m>{escape_tag(logger_name)}</m> | {message}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return log
|
return log
|
||||||
|
@@ -17,7 +17,7 @@ _✨ NoneBot 本地文档插件 ✨_
|
|||||||
<a href="https://pypi.python.org/pypi/nonebot-plugin-docs">
|
<a href="https://pypi.python.org/pypi/nonebot-plugin-docs">
|
||||||
<img src="https://img.shields.io/pypi/v/nonebot-plugin-docs.svg" alt="pypi">
|
<img src="https://img.shields.io/pypi/v/nonebot-plugin-docs.svg" alt="pypi">
|
||||||
</a>
|
</a>
|
||||||
<img src="https://img.shields.io/badge/python-3.7+-blue.svg" alt="python">
|
<img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="python">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 使用方式
|
## 使用方式
|
||||||
|
@@ -12,7 +12,7 @@ include = ["nonebot_plugin_docs/dist/**/*"]
|
|||||||
|
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.7.3"
|
python = "^3.8"
|
||||||
nonebot2 = "^2.0.0-beta.1"
|
nonebot2 = "^2.0.0-beta.1"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
|
470
poetry.lock
generated
470
poetry.lock
generated
@@ -11,15 +11,15 @@ pycares = ">=4.0.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiofiles"
|
name = "aiofiles"
|
||||||
version = "0.8.0"
|
version = "22.1.0"
|
||||||
description = "File support for asyncio."
|
description = "File support for asyncio."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = true
|
optional = true
|
||||||
python-versions = ">=3.6,<4.0"
|
python-versions = ">=3.7,<4.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
version = "3.8.1"
|
version = "3.8.3"
|
||||||
description = "Async http client/server framework (asyncio)"
|
description = "Async http client/server framework (asyncio)"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = true
|
optional = true
|
||||||
@@ -29,14 +29,12 @@ python-versions = ">=3.6"
|
|||||||
aiodns = {version = "*", optional = true, markers = "extra == \"speedups\""}
|
aiodns = {version = "*", optional = true, markers = "extra == \"speedups\""}
|
||||||
aiosignal = ">=1.1.2"
|
aiosignal = ">=1.1.2"
|
||||||
async-timeout = ">=4.0.0a3,<5.0"
|
async-timeout = ">=4.0.0a3,<5.0"
|
||||||
asynctest = {version = "0.13.0", markers = "python_version < \"3.8\""}
|
|
||||||
attrs = ">=17.3.0"
|
attrs = ">=17.3.0"
|
||||||
Brotli = {version = "*", optional = true, markers = "extra == \"speedups\""}
|
Brotli = {version = "*", optional = true, markers = "extra == \"speedups\""}
|
||||||
cchardet = {version = "*", optional = true, markers = "extra == \"speedups\""}
|
cchardet = {version = "*", optional = true, markers = "python_version < \"3.10\" and extra == \"speedups\""}
|
||||||
charset-normalizer = ">=2.0,<3.0"
|
charset-normalizer = ">=2.0,<3.0"
|
||||||
frozenlist = ">=1.1.1"
|
frozenlist = ">=1.1.1"
|
||||||
multidict = ">=4.5,<7.0"
|
multidict = ">=4.5,<7.0"
|
||||||
typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
|
|
||||||
yarl = ">=1.0,<2.0"
|
yarl = ">=1.0,<2.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
@@ -64,7 +62,6 @@ python-versions = ">=3.6.2"
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
idna = ">=2.8"
|
idna = ">=2.8"
|
||||||
sniffio = ">=1.1"
|
sniffio = ">=1.1"
|
||||||
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
|
doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
|
||||||
@@ -79,9 +76,6 @@ category = "dev"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
|
tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
|
||||||
|
|
||||||
@@ -105,25 +99,6 @@ category = "main"
|
|||||||
optional = true
|
optional = true
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""}
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "asynctest"
|
|
||||||
version = "0.13.0"
|
|
||||||
description = "Enhance the standard unittest package with features for testing asyncio libraries"
|
|
||||||
category = "main"
|
|
||||||
optional = true
|
|
||||||
python-versions = ">=3.5"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atomicwrites"
|
|
||||||
version = "1.4.1"
|
|
||||||
description = "Atomic file writes."
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "attrs"
|
name = "attrs"
|
||||||
version = "21.4.0"
|
version = "21.4.0"
|
||||||
@@ -140,7 +115,7 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "black"
|
name = "black"
|
||||||
version = "22.6.0"
|
version = "22.8.0"
|
||||||
description = "The uncompromising code formatter."
|
description = "The uncompromising code formatter."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -152,7 +127,6 @@ mypy-extensions = ">=0.4.3"
|
|||||||
pathspec = ">=0.9.0"
|
pathspec = ">=0.9.0"
|
||||||
platformdirs = ">=2"
|
platformdirs = ">=2"
|
||||||
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
|
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
|
||||||
typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""}
|
|
||||||
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
|
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
@@ -187,7 +161,7 @@ python-versions = "*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2022.6.15"
|
version = "2022.9.24"
|
||||||
description = "Python package for providing Mozilla's CA Bundle."
|
description = "Python package for providing Mozilla's CA Bundle."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -214,7 +188,7 @@ python-versions = ">=3.6.1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "charset-normalizer"
|
name = "charset-normalizer"
|
||||||
version = "2.1.0"
|
version = "2.1.1"
|
||||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -233,7 +207,6 @@ python-versions = ">=3.7"
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
@@ -245,7 +218,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "coverage"
|
name = "coverage"
|
||||||
version = "6.4.2"
|
version = "6.5.0"
|
||||||
description = "Code coverage measurement for Python"
|
description = "Code coverage measurement for Python"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -259,7 +232,7 @@ toml = ["tomli"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "distlib"
|
name = "distlib"
|
||||||
version = "0.3.5"
|
version = "0.3.6"
|
||||||
description = "Distribution utilities"
|
description = "Distribution utilities"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -278,7 +251,7 @@ testing = ["pre-commit"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastapi"
|
name = "fastapi"
|
||||||
version = "0.79.0"
|
version = "0.79.1"
|
||||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -296,15 +269,15 @@ test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.91
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filelock"
|
name = "filelock"
|
||||||
version = "3.7.1"
|
version = "3.8.0"
|
||||||
description = "A platform independent file lock."
|
description = "A platform independent file lock."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"]
|
docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"]
|
||||||
testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"]
|
testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "frozenlist"
|
name = "frozenlist"
|
||||||
@@ -362,7 +335,7 @@ socks = ["socksio (>=1.0.0,<2.0.0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httptools"
|
name = "httptools"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
description = "A collection of framework independent HTTP protocol utils."
|
description = "A collection of framework independent HTTP protocol utils."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -394,7 +367,7 @@ socks = ["socksio (>=1.0.0,<2.0.0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hypercorn"
|
name = "hypercorn"
|
||||||
version = "0.13.2"
|
version = "0.14.3"
|
||||||
description = "A ASGI Server based on Hyper libraries and inspired by Gunicorn"
|
description = "A ASGI Server based on Hyper libraries and inspired by Gunicorn"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = true
|
optional = true
|
||||||
@@ -405,11 +378,11 @@ h11 = "*"
|
|||||||
h2 = ">=3.1.0"
|
h2 = ">=3.1.0"
|
||||||
priority = "*"
|
priority = "*"
|
||||||
toml = "*"
|
toml = "*"
|
||||||
typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
|
|
||||||
wsproto = ">=0.14.0"
|
wsproto = ">=0.14.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
h3 = ["aioquic (>=0.9.0,<1.0)"]
|
h3 = ["aioquic (>=0.9.0,<1.0)"]
|
||||||
|
docs = ["pydata-sphinx-theme"]
|
||||||
trio = ["trio (>=0.11.0)"]
|
trio = ["trio (>=0.11.0)"]
|
||||||
uvloop = ["uvloop"]
|
uvloop = ["uvloop"]
|
||||||
|
|
||||||
@@ -423,7 +396,7 @@ python-versions = ">=3.6.1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "identify"
|
name = "identify"
|
||||||
version = "2.5.3"
|
version = "2.5.5"
|
||||||
description = "File identification library for Python"
|
description = "File identification library for Python"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -434,29 +407,12 @@ license = ["ukkonen"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.3"
|
version = "3.4"
|
||||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "importlib-metadata"
|
|
||||||
version = "4.12.0"
|
|
||||||
description = "Read metadata from Python packages"
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
|
|
||||||
zipp = ">=0.5"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
|
|
||||||
perf = ["ipython"]
|
|
||||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
@@ -592,7 +548,7 @@ resolved_reference = "3cb2474a22bd1a633805dfa6edbe741e44ddc851"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nonemoji"
|
name = "nonemoji"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
description = "Simple gitmoji cli written in python"
|
description = "Simple gitmoji cli written in python"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -625,11 +581,11 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pathspec"
|
name = "pathspec"
|
||||||
version = "0.9.0"
|
version = "0.10.1"
|
||||||
description = "Utility library for gitignore style pattern matching of file paths."
|
description = "Utility library for gitignore style pattern matching of file paths."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "platformdirs"
|
name = "platformdirs"
|
||||||
@@ -651,9 +607,6 @@ category = "dev"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["pre-commit", "tox"]
|
dev = ["pre-commit", "tox"]
|
||||||
testing = ["pytest", "pytest-benchmark"]
|
testing = ["pytest", "pytest-benchmark"]
|
||||||
@@ -669,7 +622,6 @@ python-versions = ">=3.7"
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
cfgv = ">=2.0.0"
|
cfgv = ">=2.0.0"
|
||||||
identify = ">=1.0.0"
|
identify = ">=1.0.0"
|
||||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
|
||||||
nodeenv = ">=0.11.1"
|
nodeenv = ">=0.11.1"
|
||||||
pyyaml = ">=5.1"
|
pyyaml = ">=5.1"
|
||||||
toml = "*"
|
toml = "*"
|
||||||
@@ -685,7 +637,7 @@ python-versions = ">=3.6.1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prompt-toolkit"
|
name = "prompt-toolkit"
|
||||||
version = "3.0.30"
|
version = "3.0.31"
|
||||||
description = "Library for building powerful interactive command lines in Python"
|
description = "Library for building powerful interactive command lines in Python"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -704,7 +656,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pycares"
|
name = "pycares"
|
||||||
version = "4.2.1"
|
version = "4.2.2"
|
||||||
description = "Python interface for c-ares"
|
description = "Python interface for c-ares"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = true
|
optional = true
|
||||||
@@ -726,7 +678,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic"
|
name = "pydantic"
|
||||||
version = "1.9.1"
|
version = "1.9.2"
|
||||||
description = "Data validation and settings management using python type hints"
|
description = "Data validation and settings management using python type hints"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -761,17 +713,15 @@ diagrams = ["railroad-diagrams", "jinja2"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "7.1.2"
|
version = "7.1.3"
|
||||||
description = "pytest: simple powerful testing with Python"
|
description = "pytest: simple powerful testing with Python"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
|
||||||
attrs = ">=19.2.0"
|
attrs = ">=19.2.0"
|
||||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
|
||||||
iniconfig = "*"
|
iniconfig = "*"
|
||||||
packaging = "*"
|
packaging = "*"
|
||||||
pluggy = ">=0.12,<2.0"
|
pluggy = ">=0.12,<2.0"
|
||||||
@@ -791,7 +741,6 @@ python-versions = ">=3.7"
|
|||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pytest = ">=6.1.0"
|
pytest = ">=6.1.0"
|
||||||
typing-extensions = {version = ">=3.7.2", markers = "python_version < \"3.8\""}
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)", "flaky (>=3.5.0)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
|
testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)", "flaky (>=3.5.0)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
|
||||||
@@ -843,11 +792,11 @@ testing = ["filelock"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dotenv"
|
name = "python-dotenv"
|
||||||
version = "0.20.0"
|
version = "0.21.0"
|
||||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
cli = ["click (>=5.0)"]
|
cli = ["click (>=5.0)"]
|
||||||
@@ -873,12 +822,10 @@ aiofiles = "*"
|
|||||||
blinker = "*"
|
blinker = "*"
|
||||||
click = "*"
|
click = "*"
|
||||||
hypercorn = ">=0.11.2"
|
hypercorn = ">=0.11.2"
|
||||||
importlib_metadata = {version = "*", markers = "python_version < \"3.8\""}
|
|
||||||
itsdangerous = "*"
|
itsdangerous = "*"
|
||||||
jinja2 = "*"
|
jinja2 = "*"
|
||||||
markupsafe = "*"
|
markupsafe = "*"
|
||||||
toml = "*"
|
toml = "*"
|
||||||
typing_extensions = {version = "*", markers = "python_version < \"3.8\""}
|
|
||||||
werkzeug = ">=2.0.0"
|
werkzeug = ">=2.0.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
@@ -926,11 +873,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sniffio"
|
name = "sniffio"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
description = "Sniff out which async library your code is running under"
|
description = "Sniff out which async library your code is running under"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "starlette"
|
name = "starlette"
|
||||||
@@ -965,20 +912,12 @@ python-versions = ">=3.7"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tomlkit"
|
name = "tomlkit"
|
||||||
version = "0.11.1"
|
version = "0.11.5"
|
||||||
description = "Style preserving TOML library"
|
description = "Style preserving TOML library"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6,<4.0"
|
python-versions = ">=3.6,<4.0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typed-ast"
|
|
||||||
version = "1.5.4"
|
|
||||||
description = "a fork of Python 2 and 3 ast modules with type comment support"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.3.0"
|
version = "4.3.0"
|
||||||
@@ -989,7 +928,7 @@ python-versions = ">=3.7"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "1.26.11"
|
version = "1.26.12"
|
||||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -997,12 +936,12 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*,
|
|||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
|
brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
|
||||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"]
|
||||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uvicorn"
|
name = "uvicorn"
|
||||||
version = "0.18.2"
|
version = "0.18.3"
|
||||||
description = "The lightning-fast ASGI server."
|
description = "The lightning-fast ASGI server."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -1014,49 +953,47 @@ colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win
|
|||||||
h11 = ">=0.8"
|
h11 = ">=0.8"
|
||||||
httptools = {version = ">=0.4.0", optional = true, markers = "extra == \"standard\""}
|
httptools = {version = ">=0.4.0", optional = true, markers = "extra == \"standard\""}
|
||||||
python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
|
python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
|
||||||
PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"standard\""}
|
pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""}
|
||||||
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
|
|
||||||
uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
|
uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""}
|
||||||
watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
|
watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""}
|
||||||
websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""}
|
websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""}
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
standard = ["websockets (>=10.0)", "httptools (>=0.4.0)", "watchfiles (>=0.13)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
|
standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uvloop"
|
name = "uvloop"
|
||||||
version = "0.16.0"
|
version = "0.17.0"
|
||||||
description = "Fast implementation of asyncio event loop on top of libuv"
|
description = "Fast implementation of asyncio event loop on top of libuv"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
|
dev = ["Cython (>=0.29.32,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=22.0.0,<22.1.0)", "mypy (>=0.800)", "aiohttp"]
|
||||||
docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"]
|
docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"]
|
||||||
test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
|
test = ["flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=22.0.0,<22.1.0)", "mypy (>=0.800)", "Cython (>=0.29.32,<0.30.0)", "aiohttp"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtualenv"
|
name = "virtualenv"
|
||||||
version = "20.16.2"
|
version = "20.16.5"
|
||||||
description = "Virtual Python Environment builder"
|
description = "Virtual Python Environment builder"
|
||||||
category = "dev"
|
category = "dev"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
distlib = ">=0.3.1,<1"
|
distlib = ">=0.3.5,<1"
|
||||||
filelock = ">=3.2,<4"
|
filelock = ">=3.4.1,<4"
|
||||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
platformdirs = ">=2.4,<3"
|
||||||
platformdirs = ">=2,<3"
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"]
|
docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"]
|
||||||
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"]
|
testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "watchfiles"
|
name = "watchfiles"
|
||||||
version = "0.16.1"
|
version = "0.17.0"
|
||||||
description = "Simple, modern and high performance file watching and code reload in python."
|
description = "Simple, modern and high performance file watching and code reload in python."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
@@ -1083,7 +1020,7 @@ python-versions = ">=3.7"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "werkzeug"
|
name = "werkzeug"
|
||||||
version = "2.2.1"
|
version = "2.2.2"
|
||||||
description = "The comprehensive WSGI web application library."
|
description = "The comprehensive WSGI web application library."
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = true
|
optional = true
|
||||||
@@ -1108,7 +1045,7 @@ dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wsproto"
|
name = "wsproto"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
description = "WebSockets state-machine based protocol implementation"
|
description = "WebSockets state-machine based protocol implementation"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = true
|
optional = true
|
||||||
@@ -1128,19 +1065,6 @@ python-versions = ">=3.7"
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
idna = ">=2.0"
|
idna = ">=2.0"
|
||||||
multidict = ">=4.0"
|
multidict = ">=4.0"
|
||||||
typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zipp"
|
|
||||||
version = "3.8.1"
|
|
||||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
|
||||||
category = "main"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"]
|
|
||||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"]
|
|
||||||
|
|
||||||
[extras]
|
[extras]
|
||||||
aiohttp = ["aiohttp"]
|
aiohttp = ["aiohttp"]
|
||||||
@@ -1151,92 +1075,16 @@ websockets = ["websockets"]
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.7.3"
|
python-versions = "^3.8"
|
||||||
content-hash = "8775c8091ff054a3fb807281e0cfebde4a0409d18970a1001a7a06fad595addf"
|
content-hash = "3b21339de9113224b61700da359ea53ef866677d19c0bdbad7126e33ceceb856"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
aiodns = [
|
aiodns = [
|
||||||
{file = "aiodns-3.0.0-py3-none-any.whl", hash = "sha256:2b19bc5f97e5c936638d28e665923c093d8af2bf3aa88d35c43417fa25d136a2"},
|
{file = "aiodns-3.0.0-py3-none-any.whl", hash = "sha256:2b19bc5f97e5c936638d28e665923c093d8af2bf3aa88d35c43417fa25d136a2"},
|
||||||
{file = "aiodns-3.0.0.tar.gz", hash = "sha256:946bdfabe743fceeeb093c8a010f5d1645f708a241be849e17edfb0e49e08cd6"},
|
{file = "aiodns-3.0.0.tar.gz", hash = "sha256:946bdfabe743fceeeb093c8a010f5d1645f708a241be849e17edfb0e49e08cd6"},
|
||||||
]
|
]
|
||||||
aiofiles = [
|
aiofiles = []
|
||||||
{file = "aiofiles-0.8.0-py3-none-any.whl", hash = "sha256:7a973fc22b29e9962d0897805ace5856e6a566ab1f0c8e5c91ff6c866519c937"},
|
aiohttp = []
|
||||||
{file = "aiofiles-0.8.0.tar.gz", hash = "sha256:8334f23235248a3b2e83b2c3a78a22674f39969b96397126cc93664d9a901e59"},
|
|
||||||
]
|
|
||||||
aiohttp = [
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:099ebd2c37ac74cce10a3527d2b49af80243e2a4fa39e7bce41617fbc35fa3c1"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e5d962cf7e1d426aa0e528a7e198658cdc8aa4fe87f781d039ad75dcd52c516"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-win32.whl", hash = "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa"},
|
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4a4a4e30bf1edcad13fb0804300557aedd07a92cabc74382fdd0ba6ca2661091"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f8b01295e26c68b3a1b90efb7a89029110d3a4139270b24fda961893216c440"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-win32.whl", hash = "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602"},
|
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2f2f69dca064926e79997f45b2f34e202b320fd3782f17a91941f7eb85502ee2"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef9612483cb35171d51d9173647eed5d0069eaa2ee812793a75373447d487aa4"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-win32.whl", hash = "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9"},
|
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:086f92daf51a032d062ec5f58af5ca6a44d082c35299c96376a41cbb33034675"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:11691cf4dc5b94236ccc609b70fec991234e7ef8d4c02dd0c9668d1e486f5abf"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-win32.whl", hash = "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a"},
|
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0770e2806a30e744b4e21c9d73b7bee18a1cfa3c47991ee2e5a65b887c49d5cf"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d15367ce87c8e9e09b0f989bfd72dc641bcd04ba091c68cd305312d00962addd"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-win32.whl", hash = "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1"},
|
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
|
|
||||||
{file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
|
|
||||||
]
|
|
||||||
aiosignal = [
|
aiosignal = [
|
||||||
{file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"},
|
{file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"},
|
||||||
{file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"},
|
{file = "aiosignal-1.2.0.tar.gz", hash = "sha256:78ed67db6c7b7ced4f98e495e572106d5c432a93e1ddd1bf475e1dc05f5b7df2"},
|
||||||
@@ -1256,11 +1104,6 @@ async-timeout = [
|
|||||||
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
|
{file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
|
||||||
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
|
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
|
||||||
]
|
]
|
||||||
asynctest = [
|
|
||||||
{file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"},
|
|
||||||
{file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"},
|
|
||||||
]
|
|
||||||
atomicwrites = []
|
|
||||||
attrs = [
|
attrs = [
|
||||||
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
||||||
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
||||||
@@ -1362,10 +1205,7 @@ cchardet = [
|
|||||||
{file = "cchardet-2.1.7-cp39-cp39-win_amd64.whl", hash = "sha256:24974b3e40fee9e7557bb352be625c39ec6f50bc2053f44a3d1191db70b51675"},
|
{file = "cchardet-2.1.7-cp39-cp39-win_amd64.whl", hash = "sha256:24974b3e40fee9e7557bb352be625c39ec6f50bc2053f44a3d1191db70b51675"},
|
||||||
{file = "cchardet-2.1.7.tar.gz", hash = "sha256:c428b6336545053c2589f6caf24ea32276c6664cb86db817e03a94c60afa0eaf"},
|
{file = "cchardet-2.1.7.tar.gz", hash = "sha256:c428b6336545053c2589f6caf24ea32276c6664cb86db817e03a94c60afa0eaf"},
|
||||||
]
|
]
|
||||||
certifi = [
|
certifi = []
|
||||||
{file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"},
|
|
||||||
{file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"},
|
|
||||||
]
|
|
||||||
cffi = [
|
cffi = [
|
||||||
{file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
|
{file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
|
||||||
{file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
|
{file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
|
||||||
@@ -1436,10 +1276,7 @@ cfgv = [
|
|||||||
{file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
|
{file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
|
||||||
{file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
|
{file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
|
||||||
]
|
]
|
||||||
charset-normalizer = [
|
charset-normalizer = []
|
||||||
{file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"},
|
|
||||||
{file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"},
|
|
||||||
]
|
|
||||||
click = [
|
click = [
|
||||||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
||||||
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
||||||
@@ -1455,10 +1292,7 @@ execnet = [
|
|||||||
{file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"},
|
{file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"},
|
||||||
]
|
]
|
||||||
fastapi = []
|
fastapi = []
|
||||||
filelock = [
|
filelock = []
|
||||||
{file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"},
|
|
||||||
{file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"},
|
|
||||||
]
|
|
||||||
frozenlist = []
|
frozenlist = []
|
||||||
h11 = [
|
h11 = [
|
||||||
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
|
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
|
||||||
@@ -1476,60 +1310,18 @@ httpcore = [
|
|||||||
{file = "httpcore-0.15.0-py3-none-any.whl", hash = "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6"},
|
{file = "httpcore-0.15.0-py3-none-any.whl", hash = "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6"},
|
||||||
{file = "httpcore-0.15.0.tar.gz", hash = "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"},
|
{file = "httpcore-0.15.0.tar.gz", hash = "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"},
|
||||||
]
|
]
|
||||||
httptools = [
|
httptools = []
|
||||||
{file = "httptools-0.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcddfe70553be717d9745990dfdb194e22ee0f60eb8f48c0794e7bfeda30d2d5"},
|
|
||||||
{file = "httptools-0.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ee0b459257e222b878a6c09ccf233957d3a4dcb883b0847640af98d2d9aac23"},
|
|
||||||
{file = "httptools-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceafd5e960b39c7e0d160a1936b68eb87c5e79b3979d66e774f0c77d4d8faaed"},
|
|
||||||
{file = "httptools-0.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fdb9f9ed79bc6f46b021b3319184699ba1a22410a82204e6e89c774530069683"},
|
|
||||||
{file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:abe829275cdd4174b4c4e65ad718715d449e308d59793bf3a931ee1bf7e7b86c"},
|
|
||||||
{file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7af6bdbd21a2a25d6784f6d67f44f5df33ef39b6159543b9f9064d365c01f919"},
|
|
||||||
{file = "httptools-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d1fe6b6661022fd6cac541f54a4237496b246e6f1c0a6b41998ee08a1135afe"},
|
|
||||||
{file = "httptools-0.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:48e48530d9b995a84d1d89ae6b3ec4e59ea7d494b150ac3bbc5e2ac4acce92cd"},
|
|
||||||
{file = "httptools-0.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a113789e53ac1fa26edf99856a61e4c493868e125ae0dd6354cf518948fbbd5c"},
|
|
||||||
{file = "httptools-0.4.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e2eb957787cbb614a0f006bfc5798ff1d90ac7c4dd24854c84edbdc8c02369e"},
|
|
||||||
{file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:7ee9f226acab9085037582c059d66769862706e8e8cd2340470ceb8b3850873d"},
|
|
||||||
{file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:701e66b59dd21a32a274771238025d58db7e2b6ecebbab64ceff51b8e31527ae"},
|
|
||||||
{file = "httptools-0.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6a1a7dfc1f9c78a833e2c4904757a0f47ce25d08634dd2a52af394eefe5f9777"},
|
|
||||||
{file = "httptools-0.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:903f739c9fb78dab8970b0f3ea51f21955b24b45afa77b22ff0e172fc11ef111"},
|
|
||||||
{file = "httptools-0.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54bbd295f031b866b9799dd39cb45deee81aca036c9bff9f58ca06726f6494f1"},
|
|
||||||
{file = "httptools-0.4.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3194f6d6443befa8d4db16c1946b2fc428a3ceb8ab32eb6f09a59f86104dc1a0"},
|
|
||||||
{file = "httptools-0.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cd1295f52971097f757edfbfce827b6dbbfb0f7a74901ee7d4933dff5ad4c9af"},
|
|
||||||
{file = "httptools-0.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:20a45bcf22452a10fa8d58b7dbdb474381f6946bf5b8933e3662d572bc61bae4"},
|
|
||||||
{file = "httptools-0.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d1f27bb0f75bef722d6e22dc609612bfa2f994541621cd2163f8c943b6463dfe"},
|
|
||||||
{file = "httptools-0.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7f7bfb74718f52d5ed47d608d507bf66d3bc01d4a8b3e6dd7134daaae129357b"},
|
|
||||||
{file = "httptools-0.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a522d12e2ddbc2e91842ffb454a1aeb0d47607972c7d8fc88bd0838d97fb8a2a"},
|
|
||||||
{file = "httptools-0.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2db44a0b294d317199e9f80123e72c6b005c55b625b57fae36de68670090fa48"},
|
|
||||||
{file = "httptools-0.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c286985b5e194ca0ebb2908d71464b9be8f17cc66d6d3e330e8d5407248f56ad"},
|
|
||||||
{file = "httptools-0.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3a4e165ca6204f34856b765d515d558dc84f1352033b8721e8d06c3e44930c3"},
|
|
||||||
{file = "httptools-0.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:72aa3fbe636b16d22e04b5a9d24711b043495e0ecfe58080addf23a1a37f3409"},
|
|
||||||
{file = "httptools-0.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9967d9758df505975913304c434cb9ab21e2c609ad859eb921f2f615a038c8de"},
|
|
||||||
{file = "httptools-0.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f72b5d24d6730035128b238decdc4c0f2104b7056a7ca55cf047c106842ec890"},
|
|
||||||
{file = "httptools-0.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:29bf97a5c532da9c7a04de2c7a9c31d1d54f3abd65a464119b680206bbbb1055"},
|
|
||||||
{file = "httptools-0.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98993805f1e3cdb53de4eed02b55dcc953cdf017ba7bbb2fd89226c086a6d855"},
|
|
||||||
{file = "httptools-0.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d9b90bf58f3ba04e60321a23a8723a1ff2a9377502535e70495e5ada8e6e6722"},
|
|
||||||
{file = "httptools-0.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a99346ebcb801b213c591540837340bdf6fd060a8687518d01c607d338b7424"},
|
|
||||||
{file = "httptools-0.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:645373c070080e632480a3d251d892cb795be3d3a15f86975d0f1aca56fd230d"},
|
|
||||||
{file = "httptools-0.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:34d2903dd2a3dd85d33705b6fde40bf91fc44411661283763fd0746723963c83"},
|
|
||||||
{file = "httptools-0.4.0.tar.gz", hash = "sha256:2c9a930c378b3d15d6b695fb95ebcff81a7395b4f9775c4f10a076beb0b2c1ff"},
|
|
||||||
]
|
|
||||||
httpx = [
|
httpx = [
|
||||||
{file = "httpx-0.23.0-py3-none-any.whl", hash = "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b"},
|
{file = "httpx-0.23.0-py3-none-any.whl", hash = "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b"},
|
||||||
{file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"},
|
{file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"},
|
||||||
]
|
]
|
||||||
hypercorn = [
|
hypercorn = []
|
||||||
{file = "Hypercorn-0.13.2-py3-none-any.whl", hash = "sha256:ca18f91ab3fa823cbe9e949738f9f2cc07027cd647c80d8f93e4b1a2a175f112"},
|
|
||||||
{file = "Hypercorn-0.13.2.tar.gz", hash = "sha256:6307be5cbdf6ba411967d4661202dc4f79bd511b5d318bc4eed88b09418427f8"},
|
|
||||||
]
|
|
||||||
hyperframe = [
|
hyperframe = [
|
||||||
{file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"},
|
{file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"},
|
||||||
{file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"},
|
{file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"},
|
||||||
]
|
]
|
||||||
identify = []
|
identify = []
|
||||||
idna = [
|
idna = []
|
||||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
|
||||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
|
||||||
]
|
|
||||||
importlib-metadata = []
|
|
||||||
iniconfig = [
|
iniconfig = [
|
||||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||||
@@ -1669,10 +1461,7 @@ packaging = [
|
|||||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||||
]
|
]
|
||||||
pathspec = [
|
pathspec = []
|
||||||
{file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
|
|
||||||
{file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
|
|
||||||
]
|
|
||||||
platformdirs = [
|
platformdirs = [
|
||||||
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
|
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
|
||||||
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
|
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
|
||||||
@@ -1686,97 +1475,23 @@ priority = [
|
|||||||
{file = "priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa"},
|
{file = "priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa"},
|
||||||
{file = "priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0"},
|
{file = "priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0"},
|
||||||
]
|
]
|
||||||
prompt-toolkit = [
|
prompt-toolkit = []
|
||||||
{file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"},
|
|
||||||
{file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"},
|
|
||||||
]
|
|
||||||
py = [
|
py = [
|
||||||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
||||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||||
]
|
]
|
||||||
pycares = [
|
pycares = []
|
||||||
{file = "pycares-4.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d83f193563b42360528167705b1c7bb91e2a09f990b98e3d6378835b72cd5c96"},
|
|
||||||
{file = "pycares-4.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b03f69df69f0ab3bfb8dbe54444afddff6ff9389561a08aade96b4f91207a655"},
|
|
||||||
{file = "pycares-4.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3b78bdee2f2f1351d5fccc2d1b667aea2d15a55d74d52cb9fd5bea8b5e74c4dc"},
|
|
||||||
{file = "pycares-4.2.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f05223de13467bb26f9a1594a1799ce2d08ad8ea241489fecd9d8ed3bbbfc672"},
|
|
||||||
{file = "pycares-4.2.1-cp310-cp310-win32.whl", hash = "sha256:1f37f762414680063b4dfec5be809a84f74cd8e203d939aaf3ba9c807a9e7013"},
|
|
||||||
{file = "pycares-4.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:1a9506d496efeb809a1b63647cb2f3f33c67fcf62bf80a2359af692fef2c1755"},
|
|
||||||
{file = "pycares-4.2.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2fd53eb5b441c4f6f9c78d7900e05883e9998b34a14b804be4fc4c6f9fea89f3"},
|
|
||||||
{file = "pycares-4.2.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:061dd4c80fec73feb150455b159704cd51a122f20d36790033bd6375d4198579"},
|
|
||||||
{file = "pycares-4.2.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a521d7f54f3e52ded4d34c306ba05cfe9eb5aaa2e5aaf83c96564b9369495588"},
|
|
||||||
{file = "pycares-4.2.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:99e00e397d07a79c9f43e4303e67f4f97bcabd013bda0d8f2d430509b7aef8a0"},
|
|
||||||
{file = "pycares-4.2.1-cp36-cp36m-win32.whl", hash = "sha256:d9cd826d8e0c270059450709bff994bfeb072f79d82fd3f11c701690ff65d0e7"},
|
|
||||||
{file = "pycares-4.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f8e6942965465ca98e212376c4afb9aec501d8129054929744b2f4a487c8c14b"},
|
|
||||||
{file = "pycares-4.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e75cbd4d3b3d9b02bba6e170846e39893a825e7a5fb1b96728fc6d7b964f8945"},
|
|
||||||
{file = "pycares-4.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2e8ec4c8e07c986b70a3cc8f5b297c53b08ac755e5b9797512002a466e2de86"},
|
|
||||||
{file = "pycares-4.2.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5333b51ef4ff3e8973b4a1b57cad5ada13e15552445ee3cd74bd77407dec9d44"},
|
|
||||||
{file = "pycares-4.2.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2113529004df4894783eaa61e9abc3a680756b6f033d942f2800301ae8c71c29"},
|
|
||||||
{file = "pycares-4.2.1-cp37-cp37m-win32.whl", hash = "sha256:e7a95763cdc20cf9ec357066e656ea30b8de6b03de6175cbb50890e22aa01868"},
|
|
||||||
{file = "pycares-4.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7a901776163a04de5d67c42bd63a287cff9cb05fc041668ad1681fe3daa36445"},
|
|
||||||
{file = "pycares-4.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:66b5390a4885a578e687d3f2683689c35e1d4573f4d0ecf217431f7bb55c49a0"},
|
|
||||||
{file = "pycares-4.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15dd5cf21bc73ad539e8aabf7afe370d1df8af7bc6944cd7298f3bfef0c1a27c"},
|
|
||||||
{file = "pycares-4.2.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4ee625d7571039038bca51ae049b047cbfcfc024b302aae6cc53d5d9aa8648a8"},
|
|
||||||
{file = "pycares-4.2.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:396ee487178e9de06ca4122a35a157474db3ce0a0db6038a31c831ebb9863315"},
|
|
||||||
{file = "pycares-4.2.1-cp38-cp38-win32.whl", hash = "sha256:e4dc37f732f7110ca6368e0128cbbd0a54f5211515a061b2add64da2ddb8e5ca"},
|
|
||||||
{file = "pycares-4.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:3636fccf643c5192c34ee0183c514a2d09419e3a76ca2717cef626638027cb21"},
|
|
||||||
{file = "pycares-4.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6724573e830ea2345f4bcf0f968af64cc6d491dc2133e9c617f603445dcdfa58"},
|
|
||||||
{file = "pycares-4.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dbfcacbde6c21380c412c13d53ea44b257dea3f7b9d80be2c873bb20e21fee"},
|
|
||||||
{file = "pycares-4.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c8a46839da642b281ac5f56d3c6336528e128b3c41eab9c5330d250f22325e9d"},
|
|
||||||
{file = "pycares-4.2.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9b05c2cec644a6c66b55bcf6c24d4dfdaf2f7205b16e5c4ceee31db104fac958"},
|
|
||||||
{file = "pycares-4.2.1-cp39-cp39-win32.whl", hash = "sha256:8bd6ed3ad3a5358a635c1acf5d0f46be9afb095772b84427ff22283d2f31db1b"},
|
|
||||||
{file = "pycares-4.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:fbd53728d798d07811898e11991e22209229c090eab265a53d12270b95d70d1a"},
|
|
||||||
{file = "pycares-4.2.1.tar.gz", hash = "sha256:735b4f75fd0f595c4e9184da18cd87737f46bc81a64ea41f4edce2b6b68d46d2"},
|
|
||||||
]
|
|
||||||
pycparser = [
|
pycparser = [
|
||||||
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
|
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
|
||||||
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
|
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
|
||||||
]
|
]
|
||||||
pydantic = [
|
pydantic = []
|
||||||
{file = "pydantic-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193"},
|
|
||||||
{file = "pydantic-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11"},
|
|
||||||
{file = "pydantic-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310"},
|
|
||||||
{file = "pydantic-1.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131"},
|
|
||||||
{file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580"},
|
|
||||||
{file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd"},
|
|
||||||
{file = "pydantic-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd"},
|
|
||||||
{file = "pydantic-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761"},
|
|
||||||
{file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918"},
|
|
||||||
{file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74"},
|
|
||||||
{file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a"},
|
|
||||||
{file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166"},
|
|
||||||
{file = "pydantic-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b"},
|
|
||||||
{file = "pydantic-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892"},
|
|
||||||
{file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e"},
|
|
||||||
{file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608"},
|
|
||||||
{file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537"},
|
|
||||||
{file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380"},
|
|
||||||
{file = "pydantic-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728"},
|
|
||||||
{file = "pydantic-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a"},
|
|
||||||
{file = "pydantic-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1"},
|
|
||||||
{file = "pydantic-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195"},
|
|
||||||
{file = "pydantic-1.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b"},
|
|
||||||
{file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49"},
|
|
||||||
{file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"},
|
|
||||||
{file = "pydantic-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0"},
|
|
||||||
{file = "pydantic-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6"},
|
|
||||||
{file = "pydantic-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810"},
|
|
||||||
{file = "pydantic-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f"},
|
|
||||||
{file = "pydantic-1.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee"},
|
|
||||||
{file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761"},
|
|
||||||
{file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd"},
|
|
||||||
{file = "pydantic-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1"},
|
|
||||||
{file = "pydantic-1.9.1-py3-none-any.whl", hash = "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58"},
|
|
||||||
{file = "pydantic-1.9.1.tar.gz", hash = "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a"},
|
|
||||||
]
|
|
||||||
pygtrie = []
|
pygtrie = []
|
||||||
pyparsing = [
|
pyparsing = [
|
||||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
||||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
||||||
]
|
]
|
||||||
pytest = [
|
pytest = []
|
||||||
{file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
|
|
||||||
{file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
|
|
||||||
]
|
|
||||||
pytest-asyncio = []
|
pytest-asyncio = []
|
||||||
pytest-cov = [
|
pytest-cov = [
|
||||||
{file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
|
{file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
|
||||||
@@ -1790,10 +1505,7 @@ pytest-xdist = [
|
|||||||
{file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"},
|
{file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"},
|
||||||
{file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"},
|
{file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"},
|
||||||
]
|
]
|
||||||
python-dotenv = [
|
python-dotenv = []
|
||||||
{file = "python-dotenv-0.20.0.tar.gz", hash = "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f"},
|
|
||||||
{file = "python_dotenv-0.20.0-py3-none-any.whl", hash = "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"},
|
|
||||||
]
|
|
||||||
pyyaml = [
|
pyyaml = [
|
||||||
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
|
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
|
||||||
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
|
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
|
||||||
@@ -1845,10 +1557,7 @@ six = [
|
|||||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
]
|
]
|
||||||
sniffio = [
|
sniffio = []
|
||||||
{file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
|
|
||||||
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
|
|
||||||
]
|
|
||||||
starlette = [
|
starlette = [
|
||||||
{file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
|
{file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
|
||||||
{file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
|
{file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
|
||||||
@@ -1862,56 +1571,13 @@ tomli = [
|
|||||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||||
]
|
]
|
||||||
tomlkit = []
|
tomlkit = []
|
||||||
typed-ast = [
|
|
||||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"},
|
|
||||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"},
|
|
||||||
{file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"},
|
|
||||||
{file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"},
|
|
||||||
{file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"},
|
|
||||||
{file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"},
|
|
||||||
{file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"},
|
|
||||||
{file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"},
|
|
||||||
{file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"},
|
|
||||||
{file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"},
|
|
||||||
{file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"},
|
|
||||||
{file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"},
|
|
||||||
{file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"},
|
|
||||||
{file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"},
|
|
||||||
{file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"},
|
|
||||||
{file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"},
|
|
||||||
{file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"},
|
|
||||||
{file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"},
|
|
||||||
{file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"},
|
|
||||||
{file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"},
|
|
||||||
{file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"},
|
|
||||||
{file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"},
|
|
||||||
{file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"},
|
|
||||||
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
|
|
||||||
]
|
|
||||||
typing-extensions = [
|
typing-extensions = [
|
||||||
{file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
|
{file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
|
||||||
{file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
|
{file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
|
||||||
]
|
]
|
||||||
urllib3 = []
|
urllib3 = []
|
||||||
uvicorn = []
|
uvicorn = []
|
||||||
uvloop = [
|
uvloop = []
|
||||||
{file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
|
|
||||||
{file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c"},
|
|
||||||
{file = "uvloop-0.16.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64"},
|
|
||||||
{file = "uvloop-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9"},
|
|
||||||
{file = "uvloop-0.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638"},
|
|
||||||
{file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450"},
|
|
||||||
{file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805"},
|
|
||||||
{file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382"},
|
|
||||||
{file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee"},
|
|
||||||
{file = "uvloop-0.16.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464"},
|
|
||||||
{file = "uvloop-0.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab"},
|
|
||||||
{file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f"},
|
|
||||||
{file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897"},
|
|
||||||
{file = "uvloop-0.16.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f"},
|
|
||||||
{file = "uvloop-0.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861"},
|
|
||||||
{file = "uvloop-0.16.0.tar.gz", hash = "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228"},
|
|
||||||
]
|
|
||||||
virtualenv = []
|
virtualenv = []
|
||||||
watchfiles = []
|
watchfiles = []
|
||||||
wcwidth = [
|
wcwidth = [
|
||||||
@@ -1973,9 +1639,5 @@ win32-setctime = [
|
|||||||
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
|
{file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
|
||||||
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
|
{file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
|
||||||
]
|
]
|
||||||
wsproto = [
|
wsproto = []
|
||||||
{file = "wsproto-1.1.0-py3-none-any.whl", hash = "sha256:2218cb57952d90b9fca325c0dcfb08c3bda93e8fd8070b0a17f048e2e47a521b"},
|
|
||||||
{file = "wsproto-1.1.0.tar.gz", hash = "sha256:a2e56bfd5c7cd83c1369d83b5feccd6d37798b74872866e62616e0ecf111bda8"},
|
|
||||||
]
|
|
||||||
yarl = []
|
yarl = []
|
||||||
zipp = []
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "nonebot2"
|
name = "nonebot2"
|
||||||
version = "2.0.0-beta.5"
|
version = "2.0.0-rc.1"
|
||||||
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"
|
||||||
@@ -22,7 +22,7 @@ packages = [
|
|||||||
include = ["nonebot/py.typed"]
|
include = ["nonebot/py.typed"]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.7.3"
|
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"
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
[report]
|
[report]
|
||||||
exclude_lines =
|
exclude_lines =
|
||||||
def __repr__
|
def __repr__
|
||||||
|
def __str__
|
||||||
pragma: no cover
|
pragma: no cover
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@(abc\.)?abstractmethod
|
@(abc\.)?abstractmethod
|
||||||
|
1
tests/plugins.invalid.json
Normal file
1
tests/plugins.invalid.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
2
tests/plugins.invalid.toml
Normal file
2
tests/plugins.invalid.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[tool]
|
||||||
|
nonebot = []
|
4
tests/plugins.json
Normal file
4
tests/plugins.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"plugins": [],
|
||||||
|
"plugin_dirs": ["plugins"]
|
||||||
|
}
|
3
tests/plugins.toml
Normal file
3
tests/plugins.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[tool.nonebot]
|
||||||
|
plugins = []
|
||||||
|
plugin_dirs = ["plugins"]
|
@@ -1,6 +1,2 @@
|
|||||||
from nonebot import export
|
|
||||||
|
|
||||||
|
|
||||||
@export()
|
|
||||||
def test():
|
def test():
|
||||||
return "export"
|
return "export"
|
||||||
|
@@ -3,5 +3,7 @@ from datetime import datetime, timedelta
|
|||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
|
|
||||||
test_temp_matcher = Matcher.new("test", temp=True)
|
test_temp_matcher = Matcher.new("test", temp=True)
|
||||||
test_datetime_matcher = Matcher.new("test", expire_time=datetime.now())
|
test_datetime_matcher = Matcher.new(
|
||||||
|
"test", expire_time=datetime.now() - timedelta(seconds=1)
|
||||||
|
)
|
||||||
test_timedelta_matcher = Matcher.new("test", expire_time=timedelta(seconds=-1))
|
test_timedelta_matcher = Matcher.new("test", expire_time=timedelta(seconds=-1))
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
from nonebot.matcher import Matcher
|
from nonebot.matcher import Matcher
|
||||||
from nonebot.permission import Permission
|
from nonebot.permission import USER, Permission
|
||||||
|
|
||||||
default_permission = Permission()
|
default_permission = Permission()
|
||||||
|
|
||||||
test_permission_updater = Matcher.new(permission=default_permission)
|
test_permission_updater = Matcher.new(permission=default_permission)
|
||||||
|
|
||||||
|
test_user_permission_updater = Matcher.new(
|
||||||
|
permission=USER("test", perm=default_permission)
|
||||||
|
)
|
||||||
|
|
||||||
test_custom_updater = Matcher.new(permission=default_permission)
|
test_custom_updater = Matcher.new(permission=default_permission)
|
||||||
|
|
||||||
|
|
||||||
|
1
tests/plugins/plugin/__init__.py
Normal file
1
tests/plugins/plugin/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import matchers
|
243
tests/plugins/plugin/matchers.py
Normal file
243
tests/plugins/plugin/matchers.py
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
from nonebot.adapters import Event
|
||||||
|
from nonebot import (
|
||||||
|
CommandGroup,
|
||||||
|
MatcherGroup,
|
||||||
|
on,
|
||||||
|
on_type,
|
||||||
|
on_regex,
|
||||||
|
on_notice,
|
||||||
|
on_command,
|
||||||
|
on_keyword,
|
||||||
|
on_message,
|
||||||
|
on_request,
|
||||||
|
on_endswith,
|
||||||
|
on_fullmatch,
|
||||||
|
on_metaevent,
|
||||||
|
on_startswith,
|
||||||
|
on_shell_command,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def rule() -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def permission() -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def handler():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
expire_time = datetime.now(timezone.utc)
|
||||||
|
priority = 100
|
||||||
|
state = {"test": "test"}
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on = on(
|
||||||
|
"test",
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_metaevent = on_metaevent(
|
||||||
|
rule=rule,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_message = on_message(
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_notice = on_notice(
|
||||||
|
rule=rule,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_request = on_request(
|
||||||
|
rule=rule,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_startswith = on_startswith(
|
||||||
|
"test",
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_endswith = on_endswith(
|
||||||
|
"test",
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_fullmatch = on_fullmatch(
|
||||||
|
"test",
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_keyword = on_keyword(
|
||||||
|
{"test"},
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_command = on_command(
|
||||||
|
"test",
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_shell_command = on_shell_command(
|
||||||
|
"test",
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_regex = on_regex(
|
||||||
|
"test",
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestEvent(Event):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
matcher_on_type = on_type(
|
||||||
|
TestEvent,
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
cmd_group = CommandGroup(
|
||||||
|
"test",
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
matcher_sub_cmd = cmd_group.command("sub")
|
||||||
|
matcher_sub_shell_cmd = cmd_group.shell_command("sub")
|
||||||
|
|
||||||
|
|
||||||
|
matcher_group = MatcherGroup(
|
||||||
|
rule=rule,
|
||||||
|
permission=permission,
|
||||||
|
handlers=[handler],
|
||||||
|
temp=True,
|
||||||
|
expire_time=expire_time,
|
||||||
|
priority=priority,
|
||||||
|
block=True,
|
||||||
|
state=state,
|
||||||
|
)
|
||||||
|
matcher_group_on = matcher_group.on(type="test")
|
||||||
|
matcher_group_on_metaevent = matcher_group.on_metaevent()
|
||||||
|
matcher_group_on_message = matcher_group.on_message()
|
||||||
|
matcher_group_on_notice = matcher_group.on_notice()
|
||||||
|
matcher_group_on_request = matcher_group.on_request()
|
||||||
|
matcher_group_on_startswith = matcher_group.on_startswith("test")
|
||||||
|
matcher_group_on_endswith = matcher_group.on_endswith("test")
|
||||||
|
matcher_group_on_fullmatch = matcher_group.on_fullmatch("test")
|
||||||
|
matcher_group_on_keyword = matcher_group.on_keyword({"test"})
|
||||||
|
matcher_group_on_command = matcher_group.on_command("test")
|
||||||
|
matcher_group_on_shell_command = matcher_group.on_shell_command("test")
|
||||||
|
matcher_group_on_regex = matcher_group.on_regex("test")
|
||||||
|
matcher_group_on_type = matcher_group.on_type(TestEvent)
|
@@ -11,11 +11,11 @@ def test_template_basis():
|
|||||||
|
|
||||||
def test_template_message():
|
def test_template_message():
|
||||||
Message = make_fake_message()
|
Message = make_fake_message()
|
||||||
template = Message.template("{a:custom}{b:text}{c:image}")
|
template = Message.template("{a:custom}{b:text}{c:image}/{d}")
|
||||||
|
|
||||||
@template.add_format_spec
|
@template.add_format_spec
|
||||||
def custom(input: str) -> str:
|
def custom(input: str) -> str:
|
||||||
return input + "-custom!"
|
return f"{input}-custom!"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
template.add_format_spec(custom)
|
template.add_format_spec(custom)
|
||||||
@@ -24,12 +24,17 @@ def test_template_message():
|
|||||||
else:
|
else:
|
||||||
raise AssertionError("Should raise ValueError")
|
raise AssertionError("Should raise ValueError")
|
||||||
|
|
||||||
format_args = {"a": "custom", "b": "text", "c": "https://example.com/test"}
|
format_args = {
|
||||||
|
"a": "custom",
|
||||||
|
"b": "text",
|
||||||
|
"c": "https://example.com/test",
|
||||||
|
"d": 114,
|
||||||
|
}
|
||||||
formatted = template.format(**format_args)
|
formatted = template.format(**format_args)
|
||||||
|
|
||||||
assert template.format_map(format_args) == formatted
|
assert template.format_map(format_args) == formatted
|
||||||
assert formatted.extract_plain_text() == "custom-custom!text"
|
assert formatted.extract_plain_text() == "custom-custom!text/114"
|
||||||
assert str(formatted) == "custom-custom!text[fake:image]"
|
assert str(formatted) == "custom-custom!text[fake:image]/114"
|
||||||
|
|
||||||
|
|
||||||
def test_rich_template_message():
|
def test_rich_template_message():
|
||||||
|
@@ -78,3 +78,27 @@ async def test_reverse_driver(app: App):
|
|||||||
assert await ws.receive_bytes() == b"pong"
|
assert await ws.receive_bytes() == b"pong"
|
||||||
|
|
||||||
await ws.close()
|
await ws.close()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"nonebug_init, driver_type",
|
||||||
|
[
|
||||||
|
pytest.param(
|
||||||
|
{"driver": "nonebot.drivers.fastapi:Driver+nonebot.drivers.aiohttp:Mixin"},
|
||||||
|
"fastapi+aiohttp",
|
||||||
|
id="fastapi+aiohttp",
|
||||||
|
),
|
||||||
|
pytest.param(
|
||||||
|
{"driver": "~httpx:Driver+~websockets"},
|
||||||
|
"block_driver+httpx+websockets",
|
||||||
|
id="httpx+websockets",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["nonebug_init"],
|
||||||
|
)
|
||||||
|
async def test_combine_driver(app: App, driver_type: str):
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
driver = nonebot.get_driver()
|
||||||
|
assert driver.type == driver_type
|
||||||
|
@@ -61,7 +61,7 @@ async def test_get(monkeypatch: pytest.MonkeyPatch, nonebug_clear):
|
|||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
get_bot()
|
get_bot()
|
||||||
|
|
||||||
monkeypatch.setattr(driver, "_clients", {"test": "test"})
|
monkeypatch.setattr(driver, "_bots", {"test": "test"})
|
||||||
assert get_bot() == "test"
|
assert get_bot() == "test"
|
||||||
assert get_bot("test") == "test"
|
assert get_bot("test") == "test"
|
||||||
assert get_bots() == {"test": "test"}
|
assert get_bots() == {"test": "test"}
|
||||||
|
@@ -104,6 +104,7 @@ async def test_permission_updater(app: App, load_plugin):
|
|||||||
default_permission,
|
default_permission,
|
||||||
test_custom_updater,
|
test_custom_updater,
|
||||||
test_permission_updater,
|
test_permission_updater,
|
||||||
|
test_user_permission_updater,
|
||||||
)
|
)
|
||||||
|
|
||||||
event = make_fake_event(_session_id="test")()
|
event = make_fake_event(_session_id="test")()
|
||||||
@@ -119,6 +120,19 @@ async def test_permission_updater(app: App, load_plugin):
|
|||||||
assert checker.users == ("test",)
|
assert checker.users == ("test",)
|
||||||
assert checker.perm is default_permission
|
assert checker.perm is default_permission
|
||||||
|
|
||||||
|
user_permission = list(test_user_permission_updater.permission.checkers)[0].call
|
||||||
|
assert isinstance(user_permission, User)
|
||||||
|
assert user_permission.perm is default_permission
|
||||||
|
async with app.test_api() as ctx:
|
||||||
|
bot = ctx.create_bot()
|
||||||
|
matcher = test_user_permission_updater()
|
||||||
|
new_perm = await matcher.update_permission(bot, event)
|
||||||
|
assert len(new_perm.checkers) == 1
|
||||||
|
checker = list(new_perm.checkers)[0].call
|
||||||
|
assert isinstance(checker, User)
|
||||||
|
assert checker.users == ("test",)
|
||||||
|
assert checker.perm is default_permission
|
||||||
|
|
||||||
assert test_custom_updater.permission is default_permission
|
assert test_custom_updater.permission is default_permission
|
||||||
async with app.test_api() as ctx:
|
async with app.test_api() as ctx:
|
||||||
bot = ctx.create_bot()
|
bot = ctx.create_bot()
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from nonebug import App
|
from nonebug import App
|
||||||
|
|
||||||
@@ -142,10 +144,11 @@ async def test_metaevent(
|
|||||||
("message", "test", True),
|
("message", "test", True),
|
||||||
("message", "foo", False),
|
("message", "foo", False),
|
||||||
("message", "faketest", True),
|
("message", "faketest", True),
|
||||||
("notice", "test", False),
|
("message", None, False),
|
||||||
|
("notice", "test", True),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_startswith(
|
async def test_superuser(
|
||||||
app: App,
|
app: App,
|
||||||
type: str,
|
type: str,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
@@ -163,3 +166,29 @@ async def test_startswith(
|
|||||||
async with app.test_api() as ctx:
|
async with app.test_api() as ctx:
|
||||||
bot = ctx.create_bot()
|
bot = ctx.create_bot()
|
||||||
assert await dependent(bot=bot, event=event) == expected
|
assert await dependent(bot=bot, event=event) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"session_ids,session_id,expected",
|
||||||
|
[
|
||||||
|
(("user", "foo"), "user", True),
|
||||||
|
(("user", "foo"), "bar", False),
|
||||||
|
(("user", "foo"), None, False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_user(
|
||||||
|
app: App, session_ids: Tuple[str, ...], session_id: Optional[str], expected: bool
|
||||||
|
):
|
||||||
|
from nonebot.permission import USER, User
|
||||||
|
|
||||||
|
dependent = list(USER(*session_ids).checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
|
||||||
|
assert isinstance(checker, User)
|
||||||
|
|
||||||
|
event = make_fake_event(_session_id=session_id)()
|
||||||
|
|
||||||
|
async with app.test_api() as ctx:
|
||||||
|
bot = ctx.create_bot()
|
||||||
|
assert await dependent(bot=bot, event=event) == expected
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
from typing import TYPE_CHECKING, Set
|
from typing import TYPE_CHECKING, Set
|
||||||
|
|
||||||
@@ -10,7 +11,21 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_load_plugin(app: App, load_plugin: Set["Plugin"]):
|
async def test_load_plugin(app: App):
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
# check regular
|
||||||
|
assert nonebot.load_plugin("plugins.metadata")
|
||||||
|
|
||||||
|
# check path
|
||||||
|
assert nonebot.load_plugin(Path("plugins/export"))
|
||||||
|
|
||||||
|
# check not found
|
||||||
|
assert nonebot.load_plugin("some_plugin_not_exist") is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_load_plugins(app: App, load_plugin: Set["Plugin"]):
|
||||||
import nonebot
|
import nonebot
|
||||||
from nonebot.plugin import PluginManager
|
from nonebot.plugin import PluginManager
|
||||||
|
|
||||||
@@ -34,9 +49,6 @@ async def test_load_plugin(app: App, load_plugin: Set["Plugin"]):
|
|||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
PluginManager(search_path=["plugins"]).load_all_plugins()
|
PluginManager(search_path=["plugins"]).load_all_plugins()
|
||||||
|
|
||||||
# check not found
|
|
||||||
assert nonebot.load_plugin("some_plugin_not_exist") is None
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_load_nested_plugin(app: App, load_plugin: Set["Plugin"]):
|
async def test_load_nested_plugin(app: App, load_plugin: Set["Plugin"]):
|
||||||
@@ -51,6 +63,29 @@ async def test_load_nested_plugin(app: App, load_plugin: Set["Plugin"]):
|
|||||||
assert parent_plugin.sub_plugins == {sub_plugin, sub_plugin2}
|
assert parent_plugin.sub_plugins == {sub_plugin, sub_plugin2}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_load_json(app: App):
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
nonebot.load_from_json("./plugins.json")
|
||||||
|
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
nonebot.load_from_json("./plugins.invalid.json")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_load_toml(app: App):
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
nonebot.load_from_toml("./plugins.toml")
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
nonebot.load_from_toml("./plugins.empty.toml")
|
||||||
|
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
nonebot.load_from_toml("./plugins.invalid.toml")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_bad_plugin(app: App):
|
async def test_bad_plugin(app: App):
|
||||||
import nonebot
|
import nonebot
|
||||||
|
116
tests/test_plugin/test_on.py
Normal file
116
tests/test_plugin/test_on.py
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
from typing import Type, Optional
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from nonebug import App
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_on(app: App, load_plugin):
|
||||||
|
import nonebot
|
||||||
|
import plugins.plugin.matchers as module
|
||||||
|
from nonebot.typing import T_RuleChecker
|
||||||
|
from nonebot.matcher import Matcher, matchers
|
||||||
|
from nonebot.rule import (
|
||||||
|
RegexRule,
|
||||||
|
IsTypeRule,
|
||||||
|
CommandRule,
|
||||||
|
EndswithRule,
|
||||||
|
KeywordsRule,
|
||||||
|
FullmatchRule,
|
||||||
|
StartswithRule,
|
||||||
|
ShellCommandRule,
|
||||||
|
)
|
||||||
|
from plugins.plugin.matchers import (
|
||||||
|
TestEvent,
|
||||||
|
rule,
|
||||||
|
state,
|
||||||
|
handler,
|
||||||
|
priority,
|
||||||
|
matcher_on,
|
||||||
|
permission,
|
||||||
|
expire_time,
|
||||||
|
matcher_on_type,
|
||||||
|
matcher_sub_cmd,
|
||||||
|
matcher_group_on,
|
||||||
|
matcher_on_regex,
|
||||||
|
matcher_on_notice,
|
||||||
|
matcher_on_command,
|
||||||
|
matcher_on_keyword,
|
||||||
|
matcher_on_message,
|
||||||
|
matcher_on_request,
|
||||||
|
matcher_on_endswith,
|
||||||
|
matcher_on_fullmatch,
|
||||||
|
matcher_on_metaevent,
|
||||||
|
matcher_group_on_type,
|
||||||
|
matcher_on_startswith,
|
||||||
|
matcher_sub_shell_cmd,
|
||||||
|
matcher_group_on_regex,
|
||||||
|
matcher_group_on_notice,
|
||||||
|
matcher_group_on_command,
|
||||||
|
matcher_group_on_keyword,
|
||||||
|
matcher_group_on_message,
|
||||||
|
matcher_group_on_request,
|
||||||
|
matcher_on_shell_command,
|
||||||
|
matcher_group_on_endswith,
|
||||||
|
matcher_group_on_fullmatch,
|
||||||
|
matcher_group_on_metaevent,
|
||||||
|
matcher_group_on_startswith,
|
||||||
|
matcher_group_on_shell_command,
|
||||||
|
)
|
||||||
|
|
||||||
|
plugin = nonebot.get_plugin("plugin")
|
||||||
|
|
||||||
|
def _check(
|
||||||
|
matcher: Type[Matcher],
|
||||||
|
pre_rule: Optional[T_RuleChecker],
|
||||||
|
has_permission: bool,
|
||||||
|
):
|
||||||
|
assert {dependent.call for dependent in matcher.rule.checkers} == (
|
||||||
|
{pre_rule, rule} if pre_rule else {rule}
|
||||||
|
)
|
||||||
|
if has_permission:
|
||||||
|
assert {dependent.call for dependent in matcher.permission.checkers} == {
|
||||||
|
permission
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
assert not matcher.permission.checkers
|
||||||
|
assert [dependent.call for dependent in matcher.handlers] == [handler]
|
||||||
|
assert matcher.temp is True
|
||||||
|
assert matcher.expire_time == expire_time
|
||||||
|
assert matcher in matchers[priority]
|
||||||
|
assert matcher.block is True
|
||||||
|
assert matcher._default_state == state
|
||||||
|
|
||||||
|
assert matcher.plugin is plugin
|
||||||
|
assert matcher.module is module
|
||||||
|
assert matcher.plugin_name == "plugin"
|
||||||
|
assert matcher.module_name == "plugins.plugin.matchers"
|
||||||
|
|
||||||
|
_check(matcher_on, None, True)
|
||||||
|
_check(matcher_on_metaevent, None, False)
|
||||||
|
_check(matcher_on_message, None, True)
|
||||||
|
_check(matcher_on_notice, None, False)
|
||||||
|
_check(matcher_on_request, None, False)
|
||||||
|
_check(matcher_on_startswith, StartswithRule(("test",)), True)
|
||||||
|
_check(matcher_on_endswith, EndswithRule(("test",)), True)
|
||||||
|
_check(matcher_on_fullmatch, FullmatchRule(("test",)), True)
|
||||||
|
_check(matcher_on_keyword, KeywordsRule("test"), True)
|
||||||
|
_check(matcher_on_command, CommandRule([("test",)]), True)
|
||||||
|
_check(matcher_on_shell_command, ShellCommandRule([("test",)], None), True)
|
||||||
|
_check(matcher_on_regex, RegexRule("test"), True)
|
||||||
|
_check(matcher_on_type, IsTypeRule(TestEvent), True)
|
||||||
|
_check(matcher_sub_cmd, CommandRule([("test", "sub")]), True)
|
||||||
|
_check(matcher_sub_shell_cmd, ShellCommandRule([("test", "sub")], None), True)
|
||||||
|
_check(matcher_group_on, None, True)
|
||||||
|
_check(matcher_group_on_metaevent, None, False)
|
||||||
|
_check(matcher_group_on_message, None, True)
|
||||||
|
_check(matcher_group_on_notice, None, False)
|
||||||
|
_check(matcher_group_on_request, None, False)
|
||||||
|
_check(matcher_group_on_startswith, StartswithRule(("test",)), True)
|
||||||
|
_check(matcher_group_on_endswith, EndswithRule(("test",)), True)
|
||||||
|
_check(matcher_group_on_fullmatch, FullmatchRule(("test",)), True)
|
||||||
|
_check(matcher_group_on_keyword, KeywordsRule("test"), True)
|
||||||
|
_check(matcher_group_on_command, CommandRule([("test",)]), True)
|
||||||
|
_check(matcher_group_on_shell_command, ShellCommandRule([("test",)], None), True)
|
||||||
|
_check(matcher_group_on_regex, RegexRule("test"), True)
|
||||||
|
_check(matcher_group_on_type, IsTypeRule(TestEvent), True)
|
@@ -1,4 +1,5 @@
|
|||||||
from typing import Tuple, Union
|
import sys
|
||||||
|
from typing import Dict, Tuple, Union, Optional
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from nonebug import App
|
from nonebug import App
|
||||||
@@ -51,6 +52,7 @@ async def test_rule(app: App):
|
|||||||
("prefix", True, "message", "Prefix_", True),
|
("prefix", True, "message", "Prefix_", True),
|
||||||
("prefix", False, "message", "prefoo", False),
|
("prefix", False, "message", "prefoo", False),
|
||||||
("prefix", False, "message", "fooprefix", False),
|
("prefix", False, "message", "fooprefix", False),
|
||||||
|
("prefix", False, "message", None, False),
|
||||||
(("prefix", "foo"), False, "message", "fooprefix", True),
|
(("prefix", "foo"), False, "message", "fooprefix", True),
|
||||||
("prefix", False, "notice", "foo", False),
|
("prefix", False, "notice", "foo", False),
|
||||||
],
|
],
|
||||||
@@ -60,7 +62,7 @@ async def test_startswith(
|
|||||||
msg: Union[str, Tuple[str, ...]],
|
msg: Union[str, Tuple[str, ...]],
|
||||||
ignorecase: bool,
|
ignorecase: bool,
|
||||||
type: str,
|
type: str,
|
||||||
text: str,
|
text: Optional[str],
|
||||||
expected: bool,
|
expected: bool,
|
||||||
):
|
):
|
||||||
from nonebot.rule import StartswithRule, startswith
|
from nonebot.rule import StartswithRule, startswith
|
||||||
@@ -73,7 +75,7 @@ async def test_startswith(
|
|||||||
assert checker.msg == (msg,) if isinstance(msg, str) else msg
|
assert checker.msg == (msg,) if isinstance(msg, str) else msg
|
||||||
assert checker.ignorecase == ignorecase
|
assert checker.ignorecase == ignorecase
|
||||||
|
|
||||||
message = 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
|
assert await dependent(event=event) == expected
|
||||||
|
|
||||||
@@ -88,6 +90,7 @@ async def test_startswith(
|
|||||||
("suffix", True, "message", "_Suffix", True),
|
("suffix", True, "message", "_Suffix", True),
|
||||||
("suffix", False, "message", "suffoo", False),
|
("suffix", False, "message", "suffoo", False),
|
||||||
("suffix", False, "message", "suffixfoo", False),
|
("suffix", False, "message", "suffixfoo", False),
|
||||||
|
("suffix", False, "message", None, False),
|
||||||
(("suffix", "foo"), False, "message", "suffixfoo", True),
|
(("suffix", "foo"), False, "message", "suffixfoo", True),
|
||||||
("suffix", False, "notice", "foo", False),
|
("suffix", False, "notice", "foo", False),
|
||||||
],
|
],
|
||||||
@@ -97,7 +100,7 @@ async def test_endswith(
|
|||||||
msg: Union[str, Tuple[str, ...]],
|
msg: Union[str, Tuple[str, ...]],
|
||||||
ignorecase: bool,
|
ignorecase: bool,
|
||||||
type: str,
|
type: str,
|
||||||
text: str,
|
text: Optional[str],
|
||||||
expected: bool,
|
expected: bool,
|
||||||
):
|
):
|
||||||
from nonebot.rule import EndswithRule, endswith
|
from nonebot.rule import EndswithRule, endswith
|
||||||
@@ -110,7 +113,7 @@ async def test_endswith(
|
|||||||
assert checker.msg == (msg,) if isinstance(msg, str) else msg
|
assert checker.msg == (msg,) if isinstance(msg, str) else msg
|
||||||
assert checker.ignorecase == ignorecase
|
assert checker.ignorecase == ignorecase
|
||||||
|
|
||||||
message = 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
|
assert await dependent(event=event) == expected
|
||||||
|
|
||||||
@@ -125,6 +128,7 @@ async def test_endswith(
|
|||||||
("fullmatch", True, "message", "Fullmatch", True),
|
("fullmatch", True, "message", "Fullmatch", True),
|
||||||
("fullmatch", False, "message", "fullfoo", False),
|
("fullmatch", False, "message", "fullfoo", False),
|
||||||
("fullmatch", False, "message", "_fullmatch_", False),
|
("fullmatch", False, "message", "_fullmatch_", False),
|
||||||
|
("fullmatch", False, "message", None, False),
|
||||||
(("fullmatch", "foo"), False, "message", "fullmatchfoo", False),
|
(("fullmatch", "foo"), False, "message", "fullmatchfoo", False),
|
||||||
("fullmatch", False, "notice", "foo", False),
|
("fullmatch", False, "notice", "foo", False),
|
||||||
],
|
],
|
||||||
@@ -134,7 +138,7 @@ async def test_fullmatch(
|
|||||||
msg: Union[str, Tuple[str, ...]],
|
msg: Union[str, Tuple[str, ...]],
|
||||||
ignorecase: bool,
|
ignorecase: bool,
|
||||||
type: str,
|
type: str,
|
||||||
text: str,
|
text: Optional[str],
|
||||||
expected: bool,
|
expected: bool,
|
||||||
):
|
):
|
||||||
from nonebot.rule import FullmatchRule, fullmatch
|
from nonebot.rule import FullmatchRule, fullmatch
|
||||||
@@ -144,10 +148,10 @@ async def test_fullmatch(
|
|||||||
checker = dependent.call
|
checker = dependent.call
|
||||||
|
|
||||||
assert isinstance(checker, FullmatchRule)
|
assert isinstance(checker, FullmatchRule)
|
||||||
assert checker.msg == {msg} if isinstance(msg, str) else {*msg}
|
assert checker.msg == ((msg,) if isinstance(msg, str) else msg)
|
||||||
assert checker.ignorecase == ignorecase
|
assert checker.ignorecase == ignorecase
|
||||||
|
|
||||||
message = 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
|
assert await dependent(event=event) == expected
|
||||||
|
|
||||||
@@ -158,6 +162,7 @@ async def test_fullmatch(
|
|||||||
[
|
[
|
||||||
(("key",), "message", "_key_", True),
|
(("key",), "message", "_key_", True),
|
||||||
(("key", "foo"), "message", "_foo_", True),
|
(("key", "foo"), "message", "_foo_", True),
|
||||||
|
(("key",), "message", None, False),
|
||||||
(("key",), "notice", "foo", False),
|
(("key",), "notice", "foo", False),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -165,7 +170,7 @@ async def test_keyword(
|
|||||||
app: App,
|
app: App,
|
||||||
kws: Tuple[str, ...],
|
kws: Tuple[str, ...],
|
||||||
type: str,
|
type: str,
|
||||||
text: str,
|
text: Optional[str],
|
||||||
expected: bool,
|
expected: bool,
|
||||||
):
|
):
|
||||||
from nonebot.rule import KeywordsRule, keyword
|
from nonebot.rule import KeywordsRule, keyword
|
||||||
@@ -177,7 +182,7 @@ async def test_keyword(
|
|||||||
assert isinstance(checker, KeywordsRule)
|
assert isinstance(checker, KeywordsRule)
|
||||||
assert checker.keywords == kws
|
assert checker.keywords == kws
|
||||||
|
|
||||||
message = 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
|
assert await dependent(event=event) == expected
|
||||||
|
|
||||||
@@ -195,16 +200,157 @@ async def test_command(app: App, cmds: Tuple[Tuple[str, ...]]):
|
|||||||
checker = dependent.call
|
checker = dependent.call
|
||||||
|
|
||||||
assert isinstance(checker, CommandRule)
|
assert isinstance(checker, CommandRule)
|
||||||
assert checker.cmds == list(cmds)
|
assert checker.cmds == cmds
|
||||||
|
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
state = {PREFIX_KEY: {CMD_KEY: cmd}}
|
state = {PREFIX_KEY: {CMD_KEY: cmd}}
|
||||||
assert await dependent(state=state)
|
assert await dependent(state=state)
|
||||||
|
|
||||||
|
|
||||||
# TODO: shell command
|
@pytest.mark.asyncio
|
||||||
|
async def test_shell_command(app: App):
|
||||||
|
from nonebot.typing import T_State
|
||||||
|
from nonebot.exception import ParserExit
|
||||||
|
from nonebot.consts import CMD_KEY, PREFIX_KEY, SHELL_ARGS, SHELL_ARGV, CMD_ARG_KEY
|
||||||
|
from nonebot.rule import Namespace, ArgumentParser, ShellCommandRule, shell_command
|
||||||
|
|
||||||
# TODO: regex
|
state: T_State
|
||||||
|
CMD = ("test",)
|
||||||
|
Message = make_fake_message()
|
||||||
|
MessageSegment = Message.get_segment_class()
|
||||||
|
|
||||||
|
test_not_cmd = shell_command(CMD)
|
||||||
|
dependent = list(test_not_cmd.checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
assert isinstance(checker, ShellCommandRule)
|
||||||
|
message = Message()
|
||||||
|
event = make_fake_event(_message=message)()
|
||||||
|
state = {PREFIX_KEY: {CMD_KEY: ("not",), CMD_ARG_KEY: message}}
|
||||||
|
assert not await dependent(event=event, state=state)
|
||||||
|
|
||||||
|
test_no_parser = shell_command(CMD)
|
||||||
|
dependent = list(test_no_parser.checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
assert isinstance(checker, ShellCommandRule)
|
||||||
|
message = Message()
|
||||||
|
event = make_fake_event(_message=message)()
|
||||||
|
state = {PREFIX_KEY: {CMD_KEY: CMD, CMD_ARG_KEY: message}}
|
||||||
|
assert await dependent(event=event, state=state)
|
||||||
|
assert state[SHELL_ARGV] == []
|
||||||
|
assert SHELL_ARGS not in state
|
||||||
|
|
||||||
|
parser = ArgumentParser("test")
|
||||||
|
parser.add_argument("-a", required=True)
|
||||||
|
|
||||||
|
test_simple_parser = shell_command(CMD, parser=parser)
|
||||||
|
dependent = list(test_simple_parser.checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
assert isinstance(checker, ShellCommandRule)
|
||||||
|
message = Message("-a 1")
|
||||||
|
event = make_fake_event(_message=message)()
|
||||||
|
state = {PREFIX_KEY: {CMD_KEY: CMD, CMD_ARG_KEY: message}}
|
||||||
|
assert await dependent(event=event, state=state)
|
||||||
|
assert state[SHELL_ARGV] == ["-a", "1"]
|
||||||
|
assert state[SHELL_ARGS] == Namespace(a="1")
|
||||||
|
|
||||||
|
test_parser_help = shell_command(CMD, parser=parser)
|
||||||
|
dependent = list(test_parser_help.checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
assert isinstance(checker, ShellCommandRule)
|
||||||
|
message = Message("-h")
|
||||||
|
event = make_fake_event(_message=message)()
|
||||||
|
state = {PREFIX_KEY: {CMD_KEY: CMD, CMD_ARG_KEY: message}}
|
||||||
|
assert await dependent(event=event, state=state)
|
||||||
|
assert state[SHELL_ARGV] == ["-h"]
|
||||||
|
assert isinstance(state[SHELL_ARGS], ParserExit)
|
||||||
|
assert state[SHELL_ARGS].status == 0
|
||||||
|
assert state[SHELL_ARGS].message == parser.format_help()
|
||||||
|
|
||||||
|
test_parser_error = shell_command(CMD, parser=parser)
|
||||||
|
dependent = list(test_parser_error.checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
assert isinstance(checker, ShellCommandRule)
|
||||||
|
message = Message()
|
||||||
|
event = make_fake_event(_message=message)()
|
||||||
|
state = {PREFIX_KEY: {CMD_KEY: CMD, CMD_ARG_KEY: message}}
|
||||||
|
assert await dependent(event=event, state=state)
|
||||||
|
assert state[SHELL_ARGV] == []
|
||||||
|
assert isinstance(state[SHELL_ARGS], ParserExit)
|
||||||
|
assert state[SHELL_ARGS].status != 0
|
||||||
|
|
||||||
|
test_message_parser = shell_command(CMD, parser=parser)
|
||||||
|
dependent = list(test_message_parser.checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
assert isinstance(checker, ShellCommandRule)
|
||||||
|
message = MessageSegment.text("-a") + MessageSegment.image("test")
|
||||||
|
event = make_fake_event(_message=message)()
|
||||||
|
state = {PREFIX_KEY: {CMD_KEY: CMD, CMD_ARG_KEY: message}}
|
||||||
|
assert await dependent(event=event, state=state)
|
||||||
|
assert state[SHELL_ARGV] == ["-a", MessageSegment.image("test")]
|
||||||
|
assert state[SHELL_ARGS] == Namespace(a=MessageSegment.image("test"))
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 9):
|
||||||
|
parser = ArgumentParser("test", exit_on_error=False)
|
||||||
|
parser.add_argument("-a", required=True)
|
||||||
|
|
||||||
|
test_not_exit = shell_command(CMD, parser=parser)
|
||||||
|
dependent = list(test_not_exit.checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
assert isinstance(checker, ShellCommandRule)
|
||||||
|
message = Message()
|
||||||
|
event = make_fake_event(_message=message)()
|
||||||
|
state = {PREFIX_KEY: {CMD_KEY: CMD, CMD_ARG_KEY: message}}
|
||||||
|
assert await dependent(event=event, state=state)
|
||||||
|
assert state[SHELL_ARGV] == []
|
||||||
|
assert isinstance(state[SHELL_ARGS], ParserExit)
|
||||||
|
assert state[SHELL_ARGS].status != 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"pattern,type,text,expected,matched,group,dict",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
r"(?P<key>key\d)",
|
||||||
|
"message",
|
||||||
|
"_key1_",
|
||||||
|
True,
|
||||||
|
"key1",
|
||||||
|
("key1",),
|
||||||
|
{"key": "key1"},
|
||||||
|
),
|
||||||
|
(r"foo", "message", None, False, None, None, None),
|
||||||
|
(r"foo", "notice", "foo", False, None, None, None),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_regex(
|
||||||
|
app: App,
|
||||||
|
pattern: str,
|
||||||
|
type: str,
|
||||||
|
text: Optional[str],
|
||||||
|
expected: bool,
|
||||||
|
matched: Optional[str],
|
||||||
|
group: Optional[Tuple[str, ...]],
|
||||||
|
dict: Optional[Dict[str, str]],
|
||||||
|
):
|
||||||
|
from nonebot.typing import T_State
|
||||||
|
from nonebot.rule import RegexRule, regex
|
||||||
|
from nonebot.consts import REGEX_DICT, REGEX_GROUP, REGEX_MATCHED
|
||||||
|
|
||||||
|
test_regex = regex(pattern)
|
||||||
|
dependent = list(test_regex.checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
|
||||||
|
assert isinstance(checker, RegexRule)
|
||||||
|
assert checker.regex == pattern
|
||||||
|
|
||||||
|
message = text if text is None else make_fake_message()(text)
|
||||||
|
event = make_fake_event(_type=type, _message=message)()
|
||||||
|
state = {}
|
||||||
|
assert await dependent(event=event, state=state) == expected
|
||||||
|
assert state.get(REGEX_MATCHED) == matched
|
||||||
|
assert state.get(REGEX_GROUP) == group
|
||||||
|
assert state.get(REGEX_DICT) == dict
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -212,11 +358,32 @@ async def test_command(app: App, cmds: Tuple[Tuple[str, ...]]):
|
|||||||
async def test_to_me(app: App, expected: bool):
|
async def test_to_me(app: App, expected: bool):
|
||||||
from nonebot.rule import ToMeRule, to_me
|
from nonebot.rule import ToMeRule, to_me
|
||||||
|
|
||||||
test_keyword = to_me()
|
test_to_me = to_me()
|
||||||
dependent = list(test_keyword.checkers)[0]
|
dependent = list(test_to_me.checkers)[0]
|
||||||
checker = dependent.call
|
checker = dependent.call
|
||||||
|
|
||||||
assert isinstance(checker, ToMeRule)
|
assert isinstance(checker, ToMeRule)
|
||||||
|
|
||||||
event = make_fake_event(_to_me=expected)()
|
event = make_fake_event(_to_me=expected)()
|
||||||
assert await dependent(event=event) == expected
|
assert await dependent(event=event) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_is_type(app: App):
|
||||||
|
from nonebot.rule import IsTypeRule, is_type
|
||||||
|
|
||||||
|
Event1 = make_fake_event()
|
||||||
|
Event2 = make_fake_event()
|
||||||
|
Event3 = make_fake_event()
|
||||||
|
|
||||||
|
test_type = is_type(Event1, Event2)
|
||||||
|
dependent = list(test_type.checkers)[0]
|
||||||
|
checker = dependent.call
|
||||||
|
|
||||||
|
assert isinstance(checker, IsTypeRule)
|
||||||
|
|
||||||
|
event = Event1()
|
||||||
|
assert await dependent(event=event)
|
||||||
|
|
||||||
|
event = Event3()
|
||||||
|
assert not await dependent(event=event)
|
||||||
|
@@ -65,7 +65,7 @@ def make_fake_event(
|
|||||||
_type: str = "message",
|
_type: str = "message",
|
||||||
_name: str = "test",
|
_name: str = "test",
|
||||||
_description: str = "test",
|
_description: str = "test",
|
||||||
_user_id: str = "test",
|
_user_id: Optional[str] = "test",
|
||||||
_session_id: Optional[str] = "test",
|
_session_id: Optional[str] = "test",
|
||||||
_message: Optional["Message"] = None,
|
_message: Optional["Message"] = None,
|
||||||
_to_me: bool = True,
|
_to_me: bool = True,
|
||||||
@@ -86,7 +86,9 @@ def make_fake_event(
|
|||||||
return _description
|
return _description
|
||||||
|
|
||||||
def get_user_id(self) -> str:
|
def get_user_id(self) -> str:
|
||||||
return _user_id
|
if _user_id is not None:
|
||||||
|
return _user_id
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_session_id(self) -> str:
|
def get_session_id(self) -> str:
|
||||||
if _session_id is not None:
|
if _session_id is not None:
|
||||||
|
@@ -8,7 +8,7 @@ slug: /
|
|||||||
|
|
||||||
NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架,它基于 Python 的类型注解和异步特性,能够为你的需求实现提供便捷灵活的支持。
|
NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架,它基于 Python 的类型注解和异步特性,能够为你的需求实现提供便捷灵活的支持。
|
||||||
|
|
||||||
需要注意的是,NoneBot2 仅支持 **Python 3.7.3 以上版本**
|
需要注意的是,NoneBot2 仅支持 **Python 3.8 以上版本**
|
||||||
|
|
||||||
## 特色
|
## 特色
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ import Asciinema from "@site/src/components/Asciinema";
|
|||||||
# 安装 NoneBot2
|
# 安装 NoneBot2
|
||||||
|
|
||||||
:::warning 注意
|
:::warning 注意
|
||||||
请确保你的 Python 版本 >= 3.7.3。
|
请确保你的 Python 版本 >= 3.8。
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::warning 注意
|
:::warning 注意
|
||||||
|
@@ -277,7 +277,7 @@ async def _(foo: str = CommandStart()): ...
|
|||||||
|
|
||||||
### ShellCommandArgs
|
### ShellCommandArgs
|
||||||
|
|
||||||
获取 shell 命令解析后的参数。
|
获取 shell 命令解析后的参数,支持 MessageSegment 富文本(如:图片)。
|
||||||
|
|
||||||
:::tip 提示
|
:::tip 提示
|
||||||
如果参数解析失败,则为 [`ParserExit`](../../api/exception.md#ParserExit) 异常,并携带错误码与错误信息。
|
如果参数解析失败,则为 [`ParserExit`](../../api/exception.md#ParserExit) 异常,并携带错误码与错误信息。
|
||||||
@@ -288,21 +288,28 @@ async def _(foo: str = CommandStart()): ...
|
|||||||
```python {8,12}
|
```python {8,12}
|
||||||
from nonebot import on_shell_command
|
from nonebot import on_shell_command
|
||||||
from nonebot.params import ShellCommandArgs
|
from nonebot.params import ShellCommandArgs
|
||||||
|
from nonebot.rule import Namespace, ArgumentParser
|
||||||
|
|
||||||
|
parser = ArgumentParser("demo")
|
||||||
|
# parser.add_argument ...
|
||||||
matcher = on_shell_command("cmd", parser)
|
matcher = on_shell_command("cmd", parser)
|
||||||
|
|
||||||
# 解析失败
|
# 解析失败
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(foo: ParserExit = ShellCommandArgs()): ...
|
async def _(foo: ParserExit = ShellCommandArgs()):
|
||||||
|
if foo.status == 0:
|
||||||
|
foo.message # help message
|
||||||
|
else:
|
||||||
|
foo.message # error message
|
||||||
|
|
||||||
# 解析成功
|
# 解析成功
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(foo: Dict[str, Any] = ShellCommandArgs()): ...
|
async def _(foo: Namespace = ShellCommandArgs()): ...
|
||||||
```
|
```
|
||||||
|
|
||||||
### ShellCommandArgv
|
### ShellCommandArgv
|
||||||
|
|
||||||
获取 shell 命令解析前的参数列表。
|
获取 shell 命令解析前的参数列表,支持 MessageSegment 富文本(如:图片)。
|
||||||
|
|
||||||
```python {7}
|
```python {7}
|
||||||
from nonebot import on_shell_command
|
from nonebot import on_shell_command
|
||||||
@@ -311,7 +318,7 @@ from nonebot.params import ShellCommandArgs
|
|||||||
matcher = on_shell_command("cmd")
|
matcher = on_shell_command("cmd")
|
||||||
|
|
||||||
@matcher.handle()
|
@matcher.handle()
|
||||||
async def _(foo: List[str] = ShellCommandArgv()): ...
|
async def _(foo: List[Union[str, MessageSegment]] = ShellCommandArgv()): ...
|
||||||
```
|
```
|
||||||
|
|
||||||
### RegexMatched
|
### RegexMatched
|
||||||
|
@@ -5,6 +5,102 @@ toc_max_heading_level: 2
|
|||||||
|
|
||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
|
## v2.0.0-rc.1
|
||||||
|
|
||||||
|
### 💥 破坏性变更
|
||||||
|
|
||||||
|
- Feature: `SUPERUSER` 权限匹配任意超管事件 [@AkiraXie](https://github.com/AkiraXie) ([#1275](https://github.com/nonebot/nonebot2/pull/1275))
|
||||||
|
- Remove: 移除过时的 State 注入参数 [@yanyongyu](https://github.com/yanyongyu) ([#1160](https://github.com/nonebot/nonebot2/pull/1160))
|
||||||
|
- Remove: 移除过时的 `nonebot.plugins` toml 配置 [@yanyongyu](https://github.com/yanyongyu) ([#1151](https://github.com/nonebot/nonebot2/pull/1151))
|
||||||
|
- Remove: 移除 Python 3.7 支持 [@yanyongyu](https://github.com/yanyongyu) ([#1148](https://github.com/nonebot/nonebot2/pull/1148))
|
||||||
|
- Remove: 删除过时的 Export 功能 [@yanyongyu](https://github.com/yanyongyu) ([#1125](https://github.com/nonebot/nonebot2/pull/1125))
|
||||||
|
|
||||||
|
### 🚀 新功能
|
||||||
|
|
||||||
|
- Feature: `SUPERUSER` 权限匹配任意超管事件 [@AkiraXie](https://github.com/AkiraXie) ([#1275](https://github.com/nonebot/nonebot2/pull/1275))
|
||||||
|
- Feature: 改进 `CommandGroup` 与 `MatcherGroup` 的结构 [@A-kirami](https://github.com/A-kirami) ([#1240](https://github.com/nonebot/nonebot2/pull/1240))
|
||||||
|
- Feature: 调整日志输出格式与等级 [@yanyongyu](https://github.com/yanyongyu) ([#1233](https://github.com/nonebot/nonebot2/pull/1233))
|
||||||
|
- Feature: 优化依赖注入结构 [@yanyongyu](https://github.com/yanyongyu) ([#1227](https://github.com/nonebot/nonebot2/pull/1227))
|
||||||
|
- Featue: `load_plugin` 支持 `pathlib.Path` [@Lancercmd](https://github.com/Lancercmd) ([#1194](https://github.com/nonebot/nonebot2/pull/1194))
|
||||||
|
- Feature: 新增事件类型过滤 rule [@yanyongyu](https://github.com/yanyongyu) ([#1183](https://github.com/nonebot/nonebot2/pull/1183))
|
||||||
|
- Feature: shell command 添加富文本支持 [@yanyongyu](https://github.com/yanyongyu) ([#1171](https://github.com/nonebot/nonebot2/pull/1171))
|
||||||
|
|
||||||
|
### 🐛 Bug 修复
|
||||||
|
|
||||||
|
- Fix: 内置规则和权限没有捕获错误 [@yanyongyu](https://github.com/yanyongyu) ([#1291](https://github.com/nonebot/nonebot2/pull/1291))
|
||||||
|
- Fix: 修复 User 会话权限更新嵌套问题 [@yanyongyu](https://github.com/yanyongyu) ([#1208](https://github.com/nonebot/nonebot2/pull/1208))
|
||||||
|
- Fix: 修复当消息与不支持的类型相加时抛出的异常类型错误 [@mnixry](https://github.com/mnixry) ([#1166](https://github.com/nonebot/nonebot2/pull/1166))
|
||||||
|
|
||||||
|
### 💫 杂项
|
||||||
|
|
||||||
|
- Fix: 修正 GenshinUID 的发布类型 [@A-kirami](https://github.com/A-kirami) ([#1243](https://github.com/nonebot/nonebot2/pull/1243))
|
||||||
|
- Remove: 移除未使用的导入 [@A-kirami](https://github.com/A-kirami) ([#1236](https://github.com/nonebot/nonebot2/pull/1236))
|
||||||
|
- Plugin: 更新插件米游社辅助工具 tag [@Ljzd-PRO](https://github.com/Ljzd-PRO) ([#1221](https://github.com/nonebot/nonebot2/pull/1221))
|
||||||
|
- Plugin: 修改插件多功能简易群管信息 [@HuYihe2008](https://github.com/HuYihe2008) ([#1180](https://github.com/nonebot/nonebot2/pull/1180))
|
||||||
|
- Plugin: 修改插件多功能简易群管信息 [@HuYihe2008](https://github.com/HuYihe2008) ([#1159](https://github.com/nonebot/nonebot2/pull/1159))
|
||||||
|
- Plugin: 修改 QQ 续火花插件信息 [@GC-ZF](https://github.com/GC-ZF) ([#1158](https://github.com/nonebot/nonebot2/pull/1158))
|
||||||
|
- Plugin: 修改插件多功能简易群管信息 [@HuYihe2008](https://github.com/HuYihe2008) ([#1154](https://github.com/nonebot/nonebot2/pull/1154))
|
||||||
|
|
||||||
|
### 🍻 插件发布
|
||||||
|
|
||||||
|
- Plugin: 文字识别 [@yanyongyu](https://github.com/yanyongyu) ([#1295](https://github.com/nonebot/nonebot2/pull/1295))
|
||||||
|
- Plugin: 在线编曲 [@yanyongyu](https://github.com/yanyongyu) ([#1293](https://github.com/nonebot/nonebot2/pull/1293))
|
||||||
|
- Plugin: 图灵机器人 [@yanyongyu](https://github.com/yanyongyu) ([#1289](https://github.com/nonebot/nonebot2/pull/1289))
|
||||||
|
- Plugin: PicStatus [@yanyongyu](https://github.com/yanyongyu) ([#1287](https://github.com/nonebot/nonebot2/pull/1287))
|
||||||
|
- Plugin: 阿里云盘福利码自动兑换 [@yanyongyu](https://github.com/yanyongyu) ([#1283](https://github.com/nonebot/nonebot2/pull/1283))
|
||||||
|
- Plugin: gal 角色语音生成 [@yanyongyu](https://github.com/yanyongyu) ([#1281](https://github.com/nonebot/nonebot2/pull/1281))
|
||||||
|
- Plugin: 漂流瓶 [@yanyongyu](https://github.com/yanyongyu) ([#1279](https://github.com/nonebot/nonebot2/pull/1279))
|
||||||
|
- Plugin: BWIKI 助手移植版 [@yanyongyu](https://github.com/yanyongyu) ([#1274](https://github.com/nonebot/nonebot2/pull/1274))
|
||||||
|
- Plugin: nonebot 物联网插件 [@yanyongyu](https://github.com/yanyongyu) ([#1265](https://github.com/nonebot/nonebot2/pull/1265))
|
||||||
|
- Plugin: 狼人杀插件 [@yanyongyu](https://github.com/yanyongyu) ([#1252](https://github.com/nonebot/nonebot2/pull/1252))
|
||||||
|
- Plugin: ayaka - 文字游戏开发辅助插件 [@yanyongyu](https://github.com/yanyongyu) ([#1254](https://github.com/nonebot/nonebot2/pull/1254))
|
||||||
|
- Plugin: 图像超分辨率重建 [@yanyongyu](https://github.com/yanyongyu) ([#1250](https://github.com/nonebot/nonebot2/pull/1250))
|
||||||
|
- Plugin: Minecraft Server 聊天同步 [@yanyongyu](https://github.com/yanyongyu) ([#1245](https://github.com/nonebot/nonebot2/pull/1245))
|
||||||
|
- Plugin: 查询 ETH 合并日期 [@yanyongyu](https://github.com/yanyongyu) ([#1232](https://github.com/nonebot/nonebot2/pull/1232))
|
||||||
|
- Plugin: 星际战甲事件查询 [@yanyongyu](https://github.com/yanyongyu) ([#1220](https://github.com/nonebot/nonebot2/pull/1220))
|
||||||
|
- Plugin: 米游社辅助工具 [@yanyongyu](https://github.com/yanyongyu) ([#1218](https://github.com/nonebot/nonebot2/pull/1218))
|
||||||
|
- Plugin: 原神每日材料查询 [@yanyongyu](https://github.com/yanyongyu) ([#1216](https://github.com/nonebot/nonebot2/pull/1216))
|
||||||
|
- Plugin: MC_QQ_MCRcon [@yanyongyu](https://github.com/yanyongyu) ([#1211](https://github.com/nonebot/nonebot2/pull/1211))
|
||||||
|
- Plugin: 原神角色展柜查询 [@yanyongyu](https://github.com/yanyongyu) ([#1209](https://github.com/nonebot/nonebot2/pull/1209))
|
||||||
|
- Plugin: 修仙模拟器 [@yanyongyu](https://github.com/yanyongyu) ([#1202](https://github.com/nonebot/nonebot2/pull/1202))
|
||||||
|
- Plugin: 赛博浅草寺 [@yanyongyu](https://github.com/yanyongyu) ([#1206](https://github.com/nonebot/nonebot2/pull/1206))
|
||||||
|
- Plugin: 不背单词 [@yanyongyu](https://github.com/yanyongyu) ([#1204](https://github.com/nonebot/nonebot2/pull/1204))
|
||||||
|
- Plugin: 自识别 todo [@yanyongyu](https://github.com/yanyongyu) ([#1193](https://github.com/nonebot/nonebot2/pull/1193))
|
||||||
|
- Plugin: 雨课堂自动签到 [@yanyongyu](https://github.com/yanyongyu) ([#1189](https://github.com/nonebot/nonebot2/pull/1189))
|
||||||
|
- Plugin: 反馈及通知 [@yanyongyu](https://github.com/yanyongyu) ([#1187](https://github.com/nonebot/nonebot2/pull/1187))
|
||||||
|
- Plugin: MagiaDice 骰娘及 TRPGLOG [@yanyongyu](https://github.com/yanyongyu) ([#1185](https://github.com/nonebot/nonebot2/pull/1185))
|
||||||
|
- Plugin: 面麻小助手 [@yanyongyu](https://github.com/yanyongyu) ([#1191](https://github.com/nonebot/nonebot2/pull/1191))
|
||||||
|
- Plugin: 话痨排行榜 [@yanyongyu](https://github.com/yanyongyu) ([#1182](https://github.com/nonebot/nonebot2/pull/1182))
|
||||||
|
- Plugin: 保存群聊闪照 [@yanyongyu](https://github.com/yanyongyu) ([#1179](https://github.com/nonebot/nonebot2/pull/1179))
|
||||||
|
- Plugin: 课表查询 [@yanyongyu](https://github.com/yanyongyu) ([#1168](https://github.com/nonebot/nonebot2/pull/1168))
|
||||||
|
- Plugin: 业余无线电助手 [@yanyongyu](https://github.com/yanyongyu) ([#1173](https://github.com/nonebot/nonebot2/pull/1173))
|
||||||
|
- Plugin: NoneBot 树形帮助插件 [@yanyongyu](https://github.com/yanyongyu) ([#1177](https://github.com/nonebot/nonebot2/pull/1177))
|
||||||
|
- Plugin: 工作性价比 [@yanyongyu](https://github.com/yanyongyu) ([#1175](https://github.com/nonebot/nonebot2/pull/1175))
|
||||||
|
- Plugin: 娶群友 [@yanyongyu](https://github.com/yanyongyu) ([#1170](https://github.com/nonebot/nonebot2/pull/1170))
|
||||||
|
- Plugin: PixivBot [@yanyongyu](https://github.com/yanyongyu) ([#1165](https://github.com/nonebot/nonebot2/pull/1165))
|
||||||
|
- Plugin: 日韩中 VITS 模型原神拟声 [@yanyongyu](https://github.com/yanyongyu) ([#1162](https://github.com/nonebot/nonebot2/pull/1162))
|
||||||
|
- Plugin: 每日人品 [@yanyongyu](https://github.com/yanyongyu) ([#1156](https://github.com/nonebot/nonebot2/pull/1156))
|
||||||
|
- Plugin: nonebot-plugin-drawer [@yanyongyu](https://github.com/yanyongyu) ([#1146](https://github.com/nonebot/nonebot2/pull/1146))
|
||||||
|
- Plugin: 小游戏合集 [@yanyongyu](https://github.com/yanyongyu) ([#1150](https://github.com/nonebot/nonebot2/pull/1150))
|
||||||
|
- Plugin: 简易群管(带入群欢迎) [@yanyongyu](https://github.com/yanyongyu) ([#1142](https://github.com/nonebot/nonebot2/pull/1142))
|
||||||
|
- Plugin: wiki 条目搜索、获取简介 [@yanyongyu](https://github.com/yanyongyu) ([#1133](https://github.com/nonebot/nonebot2/pull/1133))
|
||||||
|
- Plugin: bangumi 搜索 [@yanyongyu](https://github.com/yanyongyu) ([#1137](https://github.com/nonebot/nonebot2/pull/1137))
|
||||||
|
- Plugin: 疫情小助手-频道版 [@yanyongyu](https://github.com/yanyongyu) ([#1131](https://github.com/nonebot/nonebot2/pull/1131))
|
||||||
|
- Plugin: MC_QQ 通信 [@yanyongyu](https://github.com/yanyongyu) ([#1127](https://github.com/nonebot/nonebot2/pull/1127))
|
||||||
|
- Plugin: BAWiki [@yanyongyu](https://github.com/yanyongyu) ([#1129](https://github.com/nonebot/nonebot2/pull/1129))
|
||||||
|
|
||||||
|
### 🍻 机器人发布
|
||||||
|
|
||||||
|
- Bot: IdhagnBot [@yanyongyu](https://github.com/yanyongyu) ([#1267](https://github.com/nonebot/nonebot2/pull/1267))
|
||||||
|
- Bot: LittlePaimon [@yanyongyu](https://github.com/yanyongyu) ([#1256](https://github.com/nonebot/nonebot2/pull/1256))
|
||||||
|
- Bot: GenshinUID [@yanyongyu](https://github.com/yanyongyu) ([#1226](https://github.com/nonebot/nonebot2/pull/1226))
|
||||||
|
- Bot: 小白机器人 [@yanyongyu](https://github.com/yanyongyu) ([#1224](https://github.com/nonebot/nonebot2/pull/1224))
|
||||||
|
|
||||||
|
### 🍻 适配器发布
|
||||||
|
|
||||||
|
- Adapter: GitHub [@yanyongyu](https://github.com/yanyongyu) ([#1297](https://github.com/nonebot/nonebot2/pull/1297))
|
||||||
|
- Adapter: Console [@yanyongyu](https://github.com/yanyongyu) ([#1213](https://github.com/nonebot/nonebot2/pull/1213))
|
||||||
|
|
||||||
## v2.0.0-beta.5
|
## v2.0.0-beta.5
|
||||||
|
|
||||||
### 🚀 新功能
|
### 🚀 新功能
|
||||||
|
@@ -78,5 +78,25 @@
|
|||||||
"homepage": "https://onebot.adapters.nonebot.dev/",
|
"homepage": "https://onebot.adapters.nonebot.dev/",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"is_official": true
|
"is_official": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot.adapters.console",
|
||||||
|
"project_link": "nonebot-adapter-console",
|
||||||
|
"name": "Console",
|
||||||
|
"desc": "基于终端的交互式适配器",
|
||||||
|
"author": "Melodyknit",
|
||||||
|
"homepage": "https://github.com/nonebot/adapter-console",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot.adapters.github",
|
||||||
|
"project_link": "nonebot-adapter-github",
|
||||||
|
"name": "GitHub",
|
||||||
|
"desc": "GitHub APP & OAuth APP integration",
|
||||||
|
"author": "yanyongyu",
|
||||||
|
"homepage": "https://github.com/nonebot/adapter-github",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": true
|
||||||
}
|
}
|
||||||
]
|
]
|
@@ -233,5 +233,48 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_official": false
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "小白机器人",
|
||||||
|
"desc": "一个高度依赖数据库的群管理机器人",
|
||||||
|
"author": "SDIJF1521",
|
||||||
|
"homepage": "https://github.com/SDIJF1521/qqai",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "群管理",
|
||||||
|
"color": "#ea5252"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "新人作品",
|
||||||
|
"color": "#ea5252"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LittlePaimon",
|
||||||
|
"desc": "小派蒙,多功能原神机器人。",
|
||||||
|
"author": "CMHopeSunshine",
|
||||||
|
"homepage": "https://github.com/CMHopeSunshine/LittlePaimon",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "原神",
|
||||||
|
"color": "#7a52ea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IdhagnBot",
|
||||||
|
"desc": "🐱🤖 一个以娱乐功能为主的缝合怪(划掉)QQ机器人,包含一定Furry要素但是不会卖萌(就是逊啦!)",
|
||||||
|
"author": "su226",
|
||||||
|
"homepage": "https://github.com/su226/IdhagnBot",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "a:OneBot",
|
||||||
|
"color": "#ea5252"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
}
|
}
|
||||||
]
|
]
|
@@ -1974,7 +1974,7 @@
|
|||||||
"module_name": "nonebot_plugin_firexN",
|
"module_name": "nonebot_plugin_firexN",
|
||||||
"project_link": "nonebot-plugin-firexN",
|
"project_link": "nonebot-plugin-firexN",
|
||||||
"name": "一起燚xN吧",
|
"name": "一起燚xN吧",
|
||||||
"desc": "QQ续火花,可以自定义多个联系人、消息内容、发送时间",
|
"desc": "QQ续火花,可以自定义多个联系人、消息内容、发送时间,支持群聊定时消息",
|
||||||
"author": "GC-ZF",
|
"author": "GC-ZF",
|
||||||
"homepage": "https://github.com/GC-ZF/nonebot_plugin_firexN",
|
"homepage": "https://github.com/GC-ZF/nonebot_plugin_firexN",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
@@ -2125,5 +2125,757 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_official": false
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_bawiki",
|
||||||
|
"project_link": "nonebot-plugin-bawiki",
|
||||||
|
"name": "BAWiki",
|
||||||
|
"desc": "基于 NoneBot2 的碧蓝档案 Wiki 插件",
|
||||||
|
"author": "lgc2333",
|
||||||
|
"homepage": "https://github.com/lgc2333/nonebot-plugin-bawiki/",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "t:碧蓝档案",
|
||||||
|
"color": "#00d7fb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:蔚藍檔案",
|
||||||
|
"color": "#00d7fb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_mcqq",
|
||||||
|
"project_link": "nonebot-plugin-mcqq",
|
||||||
|
"name": "MC_QQ通信",
|
||||||
|
"desc": "基于NoneBot的与Minecraft Server互通消息的插件",
|
||||||
|
"author": "17TheWord",
|
||||||
|
"homepage": "https://github.com/17TheWord/nonebot-plugin-mcqq",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "Minecraft",
|
||||||
|
"color": "#52ea6f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "消息互通",
|
||||||
|
"color": "#52eadf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_covid_19_by_guild",
|
||||||
|
"project_link": "nonebot-plugin-covid-19-by-guild",
|
||||||
|
"name": "疫情小助手-频道版",
|
||||||
|
"desc": "👉 疫情小助手 频道版👈",
|
||||||
|
"author": "bingqiu456",
|
||||||
|
"homepage": "https://github.com/bingqiu456/nonebot-plugin-covid-19-by-guild",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "a:QQ频道",
|
||||||
|
"color": "#ea5252"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:疫情小助手",
|
||||||
|
"color": "#526fea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot-plugin-bangumi-search",
|
||||||
|
"project_link": "nonebot-plugin-bangumi-search",
|
||||||
|
"name": "bangumi搜索",
|
||||||
|
"desc": "搜索番剧,并且返回简介栏和收藏盒",
|
||||||
|
"author": "Ankhyty",
|
||||||
|
"homepage": "https://github.com/Ankhyty/nonebot-plugin-bangumi-search",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "a:onebot",
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:漫研",
|
||||||
|
"color": "#2f48e0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "bangumi",
|
||||||
|
"color": "#de2968"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_wiki",
|
||||||
|
"project_link": "nonebot-plugin-wiki",
|
||||||
|
"name": "wiki条目搜索、获取简介",
|
||||||
|
"desc": "通过mediawiki api进行条目搜索、生成简介(biliwiki部分通过网页解析实现)",
|
||||||
|
"author": "ZombieFly",
|
||||||
|
"homepage": "https://github.com/ZombieFly/nb2-wiki",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_groupmanager",
|
||||||
|
"project_link": "nonebot-plugin-groupmanager",
|
||||||
|
"name": "多功能群管",
|
||||||
|
"desc": "与简易群管属于同个产品,就是多了一个入群欢迎,感谢@幼稚园园长 开发的原版插件的帮助",
|
||||||
|
"author": "HuYihe2008",
|
||||||
|
"homepage": "https://github.com/HuYihe2008/nonebot_plugin_groupmanager",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "简易群管",
|
||||||
|
"color": "#53e950"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "插件改良",
|
||||||
|
"color": "#2b7be2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_game_collection",
|
||||||
|
"project_link": "nonebot_plugin_game_collection",
|
||||||
|
"name": "小游戏合集",
|
||||||
|
"desc": "改自 nonebot_plugin_russian 合并了nonebot_plugin_horserace还有一些自编玩法的小游戏合集。",
|
||||||
|
"author": "KarisAya",
|
||||||
|
"homepage": "https://github.com/KarisAya/nonebot_plugin_game_collection",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_drawer",
|
||||||
|
"project_link": "nonebot-plugin-drawer",
|
||||||
|
"name": "nonebot-plugin-drawer",
|
||||||
|
"desc": "适用于 Nonebot2 的AI画画插件",
|
||||||
|
"author": "CrazyBoyM",
|
||||||
|
"homepage": "https://github.com/CrazyBoyM/nonebot-plugin-drawer",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_jrrp-n",
|
||||||
|
"project_link": "nonebot-plugin-jrrp-n",
|
||||||
|
"name": "每日人品",
|
||||||
|
"desc": "不基于random的每日人品插件",
|
||||||
|
"author": "SkyDynamic",
|
||||||
|
"homepage": "https://github.com/SkyDynamic/nonebot_plugin_jrrp",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "每日人品",
|
||||||
|
"color": "#ea5252"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "jrrp",
|
||||||
|
"color": "#529fea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_moegoe",
|
||||||
|
"project_link": "nonebot-plugin-moegoe",
|
||||||
|
"name": "日韩中 VITS 模型原神拟声",
|
||||||
|
"desc": "现在是琪亚娜时间~",
|
||||||
|
"author": "Yiyuiii",
|
||||||
|
"homepage": "https://github.com/Yiyuiii/nonebot-plugin-moegoe",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_pixivbot",
|
||||||
|
"project_link": "nonebot-plugin-pixivbot",
|
||||||
|
"name": "PixivBot",
|
||||||
|
"desc": "NoneBot插件,发送随机Pixiv插画、画师更新推送、定时订阅推送……",
|
||||||
|
"author": "ssttkkl",
|
||||||
|
"homepage": "https://github.com/ssttkkl/nonebot-plugin-pixivbot",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_groupmate_waifu",
|
||||||
|
"project_link": "nonebot_plugin_groupmate_waifu",
|
||||||
|
"name": "娶群友",
|
||||||
|
"desc": "娶群友(纯爱版,ntr版)",
|
||||||
|
"author": "KarisAya",
|
||||||
|
"homepage": "https://github.com/KarisAya/nonebot_plugin_groupmate_waifu",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "娶群友",
|
||||||
|
"color": "#529bea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_workscore",
|
||||||
|
"project_link": "nonebot-plugin-workscore",
|
||||||
|
"name": "工作性价比",
|
||||||
|
"desc": "看看你的工作惨不惨",
|
||||||
|
"author": "yzyyz1387",
|
||||||
|
"homepage": "https://github.com/yzyyz1387/nonebot_plugin_workscore",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "工作性价比计算器",
|
||||||
|
"color": "#3898fc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_treehelp",
|
||||||
|
"project_link": "nonebot-plugin-treehelp",
|
||||||
|
"name": "NoneBot 树形帮助插件",
|
||||||
|
"desc": "利用插件元数据生成插件帮助,并支持插件嵌套。",
|
||||||
|
"author": "he0119",
|
||||||
|
"homepage": "https://github.com/he0119/nonebot-plugin-treehelp",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "cqsat",
|
||||||
|
"project_link": "nonebot-plugin-cqsat",
|
||||||
|
"name": "业余无线电助手",
|
||||||
|
"desc": "一个业余无线电相关的插件",
|
||||||
|
"author": "yzyyz1387",
|
||||||
|
"homepage": "https://github.com/yzyyz1387/cqsat",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "业余无线电",
|
||||||
|
"color": "#ea5252"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "HAM",
|
||||||
|
"color": "#3898fc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "卫星追踪",
|
||||||
|
"color": "#fca638"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_course",
|
||||||
|
"project_link": "nonebot-plugin-course",
|
||||||
|
"name": "课表查询",
|
||||||
|
"desc": "本周课表,完整课表,下节课在哪上,今天还有什么课🤔",
|
||||||
|
"author": "InariInDream",
|
||||||
|
"homepage": "https://github.com/InariInDream/nonebot_plugin_course",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "课表",
|
||||||
|
"color": "#6e9af2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_flash_silentsave",
|
||||||
|
"project_link": "nonebot_plugin_flash_silentsave",
|
||||||
|
"name": "保存群聊闪照",
|
||||||
|
"desc": "静默保存群聊中的闪照",
|
||||||
|
"author": "KarisAya",
|
||||||
|
"homepage": "https://github.com/KarisAya/nonebot_plugin_flash_silentsave",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "反闪照",
|
||||||
|
"color": "#52a5ea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_dialectlist",
|
||||||
|
"project_link": "nonebot-plugin-dialectlist",
|
||||||
|
"name": "话痨排行榜",
|
||||||
|
"desc": "给话多的群友一个排行",
|
||||||
|
"author": "X-Skirt-X",
|
||||||
|
"homepage": "https://github.com/X-Skirt-X/nonebot_plugin_dialectlist",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_offline_mahjong_helper",
|
||||||
|
"project_link": "nonebot-plugin-offline-mahjong-helper",
|
||||||
|
"name": "面麻小助手",
|
||||||
|
"desc": "可约桌、算点、精算、雀魂查询的面麻助手。",
|
||||||
|
"author": "Nranphy",
|
||||||
|
"homepage": "https://github.com/Nranphy/nonebot_plugin_offline_mahjong_helper",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "Mahjong",
|
||||||
|
"color": "#ea5252"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "雀魂",
|
||||||
|
"color": "#eaa452"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "线下约桌",
|
||||||
|
"color": "#52a6ea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot-plugin-magiadice",
|
||||||
|
"project_link": "nonebot-plugin-magiadice",
|
||||||
|
"name": "MagiaDice骰娘及TRPGLOG",
|
||||||
|
"desc": "能够实时在网页围观的TRPG记录插件,同时具有骰娘的功能",
|
||||||
|
"author": "sena-nana",
|
||||||
|
"homepage": "https://github.com/sena-nana/MutsukiBot/tree/main/nonebot-plugin-magiadice",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "TRPG",
|
||||||
|
"color": "#ea5252"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot-plugin-send",
|
||||||
|
"project_link": "nonebot-plugin-send",
|
||||||
|
"name": "反馈及通知",
|
||||||
|
"desc": "用户通过bot向superuser发送反馈,以及bot向全群发送通知",
|
||||||
|
"author": "sena-nana",
|
||||||
|
"homepage": "https://github.com/sena-nana/MutsukiBot/tree/main/nonebot-plugin-send",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "send",
|
||||||
|
"color": "#ea5252"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "notice",
|
||||||
|
"color": "#ea5252"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "公告",
|
||||||
|
"color": "#ea5252"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot-plugin-ykt",
|
||||||
|
"project_link": "nonebot-plugin-ykt",
|
||||||
|
"name": "雨课堂自动签到",
|
||||||
|
"desc": "使用playwright模拟浏览器签到雨课堂,并将腾讯会议号发送给superuser",
|
||||||
|
"author": "sena-nana",
|
||||||
|
"homepage": "https://github.com/sena-nana/MutsukiBot/tree/main/nonebot-plugin-ykt",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "playwright",
|
||||||
|
"color": "#ea5252"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_todo_nlp",
|
||||||
|
"project_link": "nonebot-plugin-todo-nlp",
|
||||||
|
"name": "自识别todo",
|
||||||
|
"desc": "一款自动识别提醒内容,可生成todo图片并定时推送的nonebot2插件,v11适配器可用",
|
||||||
|
"author": "CofinCup",
|
||||||
|
"homepage": "https://github.com/CofinCup/nonebot-plugin-todo-nlp",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "t:todo",
|
||||||
|
"color": "#499bdd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:nlp",
|
||||||
|
"color": "#83b279"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_wordsnorote",
|
||||||
|
"project_link": "nonebot-plugin-wordsnorote",
|
||||||
|
"name": "不背单词",
|
||||||
|
"desc": "哥们哥们,背单词么?哥们!",
|
||||||
|
"author": "GC-ZF",
|
||||||
|
"homepage": "https://github.com/GC-ZF/nonebot_plugin_wordsnorote",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "四六级",
|
||||||
|
"color": "#24a0d8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_CyberSensoji",
|
||||||
|
"project_link": "nonebot-plugin-CyberSensoji",
|
||||||
|
"name": "赛博浅草寺",
|
||||||
|
"desc": "随机抽取浅草寺的一签运势",
|
||||||
|
"author": "Raidenneox",
|
||||||
|
"homepage": "https://github.com/Raidenneox/nonebot_plugin_CyberSensoji",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "t:抽签",
|
||||||
|
"color": "#52eadf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_xiuxian",
|
||||||
|
"project_link": "nonebot-plugin-xiuxian",
|
||||||
|
"name": "修仙模拟器",
|
||||||
|
"desc": "加入群内修仙吧!",
|
||||||
|
"author": "s52047qwas",
|
||||||
|
"homepage": "https://github.com/s52047qwas/nonebot_plugin_xiuxian",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_gspanel",
|
||||||
|
"project_link": "nonebot-plugin-gspanel",
|
||||||
|
"name": "原神角色展柜查询",
|
||||||
|
"desc": "移植自 @yoimiya-kokomi/miao-plugin 的原神游戏内角色展柜数据查询",
|
||||||
|
"author": "monsterxcn",
|
||||||
|
"homepage": "https://github.com/monsterxcn/nonebot-plugin-gspanel",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "t:Genshin",
|
||||||
|
"color": "#ffd49f"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_mcqq_mcrcon",
|
||||||
|
"project_link": "nonebot-plugin-mcqq-mcrcon",
|
||||||
|
"name": "MC_QQ_MCRcon",
|
||||||
|
"desc": "基于NoneBot的QQ群聊与Minecraft Server消息互通插件",
|
||||||
|
"author": "17TheWord",
|
||||||
|
"homepage": "https://github.com/17TheWord/nonebot-plugin-mcqq/tree/mcqq_mcrcon",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "Minecraft",
|
||||||
|
"color": "#4aea7a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "消息互通",
|
||||||
|
"color": "#32ddea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_gsmaterial",
|
||||||
|
"project_link": "nonebot-plugin-gsmaterial",
|
||||||
|
"name": "原神每日材料查询",
|
||||||
|
"desc": "原神每日天赋培养、武器突破材料查询,通过 Project Amber 数据库(支持部分尚未实装角色)自动更新每日数据~",
|
||||||
|
"author": "monsterxcn",
|
||||||
|
"homepage": "https://github.com/monsterxcn/nonebot-plugin-gsmaterial",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "t:Genshin",
|
||||||
|
"color": "#ffd49f"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_mystool",
|
||||||
|
"project_link": "nonebot-plugin-mystool",
|
||||||
|
"name": "米游社辅助工具",
|
||||||
|
"desc": "每日完成米游币任务、游戏签到、商品兑换、免抓包登录",
|
||||||
|
"author": "Ljzd-PRO",
|
||||||
|
"homepage": "https://github.com/Ljzd-PRO/nonebot-plugin-mystool",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "a:onebot",
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:米游社",
|
||||||
|
"color": "#66e0ff"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:原神",
|
||||||
|
"color": "#faf3c4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_warframe",
|
||||||
|
"project_link": "nonebot-plugin-warframe",
|
||||||
|
"name": "星际战甲事件查询",
|
||||||
|
"desc": "基于NoneBot的星际战甲事件查询插件",
|
||||||
|
"author": "17TheWord",
|
||||||
|
"homepage": "https://github.com/17TheWord/nonebot-plugin-warframe",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "星际战甲",
|
||||||
|
"color": "#ed3f3f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "WarFrame",
|
||||||
|
"color": "#edea3f"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "ETH_Terminal",
|
||||||
|
"project_link": "nonebot2-plugin-eth-terminal",
|
||||||
|
"name": "查询ETH合并日期",
|
||||||
|
"desc": "矿难!!!!",
|
||||||
|
"author": "Sclock",
|
||||||
|
"homepage": "https://github.com/Sclock/nonebot2_plugin_eth_terminal",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "爬虫",
|
||||||
|
"color": "#ea5252"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "ETH",
|
||||||
|
"color": "#5285ea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_mcqq_server",
|
||||||
|
"project_link": "nonebot-plugin-mcqq-server",
|
||||||
|
"name": "Minecraft Server 聊天同步",
|
||||||
|
"desc": "采用本地读取log信息的方法的Minecraft Server互通消息的插件,mcqq服主版。",
|
||||||
|
"author": "KarisAya",
|
||||||
|
"homepage": "https://github.com/KarisAya/nonebot_plugin_mcqq_server",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "Minecraft",
|
||||||
|
"color": "#52ea64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "消息互通",
|
||||||
|
"color": "#52e5ea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "GenshinUID",
|
||||||
|
"project_link": "git+https://github.com/KimigaiiWuyi/GenshinUID.git@nonebot2-beta1#egg=GenshinUID",
|
||||||
|
"name": "GenshinUID",
|
||||||
|
"desc": "原神Uid查询/原神Wiki/米社签到/树脂提醒插件",
|
||||||
|
"author": "KimigaiiWuyi",
|
||||||
|
"homepage": "https://github.com/KimigaiiWuyi/GenshinUID/tree/nonebot2-beta1",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "原神",
|
||||||
|
"color": "#8edffb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_RealESRGAN",
|
||||||
|
"project_link": "nonebot_plugin_RealESRGAN",
|
||||||
|
"name": "图像超分辨率重建",
|
||||||
|
"desc": "对图像进行超分辨率重建,可以理解成将图像变大变清晰,不过线上重建的效果没有本地的好,具体可以查阅仓库",
|
||||||
|
"author": "ppxxxg22",
|
||||||
|
"homepage": "https://github.com/ppxxxg22/nonebot_plugin_RealESRGAN",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "图像超分辨率重建",
|
||||||
|
"color": "#ea5252"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "ayaka",
|
||||||
|
"project_link": "nonebot-plugin-ayaka",
|
||||||
|
"name": "ayaka - 文字游戏开发辅助插件",
|
||||||
|
"desc": "提供了状态机、命令隔离、缓存、固存、帮助指令等功能,可以管理多个文字游戏避免命令冲突,帮助开发者在nonebot2框架基础上快速开发qq文字游戏",
|
||||||
|
"author": "bridgeL",
|
||||||
|
"homepage": "https://github.com/bridgeL/nonebot-plugin-ayaka",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "a:cqhttp",
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "a:onebot",
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:文字游戏",
|
||||||
|
"color": "#e36306"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot-plugin-wolf-kill",
|
||||||
|
"project_link": "nonebot-plugin-wolf-kill",
|
||||||
|
"name": "狼人杀插件",
|
||||||
|
"desc": "在群里玩狼人杀",
|
||||||
|
"author": "AbCooly",
|
||||||
|
"homepage": "https://github.com/AbCooly/nonebot_plugin_wolf_kill",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "iot",
|
||||||
|
"project_link": "nonebot-plugin-iot",
|
||||||
|
"name": "nonebot物联网插件",
|
||||||
|
"desc": "为nonebot接入物联网提供方案,目前支持天猫精灵终端,后续会增加",
|
||||||
|
"author": "littlebutt",
|
||||||
|
"homepage": "https://github.com/littlebutt/nonebot-plugin-iot",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "t:物联网",
|
||||||
|
"color": "#4b86d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:天猫精灵",
|
||||||
|
"color": "#4b86d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:IOT",
|
||||||
|
"color": "#4b86d7"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_bwiki_navigator",
|
||||||
|
"project_link": "nonebot-plugin-bwiki-navigator",
|
||||||
|
"name": "BWIKI助手移植版",
|
||||||
|
"desc": "BWIKI助手机器人Nonebot2插件移植版",
|
||||||
|
"author": "XZhouQD",
|
||||||
|
"homepage": "https://github.com/XZhouQD/nonebot-plugin-bwiki-navigator",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "a:onebot",
|
||||||
|
"color": "#000000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:wiki",
|
||||||
|
"color": "#29a5e3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_bottle",
|
||||||
|
"project_link": "nonebot-plugin-bottle",
|
||||||
|
"name": "漂流瓶",
|
||||||
|
"desc": "群与群互通的漂流瓶插件",
|
||||||
|
"author": "Todysheep",
|
||||||
|
"homepage": "https://github.com/Todysheep/nonebot_plugin_bottle",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "漂流瓶",
|
||||||
|
"color": "#0893f2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_tts_gal",
|
||||||
|
"project_link": "nonebot-plugin-tts-gal",
|
||||||
|
"name": "gal角色语音生成",
|
||||||
|
"desc": "能够根据发送角色名和文本生成对应角色语音",
|
||||||
|
"author": "dpm12345",
|
||||||
|
"homepage": "https://github.com/dpm12345/nonebot_plugin_tts_gal",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "VITS",
|
||||||
|
"color": "#ea5252"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_alicdk_get",
|
||||||
|
"project_link": "nonebot-plugin-alicdk-get",
|
||||||
|
"name": "阿里云盘福利码自动兑换",
|
||||||
|
"desc": "基于nonebot2与aligo的阿里云盘兑换码自动获取和兑换插件。",
|
||||||
|
"author": "Kaguya233qwq",
|
||||||
|
"homepage": "https://github.com/Kaguya233qwq/nonebot_plugin_alicdk_get",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "兑换码",
|
||||||
|
"color": "#595fd6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "auto",
|
||||||
|
"color": "#595fd6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "t:阿里云盘",
|
||||||
|
"color": "#595fd6"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_picstatus",
|
||||||
|
"project_link": "nonebot-plugin-picstatus",
|
||||||
|
"name": "PicStatus",
|
||||||
|
"desc": "服务器运行状态图片版",
|
||||||
|
"author": "lgc2333",
|
||||||
|
"homepage": "https://github.com/lgc2333/nonebot-plugin-picstatus",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "t:server",
|
||||||
|
"color": "#8bff00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "a:OB V11",
|
||||||
|
"color": "#ff43c3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_tuling",
|
||||||
|
"project_link": "nonebot-plugin-tuling",
|
||||||
|
"name": "图灵机器人",
|
||||||
|
"desc": "接入图灵机器人做聊天",
|
||||||
|
"author": "koking0",
|
||||||
|
"homepage": "https://github.com/Matrix-King-Studio/nonebot_plugin_tuling",
|
||||||
|
"tags": [],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_makemidi",
|
||||||
|
"project_link": "nonebot_plugin_makemidi",
|
||||||
|
"name": "在线编曲",
|
||||||
|
"desc": "发送简谱生成音乐",
|
||||||
|
"author": "RandomEnch",
|
||||||
|
"homepage": "https://github.com/RandomEnch/nonebot_plugin_makemidi",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "midi",
|
||||||
|
"color": "#6515a8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "nonebot_plugin_ocr",
|
||||||
|
"project_link": "nonebot-plugin-ocr",
|
||||||
|
"name": "文字识别",
|
||||||
|
"desc": "识别图片中的文字",
|
||||||
|
"author": "NewYearPrism",
|
||||||
|
"homepage": "https://github.com/NewYearPrism/nonebot-plugin-ocr",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"label": "ocr ",
|
||||||
|
"color": "#ea5252"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_official": false
|
||||||
}
|
}
|
||||||
]
|
]
|
@@ -1,106 +0,0 @@
|
|||||||
---
|
|
||||||
sidebar_position: 0
|
|
||||||
description: nonebot.dependencies 模块
|
|
||||||
---
|
|
||||||
|
|
||||||
# nonebot.dependencies
|
|
||||||
|
|
||||||
本模块模块实现了依赖注入的定义与处理。
|
|
||||||
|
|
||||||
## _abstract class_ `Param(default=PydanticUndefined, **kwargs)` {#Param}
|
|
||||||
|
|
||||||
- **说明**
|
|
||||||
|
|
||||||
依赖注入的基本单元 —— 参数。
|
|
||||||
|
|
||||||
继承自 `pydantic.fields.FieldInfo`,用于描述参数信息(不包括参数名)。
|
|
||||||
|
|
||||||
- **参数**
|
|
||||||
|
|
||||||
- `default` (Any)
|
|
||||||
|
|
||||||
- `**kwargs` (Any)
|
|
||||||
|
|
||||||
## _class_ `Dependent(*, call, pre_checkers=None, params=None, parameterless=None, allow_types=None)` {#Dependent}
|
|
||||||
|
|
||||||
- **说明**
|
|
||||||
|
|
||||||
依赖注入容器
|
|
||||||
|
|
||||||
- **参数**
|
|
||||||
|
|
||||||
- `call` ((\*Any, \*\*Any) -> Any): 依赖注入的可调用对象,可以是任何 Callable 对象
|
|
||||||
|
|
||||||
- `pre_checkers` (list[[Param](#Param)] | None): 依赖注入解析前的参数检查
|
|
||||||
|
|
||||||
- `params` (list[pydantic.fields.ModelField] | None): 具名参数列表
|
|
||||||
|
|
||||||
- `parameterless` (list[[Param](#Param)] | None): 匿名参数列表
|
|
||||||
|
|
||||||
- `allow_types` (list[Type[[Param](#Param)]] | None): 允许的参数类型
|
|
||||||
|
|
||||||
### _method_ `append_parameterless(self, value)` {#Dependent-append_parameterless}
|
|
||||||
|
|
||||||
- **参数**
|
|
||||||
|
|
||||||
- `value` (Any)
|
|
||||||
|
|
||||||
- **返回**
|
|
||||||
|
|
||||||
- None
|
|
||||||
|
|
||||||
### _classmethod_ `parse(cls, *, call, parameterless=None, allow_types=None)` {#Dependent-parse}
|
|
||||||
|
|
||||||
- **参数**
|
|
||||||
|
|
||||||
- `call` ((\*Any, \*\*Any) -> Any)
|
|
||||||
|
|
||||||
- `parameterless` (list[Any] | None)
|
|
||||||
|
|
||||||
- `allow_types` (list[Type[[Param](#Param)]] | None)
|
|
||||||
|
|
||||||
- **返回**
|
|
||||||
|
|
||||||
- (~ T)
|
|
||||||
|
|
||||||
### _method_ `parse_param(self, name, param)` {#Dependent-parse_param}
|
|
||||||
|
|
||||||
- **参数**
|
|
||||||
|
|
||||||
- `name` (str)
|
|
||||||
|
|
||||||
- `param` (inspect.Parameter)
|
|
||||||
|
|
||||||
- **返回**
|
|
||||||
|
|
||||||
- [Param](#Param)
|
|
||||||
|
|
||||||
### _method_ `parse_parameterless(self, value)` {#Dependent-parse_parameterless}
|
|
||||||
|
|
||||||
- **参数**
|
|
||||||
|
|
||||||
- `value` (Any)
|
|
||||||
|
|
||||||
- **返回**
|
|
||||||
|
|
||||||
- [Param](#Param)
|
|
||||||
|
|
||||||
### _method_ `prepend_parameterless(self, value)` {#Dependent-prepend_parameterless}
|
|
||||||
|
|
||||||
- **参数**
|
|
||||||
|
|
||||||
- `value` (Any)
|
|
||||||
|
|
||||||
- **返回**
|
|
||||||
|
|
||||||
- None
|
|
||||||
|
|
||||||
### _async method_ `solve(self, **params)` {#Dependent-solve}
|
|
||||||
|
|
||||||
- **参数**
|
|
||||||
|
|
||||||
- `**params` (Any)
|
|
||||||
|
|
||||||
- **返回**
|
|
||||||
|
|
||||||
- dict[str, Any]
|
|
@@ -1,45 +0,0 @@
|
|||||||
---
|
|
||||||
sidebar_position: 4
|
|
||||||
description: nonebot.plugin.export 模块
|
|
||||||
---
|
|
||||||
|
|
||||||
# nonebot.plugin.export
|
|
||||||
|
|
||||||
本模块定义了插件导出的内容对象。
|
|
||||||
|
|
||||||
在新版插件系统中,推荐优先使用直接 import 所需要的插件内容。
|
|
||||||
|
|
||||||
## _class_ `Export()` {#Export}
|
|
||||||
|
|
||||||
- **说明**
|
|
||||||
|
|
||||||
插件导出内容以使得其他插件可以获得。
|
|
||||||
|
|
||||||
- **用法**
|
|
||||||
|
|
||||||
```python
|
|
||||||
nonebot.export().default = "bar"
|
|
||||||
|
|
||||||
@nonebot.export()
|
|
||||||
def some_function():
|
|
||||||
pass
|
|
||||||
|
|
||||||
# this doesn't work before python 3.9
|
|
||||||
# use
|
|
||||||
# export = nonebot.export(); @export.sub
|
|
||||||
# instead
|
|
||||||
# See also PEP-614: https://www.python.org/dev/peps/pep-0614/
|
|
||||||
@nonebot.export().sub
|
|
||||||
def something_else():
|
|
||||||
pass
|
|
||||||
```
|
|
||||||
|
|
||||||
## _def_ `export()` {#export}
|
|
||||||
|
|
||||||
- **说明**
|
|
||||||
|
|
||||||
获取当前插件的导出内容对象
|
|
||||||
|
|
||||||
- **返回**
|
|
||||||
|
|
||||||
- [Export](#Export)
|
|
@@ -8,7 +8,7 @@ slug: /
|
|||||||
|
|
||||||
NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架,它基于 Python 的类型注解和异步特性,能够为你的需求实现提供便捷灵活的支持。
|
NoneBot2 是一个现代、跨平台、可扩展的 Python 聊天机器人框架,它基于 Python 的类型注解和异步特性,能够为你的需求实现提供便捷灵活的支持。
|
||||||
|
|
||||||
需要注意的是,NoneBot2 仅支持 **Python 3.7.3 以上版本**
|
需要注意的是,NoneBot2 仅支持 **Python 3.8 以上版本**
|
||||||
|
|
||||||
## 特色
|
## 特色
|
||||||
|
|
Before Width: | Height: | Size: 376 KiB After Width: | Height: | Size: 376 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
@@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 0
|
||||||
|
description: nonebot.dependencies 模块
|
||||||
|
---
|
||||||
|
|
||||||
|
# nonebot.dependencies
|
||||||
|
|
||||||
|
本模块模块实现了依赖注入的定义与处理。
|
||||||
|
|
||||||
|
## _abstract class_ `Param(default=PydanticUndefined, **kwargs)` {#Param}
|
||||||
|
|
||||||
|
- **说明**
|
||||||
|
|
||||||
|
依赖注入的基本单元 —— 参数。
|
||||||
|
|
||||||
|
继承自 `pydantic.fields.FieldInfo`,用于描述参数信息(不包括参数名)。
|
||||||
|
|
||||||
|
- **参数**
|
||||||
|
|
||||||
|
- `default` (Any)
|
||||||
|
|
||||||
|
- `**kwargs` (Any)
|
||||||
|
|
||||||
|
## _class_ `Dependent(call, params=<factory>, parameterless=<factory>)` {#Dependent}
|
||||||
|
|
||||||
|
- **说明**
|
||||||
|
|
||||||
|
依赖注入容器
|
||||||
|
|
||||||
|
- **参数**
|
||||||
|
|
||||||
|
- `call` ((*Any, \*\*Any) -> (~ R) | (*Any, \*\*Any) -> Awaitable[(~ R)]): 依赖注入的可调用对象,可以是任何 Callable 对象
|
||||||
|
|
||||||
|
- `params` (tuple[pydantic.fields.ModelField]): 具名参数列表
|
||||||
|
|
||||||
|
- `parameterless` (tuple[[Param](#Param)]): 匿名参数列表
|
||||||
|
|
||||||
|
- `pre_checkers`: 依赖注入解析前的参数检查
|
||||||
|
|
||||||
|
- `allow_types`: 允许的参数类型
|
||||||
|
|
||||||
|
### _async method_ `check(self, **params)` {#Dependent-check}
|
||||||
|
|
||||||
|
- **参数**
|
||||||
|
|
||||||
|
- `**params` (Any)
|
||||||
|
|
||||||
|
- **返回**
|
||||||
|
|
||||||
|
- None
|
||||||
|
|
||||||
|
### _classmethod_ `parse(cls, *, call, parameterless=None, allow_types)` {#Dependent-parse}
|
||||||
|
|
||||||
|
- **参数**
|
||||||
|
|
||||||
|
- `call` ((*Any, \*\*Any) -> (~ R) | (*Any, \*\*Any) -> Awaitable[(~ R)])
|
||||||
|
|
||||||
|
- `parameterless` (Iterable[Any] | None)
|
||||||
|
|
||||||
|
- `allow_types` (Iterable[Type[[Param](#Param)]])
|
||||||
|
|
||||||
|
- **返回**
|
||||||
|
|
||||||
|
- Dependent[R]
|
||||||
|
|
||||||
|
### _staticmethod_ `parse_parameterless(parameterless, allow_types)` {#Dependent-parse_parameterless}
|
||||||
|
|
||||||
|
- **参数**
|
||||||
|
|
||||||
|
- `parameterless` (tuple[Any, ...])
|
||||||
|
|
||||||
|
- `allow_types` (tuple[Type[[Param](#Param)], ...])
|
||||||
|
|
||||||
|
- **返回**
|
||||||
|
|
||||||
|
- tuple[[Param](#Param), ...]
|
||||||
|
|
||||||
|
### _staticmethod_ `parse_params(call, allow_types)` {#Dependent-parse_params}
|
||||||
|
|
||||||
|
- **参数**
|
||||||
|
|
||||||
|
- `call` ((*Any, \*\*Any) -> (~ R) | (*Any, \*\*Any) -> Awaitable[(~ R)])
|
||||||
|
|
||||||
|
- `allow_types` (tuple[Type[[Param](#Param)], ...])
|
||||||
|
|
||||||
|
- **返回**
|
||||||
|
|
||||||
|
- tuple[pydantic.fields.ModelField]
|
||||||
|
|
||||||
|
### _async method_ `solve(self, **params)` {#Dependent-solve}
|
||||||
|
|
||||||
|
- **参数**
|
||||||
|
|
||||||
|
- `**params` (Any)
|
||||||
|
|
||||||
|
- **返回**
|
||||||
|
|
||||||
|
- dict[str, Any]
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user