diff --git a/.env b/.env index 061c92a5..a5d04480 100644 --- a/.env +++ b/.env @@ -8,12 +8,29 @@ SUPERUSERS=[] COMMAND_START=["/", "/"] COMMAND_SEP=[".","。"] +# 机器人实现配置 ----------------------------- + # OneBot 配置 # https://onebot.adapters.nonebot.dev/docs/guide/configuration ONEBOT_ACCESS_TOKEN ONEBOT_V12_ACCESS_TOKEN ONEBOT_V12_USE_MSGPACK=true +# Red +# https://github.com/nonebot/adapter-red +RED_BOTS=[] + +# QQ 频道 +# https://github.com/nonebot/adapter-qqguild +QQGUILD_BOTS=[] +QQGUILD_IS_SANDBOX=false + +# 开黑啦 +# https://github.com/Tian-que/nonebot-adapter-kaiheila/blob/master/MANUAL.md +KAIHEILA_BOTS=[] + +# 内部插件配置 ----------------------------- + # FF14 FFLOGS_RANGE=14 FFLOGS_CACHE_TIME=4:30:00 @@ -27,15 +44,7 @@ HEWEATHER_KEY # 复读 REPEAT_MIGRATION_GROUP_ID -# 机器人实现配置 ----------------------------- - -# Teydacore(telegram bot) -# https://github.com/teyda/teydacore -TELEGRAM_TOKEN=TELEGRAM_TOKEN -ONEBOT_WSR_URL=ws://coolqbot:8080/onebot/v12 -ONEBOT_WSR_ACCESS_TOKEN=ACCESS_TOKEN - -# 外部插件配置 +# 外部插件配置 ----------------------------- # Treehelp # https://github.com/he0119/nonebot-plugin-treehelp diff --git a/.vscode/launch.json b/.vscode/launch.json index 66dd0276..07758646 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -23,12 +23,9 @@ "justMyCode": false }, { - "name": "CoolQBot Test - Single", + "name": "Debug Unit Test", "type": "python", - "request": "launch", - "module": "pytest", - "args": ["-s", "-k", "${selectedText}"], - "console": "integratedTerminal", + "request": "test", "justMyCode": false } ] diff --git a/bot.py b/bot.py index 8133b94d..307475a5 100644 --- a/bot.py +++ b/bot.py @@ -1,6 +1,9 @@ import nonebot +from nonebot.adapters.kaiheila import Adapter as KaiheilaAdapter from nonebot.adapters.onebot.v11 import Adapter as OneBotV11Adapter from nonebot.adapters.onebot.v12 import Adapter as OneBotV12Adapter +from nonebot.adapters.qqguild import Adapter as QQGuildAdapter +from nonebot.adapters.red import Adapter as RedAdapter from nonebot.log import logger from sqlalchemy import StaticPool @@ -10,6 +13,9 @@ driver = nonebot.get_driver() driver.register_adapter(OneBotV11Adapter) driver.register_adapter(OneBotV12Adapter) +driver.register_adapter(KaiheilaAdapter) +driver.register_adapter(QQGuildAdapter) +driver.register_adapter(RedAdapter) # 加载插件 nonebot.load_from_toml("pyproject.toml") diff --git a/poetry.lock b/poetry.lock index 5b63632d..a435a39d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -95,17 +95,17 @@ zookeeper = ["kazoo"] [[package]] name = "arclet-alconna" -version = "1.7.20" +version = "1.7.21" description = "A High-performance, Generality, Humane Command Line Arguments Parser Library." optional = false python-versions = ">=3.8" files = [ - {file = "arclet_alconna-1.7.20-py3-none-any.whl", hash = "sha256:e43681200accce580787b48defc5a2503567efb63f427e4ae9f5d95aaa8b0509"}, - {file = "arclet_alconna-1.7.20.tar.gz", hash = "sha256:a6ce4f66dbb2f1dcb291eadcc4133277da6b9f2fda60a51b89b918f03216d625"}, + {file = "arclet_alconna-1.7.21-py3-none-any.whl", hash = "sha256:77cb9c7b1f83c92a007139c87bbd5cae50c61ad1785ed0696872ef11e65f6c5b"}, + {file = "arclet_alconna-1.7.21.tar.gz", hash = "sha256:240f2db51b5bfe51f95b4ce265731f1daf1a2097d1a63342ceb62a51ed60d05a"}, ] [package.dependencies] -nepattern = ">=0.5.13,<0.6.0" +nepattern = ">=0.5.14,<0.6.0" tarina = ">=0.3.3" typing-extensions = ">=4.5.0" @@ -114,18 +114,18 @@ full = ["arclet-alconna-tools (>=0.2.0)"] [[package]] name = "arclet-alconna-tools" -version = "0.6.6" +version = "0.6.7" description = "Builtin Tools for Alconna" optional = false python-versions = ">=3.8" files = [ - {file = "arclet-alconna-tools-0.6.6.tar.gz", hash = "sha256:fd9918f40c4ead8ce189f7151b6fe1ad1501e73dc51b291cb2a063c18dbe38db"}, - {file = "arclet_alconna_tools-0.6.6-py3-none-any.whl", hash = "sha256:c955553a1a1e171efa5cd2563757c1e9fed0b5af9600a5c1c4ad3d149d7abba6"}, + {file = "arclet-alconna-tools-0.6.7.tar.gz", hash = "sha256:3cc640956883d5c8452b241d85368970de4150037d696216ef8d117bde388dca"}, + {file = "arclet_alconna_tools-0.6.7-py3-none-any.whl", hash = "sha256:acffb10ccc77a32d282c3683c4589aa875711a8a56547136752db7a43d5ceb12"}, ] [package.dependencies] -arclet-alconna = ">=1.7.18,<2.0" -nepattern = ">=0.5.13,<0.6.0" +arclet-alconna = ">=1.7.21,<2.0" +nepattern = ">=0.5.14,<0.6.0" [[package]] name = "arrow" @@ -241,6 +241,17 @@ files = [ [package.dependencies] beautifulsoup4 = "*" +[[package]] +name = "cachetools" +version = "5.3.1" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, +] + [[package]] name = "cashews" version = "6.2.0" @@ -471,63 +482,63 @@ rich = "*" [[package]] name = "coverage" -version = "7.3.0" +version = "7.3.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db76a1bcb51f02b2007adacbed4c88b6dee75342c37b05d1822815eed19edee5"}, - {file = "coverage-7.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c02cfa6c36144ab334d556989406837336c1d05215a9bdf44c0bc1d1ac1cb637"}, - {file = "coverage-7.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:477c9430ad5d1b80b07f3c12f7120eef40bfbf849e9e7859e53b9c93b922d2af"}, - {file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce2ee86ca75f9f96072295c5ebb4ef2a43cecf2870b0ca5e7a1cbdd929cf67e1"}, - {file = "coverage-7.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68d8a0426b49c053013e631c0cdc09b952d857efa8f68121746b339912d27a12"}, - {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3eb0c93e2ea6445b2173da48cb548364f8f65bf68f3d090404080d338e3a689"}, - {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:90b6e2f0f66750c5a1178ffa9370dec6c508a8ca5265c42fbad3ccac210a7977"}, - {file = "coverage-7.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96d7d761aea65b291a98c84e1250cd57b5b51726821a6f2f8df65db89363be51"}, - {file = "coverage-7.3.0-cp310-cp310-win32.whl", hash = "sha256:63c5b8ecbc3b3d5eb3a9d873dec60afc0cd5ff9d9f1c75981d8c31cfe4df8527"}, - {file = "coverage-7.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:97c44f4ee13bce914272589b6b41165bbb650e48fdb7bd5493a38bde8de730a1"}, - {file = "coverage-7.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74c160285f2dfe0acf0f72d425f3e970b21b6de04157fc65adc9fd07ee44177f"}, - {file = "coverage-7.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b543302a3707245d454fc49b8ecd2c2d5982b50eb63f3535244fd79a4be0c99d"}, - {file = "coverage-7.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad0f87826c4ebd3ef484502e79b39614e9c03a5d1510cfb623f4a4a051edc6fd"}, - {file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13c6cbbd5f31211d8fdb477f0f7b03438591bdd077054076eec362cf2207b4a7"}, - {file = "coverage-7.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac440c43e9b479d1241fe9d768645e7ccec3fb65dc3a5f6e90675e75c3f3e3a"}, - {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3c9834d5e3df9d2aba0275c9f67989c590e05732439b3318fa37a725dff51e74"}, - {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4c8e31cf29b60859876474034a83f59a14381af50cbe8a9dbaadbf70adc4b214"}, - {file = "coverage-7.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7a9baf8e230f9621f8e1d00c580394a0aa328fdac0df2b3f8384387c44083c0f"}, - {file = "coverage-7.3.0-cp311-cp311-win32.whl", hash = "sha256:ccc51713b5581e12f93ccb9c5e39e8b5d4b16776d584c0f5e9e4e63381356482"}, - {file = "coverage-7.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:887665f00ea4e488501ba755a0e3c2cfd6278e846ada3185f42d391ef95e7e70"}, - {file = "coverage-7.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d000a739f9feed900381605a12a61f7aaced6beae832719ae0d15058a1e81c1b"}, - {file = "coverage-7.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59777652e245bb1e300e620ce2bef0d341945842e4eb888c23a7f1d9e143c446"}, - {file = "coverage-7.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9737bc49a9255d78da085fa04f628a310c2332b187cd49b958b0e494c125071"}, - {file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5247bab12f84a1d608213b96b8af0cbb30d090d705b6663ad794c2f2a5e5b9fe"}, - {file = "coverage-7.3.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ac9a1de294773b9fa77447ab7e529cf4fe3910f6a0832816e5f3d538cfea9a"}, - {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:85b7335c22455ec12444cec0d600533a238d6439d8d709d545158c1208483873"}, - {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:36ce5d43a072a036f287029a55b5c6a0e9bd73db58961a273b6dc11a2c6eb9c2"}, - {file = "coverage-7.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:211a4576e984f96d9fce61766ffaed0115d5dab1419e4f63d6992b480c2bd60b"}, - {file = "coverage-7.3.0-cp312-cp312-win32.whl", hash = "sha256:56afbf41fa4a7b27f6635bc4289050ac3ab7951b8a821bca46f5b024500e6321"}, - {file = "coverage-7.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f297e0c1ae55300ff688568b04ff26b01c13dfbf4c9d2b7d0cb688ac60df479"}, - {file = "coverage-7.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac0dec90e7de0087d3d95fa0533e1d2d722dcc008bc7b60e1143402a04c117c1"}, - {file = "coverage-7.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:438856d3f8f1e27f8e79b5410ae56650732a0dcfa94e756df88c7e2d24851fcd"}, - {file = "coverage-7.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1084393c6bda8875c05e04fce5cfe1301a425f758eb012f010eab586f1f3905e"}, - {file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49ab200acf891e3dde19e5aa4b0f35d12d8b4bd805dc0be8792270c71bd56c54"}, - {file = "coverage-7.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a67e6bbe756ed458646e1ef2b0778591ed4d1fcd4b146fc3ba2feb1a7afd4254"}, - {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f39c49faf5344af36042b293ce05c0d9004270d811c7080610b3e713251c9b0"}, - {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7df91fb24c2edaabec4e0eee512ff3bc6ec20eb8dccac2e77001c1fe516c0c84"}, - {file = "coverage-7.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:34f9f0763d5fa3035a315b69b428fe9c34d4fc2f615262d6be3d3bf3882fb985"}, - {file = "coverage-7.3.0-cp38-cp38-win32.whl", hash = "sha256:bac329371d4c0d456e8d5f38a9b0816b446581b5f278474e416ea0c68c47dcd9"}, - {file = "coverage-7.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b859128a093f135b556b4765658d5d2e758e1fae3e7cc2f8c10f26fe7005e543"}, - {file = "coverage-7.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed8d310afe013db1eedd37176d0839dc66c96bcfcce8f6607a73ffea2d6ba"}, - {file = "coverage-7.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61260ec93f99f2c2d93d264b564ba912bec502f679793c56f678ba5251f0393"}, - {file = "coverage-7.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97af9554a799bd7c58c0179cc8dbf14aa7ab50e1fd5fa73f90b9b7215874ba28"}, - {file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3558e5b574d62f9c46b76120a5c7c16c4612dc2644c3d48a9f4064a705eaee95"}, - {file = "coverage-7.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37d5576d35fcb765fca05654f66aa71e2808d4237d026e64ac8b397ffa66a56a"}, - {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07ea61bcb179f8f05ffd804d2732b09d23a1238642bf7e51dad62082b5019b34"}, - {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:80501d1b2270d7e8daf1b64b895745c3e234289e00d5f0e30923e706f110334e"}, - {file = "coverage-7.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4eddd3153d02204f22aef0825409091a91bf2a20bce06fe0f638f5c19a85de54"}, - {file = "coverage-7.3.0-cp39-cp39-win32.whl", hash = "sha256:2d22172f938455c156e9af2612650f26cceea47dc86ca048fa4e0b2d21646ad3"}, - {file = "coverage-7.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:60f64e2007c9144375dd0f480a54d6070f00bb1a28f65c408370544091c9bc9e"}, - {file = "coverage-7.3.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:5492a6ce3bdb15c6ad66cb68a0244854d9917478877a25671d70378bdc8562d0"}, - {file = "coverage-7.3.0.tar.gz", hash = "sha256:49dbb19cdcafc130f597d9e04a29d0a032ceedf729e41b181f51cd170e6ee865"}, + {file = "coverage-7.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3"}, + {file = "coverage-7.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276"}, + {file = "coverage-7.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e"}, + {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f"}, + {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392"}, + {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887"}, + {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d"}, + {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136"}, + {file = "coverage-7.3.1-cp310-cp310-win32.whl", hash = "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f"}, + {file = "coverage-7.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520"}, + {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, + {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, + {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, + {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, + {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, + {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, + {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, + {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, + {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, + {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, + {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, + {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, + {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, + {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, + {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, + {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, + {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, + {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, + {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, + {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, + {file = "coverage-7.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f"}, + {file = "coverage-7.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981"}, + {file = "coverage-7.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465"}, + {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344"}, + {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7"}, + {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40"}, + {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086"}, + {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff"}, + {file = "coverage-7.3.1-cp38-cp38-win32.whl", hash = "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3"}, + {file = "coverage-7.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e"}, + {file = "coverage-7.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1"}, + {file = "coverage-7.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6"}, + {file = "coverage-7.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4"}, + {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745"}, + {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7"}, + {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0"}, + {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0"}, + {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8"}, + {file = "coverage-7.3.1-cp39-cp39-win32.whl", hash = "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140"}, + {file = "coverage-7.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981"}, + {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, + {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, ] [package.extras] @@ -751,6 +762,20 @@ ufo = ["fs (>=2.2.0,<3)"] unicode = ["unicodedata2 (>=15.0.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] +[[package]] +name = "freezegun" +version = "1.2.2" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.6" +files = [ + {file = "freezegun-1.2.2-py3-none-any.whl", hash = "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f"}, + {file = "freezegun-1.2.2.tar.gz", hash = "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + [[package]] name = "greenlet" version = "2.0.2" @@ -867,13 +892,13 @@ files = [ [[package]] name = "httpcore" -version = "0.17.3" +version = "0.18.0" description = "A minimal low-level HTTP client." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "httpcore-0.17.3-py3-none-any.whl", hash = "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87"}, - {file = "httpcore-0.17.3.tar.gz", hash = "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888"}, + {file = "httpcore-0.18.0-py3-none-any.whl", hash = "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"}, + {file = "httpcore-0.18.0.tar.gz", hash = "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9"}, ] [package.dependencies] @@ -935,19 +960,19 @@ test = ["Cython (>=0.29.24,<0.30.0)"] [[package]] name = "httpx" -version = "0.24.1" +version = "0.25.0" description = "The next generation HTTP client." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"}, - {file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"}, + {file = "httpx-0.25.0-py3-none-any.whl", hash = "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100"}, + {file = "httpx-0.25.0.tar.gz", hash = "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"}, ] [package.dependencies] certifi = "*" h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""} -httpcore = ">=0.15.0,<0.18.0" +httpcore = ">=0.18.0,<0.19.0" idna = "*" sniffio = "*" @@ -1146,13 +1171,13 @@ files = [ [[package]] name = "loguru" -version = "0.7.1" +version = "0.7.2" description = "Python logging made (stupidly) simple" optional = false python-versions = ">=3.5" files = [ - {file = "loguru-0.7.1-py3-none-any.whl", hash = "sha256:046bf970cb3cad77a28d607cbf042ac25a407db987a1e801c7f7e692469982f9"}, - {file = "loguru-0.7.1.tar.gz", hash = "sha256:7ba2a7d81b79a412b0ded69bd921e012335e80fd39937a633570f273a343579e"}, + {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, + {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, ] [package.dependencies] @@ -1160,7 +1185,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] -dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "pre-commit (==3.3.1)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] +dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] [[package]] name = "lxml" @@ -1388,52 +1413,58 @@ files = [ [[package]] name = "matplotlib" -version = "3.7.2" +version = "3.7.3" description = "Python plotting package" optional = false python-versions = ">=3.8" files = [ - {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:2699f7e73a76d4c110f4f25be9d2496d6ab4f17345307738557d345f099e07de"}, - {file = "matplotlib-3.7.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a8035ba590658bae7562786c9cc6ea1a84aa49d3afab157e414c9e2ea74f496d"}, - {file = "matplotlib-3.7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f8e4a49493add46ad4a8c92f63e19d548b2b6ebbed75c6b4c7f46f57d36cdd1"}, - {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71667eb2ccca4c3537d9414b1bc00554cb7f91527c17ee4ec38027201f8f1603"}, - {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:152ee0b569a37630d8628534c628456b28686e085d51394da6b71ef84c4da201"}, - {file = "matplotlib-3.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070f8dddd1f5939e60aacb8fa08f19551f4b0140fab16a3669d5cd6e9cb28fc8"}, - {file = "matplotlib-3.7.2-cp310-cp310-win32.whl", hash = "sha256:fdbb46fad4fb47443b5b8ac76904b2e7a66556844f33370861b4788db0f8816a"}, - {file = "matplotlib-3.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:23fb1750934e5f0128f9423db27c474aa32534cec21f7b2153262b066a581fd1"}, - {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:30e1409b857aa8a747c5d4f85f63a79e479835f8dffc52992ac1f3f25837b544"}, - {file = "matplotlib-3.7.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:50e0a55ec74bf2d7a0ebf50ac580a209582c2dd0f7ab51bc270f1b4a0027454e"}, - {file = "matplotlib-3.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ac60daa1dc83e8821eed155796b0f7888b6b916cf61d620a4ddd8200ac70cd64"}, - {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305e3da477dc8607336ba10bac96986d6308d614706cae2efe7d3ffa60465b24"}, - {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c308b255efb9b06b23874236ec0f10f026673ad6515f602027cc8ac7805352d"}, - {file = "matplotlib-3.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60c521e21031632aa0d87ca5ba0c1c05f3daacadb34c093585a0be6780f698e4"}, - {file = "matplotlib-3.7.2-cp311-cp311-win32.whl", hash = "sha256:26bede320d77e469fdf1bde212de0ec889169b04f7f1179b8930d66f82b30cbc"}, - {file = "matplotlib-3.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:af4860132c8c05261a5f5f8467f1b269bf1c7c23902d75f2be57c4a7f2394b3e"}, - {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:a1733b8e84e7e40a9853e505fe68cc54339f97273bdfe6f3ed980095f769ddc7"}, - {file = "matplotlib-3.7.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d9881356dc48e58910c53af82b57183879129fa30492be69058c5b0d9fddf391"}, - {file = "matplotlib-3.7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f081c03f413f59390a80b3e351cc2b2ea0205839714dbc364519bcf51f4b56ca"}, - {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1cd120fca3407a225168238b790bd5c528f0fafde6172b140a2f3ab7a4ea63e9"}, - {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2c1590b90aa7bd741b54c62b78de05d4186271e34e2377e0289d943b3522273"}, - {file = "matplotlib-3.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d2ff3c984b8a569bc1383cd468fc06b70d7b59d5c2854ca39f1436ae8394117"}, - {file = "matplotlib-3.7.2-cp38-cp38-win32.whl", hash = "sha256:5dea00b62d28654b71ca92463656d80646675628d0828e08a5f3b57e12869e13"}, - {file = "matplotlib-3.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:0f506a1776ee94f9e131af1ac6efa6e5bc7cb606a3e389b0ccb6e657f60bb676"}, - {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:6515e878f91894c2e4340d81f0911857998ccaf04dbc1bba781e3d89cbf70608"}, - {file = "matplotlib-3.7.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:71f7a8c6b124e904db550f5b9fe483d28b896d4135e45c4ea381ad3b8a0e3256"}, - {file = "matplotlib-3.7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12f01b92ecd518e0697da4d97d163b2b3aa55eb3eb4e2c98235b3396d7dad55f"}, - {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7e28d6396563955f7af437894a36bf2b279462239a41028323e04b85179058b"}, - {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbcf59334ff645e6a67cd5f78b4b2cdb76384cdf587fa0d2dc85f634a72e1a3e"}, - {file = "matplotlib-3.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:318c89edde72ff95d8df67d82aca03861240512994a597a435a1011ba18dbc7f"}, - {file = "matplotlib-3.7.2-cp39-cp39-win32.whl", hash = "sha256:ce55289d5659b5b12b3db4dc9b7075b70cef5631e56530f14b2945e8836f2d20"}, - {file = "matplotlib-3.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:2ecb5be2b2815431c81dc115667e33da0f5a1bcf6143980d180d09a717c4a12e"}, - {file = "matplotlib-3.7.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fdcd28360dbb6203fb5219b1a5658df226ac9bebc2542a9e8f457de959d713d0"}, - {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c3cca3e842b11b55b52c6fb8bd6a4088693829acbfcdb3e815fa9b7d5c92c1b"}, - {file = "matplotlib-3.7.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebf577c7a6744e9e1bd3fee45fc74a02710b214f94e2bde344912d85e0c9af7c"}, - {file = "matplotlib-3.7.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:936bba394682049919dda062d33435b3be211dc3dcaa011e09634f060ec878b2"}, - {file = "matplotlib-3.7.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bc221ffbc2150458b1cd71cdd9ddd5bb37962b036e41b8be258280b5b01da1dd"}, - {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35d74ebdb3f71f112b36c2629cf32323adfbf42679e2751252acd468f5001c07"}, - {file = "matplotlib-3.7.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:717157e61b3a71d3d26ad4e1770dc85156c9af435659a25ee6407dc866cb258d"}, - {file = "matplotlib-3.7.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:20f844d6be031948148ba49605c8b96dfe7d3711d1b63592830d650622458c11"}, - {file = "matplotlib-3.7.2.tar.gz", hash = "sha256:a8cdb91dddb04436bd2f098b8fdf4b81352e68cf4d2c6756fcc414791076569b"}, + {file = "matplotlib-3.7.3-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:085c33b27561d9c04386789d5aa5eb4a932ddef43cfcdd0e01735f9a6e85ce0c"}, + {file = "matplotlib-3.7.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c568e80e1c17f68a727f30f591926751b97b98314d8e59804f54f86ae6fa6a22"}, + {file = "matplotlib-3.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7baf98c5ad59c5c4743ea884bb025cbffa52dacdfdac0da3e6021a285a90377e"}, + {file = "matplotlib-3.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:236024f582e40dac39bca592258888b38ae47a9fed7b8de652d68d3d02d47d2b"}, + {file = "matplotlib-3.7.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12b4f6795efea037ce2d41e7c417ad8bd02d5719c6ad4a8450a0708f4a1cfb89"}, + {file = "matplotlib-3.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b2136cc6c5415b78977e0e8c608647d597204b05b1d9089ccf513c7d913733"}, + {file = "matplotlib-3.7.3-cp310-cp310-win32.whl", hash = "sha256:122dcbf9be0086e2a95d9e5e0632dbf3bd5b65eaa68c369363310a6c87753059"}, + {file = "matplotlib-3.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:4aab27d9e33293389e3c1d7c881d414a72bdfda0fedc3a6bf46c6fa88d9b8015"}, + {file = "matplotlib-3.7.3-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:d5adc743de91e8e0b13df60deb1b1c285b8effea3d66223afceb14b63c9b05de"}, + {file = "matplotlib-3.7.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:55de4cf7cd0071b8ebf203981b53ab64f988a0a1f897a2dff300a1124e8bcd8b"}, + {file = "matplotlib-3.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ac03377fd908aaee2312d0b11735753e907adb6f4d1d102de5e2425249693f6c"}, + {file = "matplotlib-3.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:755bafc10a46918ce9a39980009b54b02dd249594e5adf52f9c56acfddb5d0b7"}, + {file = "matplotlib-3.7.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a6094c6f8e8d18db631754df4fe9a34dec3caf074f6869a7db09f18f9b1d6b2"}, + {file = "matplotlib-3.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:272dba2f1b107790ed78ebf5385b8d14b27ad9e90419de340364b49fe549a993"}, + {file = "matplotlib-3.7.3-cp311-cp311-win32.whl", hash = "sha256:591c123bed1cb4b9996fb60b41a6d89c2ec4943244540776c5f1283fb6960a53"}, + {file = "matplotlib-3.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:3bf3a178c6504694cee8b88b353df0051583f2f6f8faa146f67115c27c856881"}, + {file = "matplotlib-3.7.3-cp312-cp312-macosx_10_12_universal2.whl", hash = "sha256:edf54cac8ee3603f3093616b40a931e8c063969756a4d78a86e82c2fea9659f7"}, + {file = "matplotlib-3.7.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:91e36a85ea639a1ba9f91427041eac064b04829945fe331a92617b6cb21d27e5"}, + {file = "matplotlib-3.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:caf5eaaf7c68f8d7df269dfbcaf46f48a70ff482bfcebdcc97519671023f2a7d"}, + {file = "matplotlib-3.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74bf57f505efea376097e948b7cdd87191a7ce8180616390aef496639edf601f"}, + {file = "matplotlib-3.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee152a88a0da527840a426535514b6ed8ac4240eb856b1da92cf48124320e346"}, + {file = "matplotlib-3.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:67a410a9c9e07cbc83581eeea144bbe298870bf0ac0ee2f2e10a015ab7efee19"}, + {file = "matplotlib-3.7.3-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:259999c05285cb993d7f2a419cea547863fa215379eda81f7254c9e932963729"}, + {file = "matplotlib-3.7.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3f4e7fd5a6157e1d018ce2166ec8e531a481dd4a36f035b5c23edfe05a25419a"}, + {file = "matplotlib-3.7.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:faa3d12d8811d08d14080a8b7b9caea9a457dc495350166b56df0db4b9909ef5"}, + {file = "matplotlib-3.7.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:336e88900c11441e458da01c8414fc57e04e17f9d3bb94958a76faa2652bcf6b"}, + {file = "matplotlib-3.7.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:12f4c0dd8aa280d796c8772ea8265a14f11a04319baa3a16daa5556065e8baea"}, + {file = "matplotlib-3.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1990955b11e7918d256cf3b956b10997f405b7917a3f1c7d8e69c1d15c7b1930"}, + {file = "matplotlib-3.7.3-cp38-cp38-win32.whl", hash = "sha256:e78707b751260b42b721507ad7aa60fe4026d7f51c74cca6b9cd8b123ebb633a"}, + {file = "matplotlib-3.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:e594ee43c59ea39ca5c6244667cac9d017a3527febc31f5532ad9135cf7469ec"}, + {file = "matplotlib-3.7.3-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:6eaa1cf0e94c936a26b78f6d756c5fbc12e0a58c8a68b7248a2a31456ce4e234"}, + {file = "matplotlib-3.7.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0a97af9d22e8ebedc9f00b043d9bbd29a375e9e10b656982012dded44c10fd77"}, + {file = "matplotlib-3.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f9c6c16597af660433ab330b59ee2934b832ee1fabcaf5cbde7b2add840f31e"}, + {file = "matplotlib-3.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7240259b4b9cbc62381f6378cff4d57af539162a18e832c1e48042fabc40b6b"}, + {file = "matplotlib-3.7.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:747c6191d2e88ae854809e69aa358dbf852ff1a5738401b85c1cc9012309897a"}, + {file = "matplotlib-3.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec726b08a5275d827aa91bb951e68234a4423adb91cf65bc0fcdc0f2777663f7"}, + {file = "matplotlib-3.7.3-cp39-cp39-win32.whl", hash = "sha256:40e3b9b450c6534f07278310c4e34caff41c2a42377e4b9d47b0f8d3ac1083a2"}, + {file = "matplotlib-3.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfc118642903a23e309b1da32886bb39a4314147d013e820c86b5fb4cb2e36d0"}, + {file = "matplotlib-3.7.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:165c8082bf8fc0360c24aa4724a22eaadbfd8c28bf1ccf7e94d685cad48261e4"}, + {file = "matplotlib-3.7.3-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebd8470cc2a3594746ff0513aecbfa2c55ff6f58e6cef2efb1a54eb87c88ffa2"}, + {file = "matplotlib-3.7.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7153453669c9672b52095119fd21dd032d19225d48413a2871519b17db4b0fde"}, + {file = "matplotlib-3.7.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:498a08267dc69dd8f24c4b5d7423fa584d7ce0027ba71f7881df05fc09b89bb7"}, + {file = "matplotlib-3.7.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48999c4b19b5a0c058c9cd828ff6fc7748390679f6cf9a2ad653a3e802c87d3"}, + {file = "matplotlib-3.7.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22d65d18b4ee8070a5fea5761d59293f1f9e2fac37ec9ce090463b0e629432fd"}, + {file = "matplotlib-3.7.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c40cde976c36693cc0767e27cf5f443f91c23520060bd9496678364adfafe9c"}, + {file = "matplotlib-3.7.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:39018a2b17592448fbfdf4b8352955e6c3905359939791d4ff429296494d1a0c"}, + {file = "matplotlib-3.7.3.tar.gz", hash = "sha256:f09b3dd6bdeb588de91f853bbb2d6f0ff8ab693485b0c49035eaa510cb4f142e"}, ] [package.dependencies] @@ -1441,11 +1472,12 @@ contourpy = ">=1.0.1" cycler = ">=0.10" fonttools = ">=4.22.0" kiwisolver = ">=1.0.1" -numpy = ">=1.20" +numpy = ">=1.20,<2" packaging = ">=20.0" pillow = ">=6.2.0" -pyparsing = ">=2.3.1,<3.1" +pyparsing = ">=2.3.1" python-dateutil = ">=2.7" +setuptools_scm = ">=7" [[package]] name = "mdurl" @@ -1639,13 +1671,13 @@ files = [ [[package]] name = "nb-cli" -version = "1.2.3" +version = "1.2.4" description = "CLI for nonebot2" optional = false python-versions = ">=3.8, <4.0" files = [ - {file = "nb-cli-1.2.3.tar.gz", hash = "sha256:b3fc95873852c258fbaaa1979af95a7f83369c67c85d314cb015ee7b1275c4f9"}, - {file = "nb_cli-1.2.3-py3-none-any.whl", hash = "sha256:db0019bbffcf1004ccc0eae1f1f85453d55f22cb2fbbf21cfb02b9248007fdae"}, + {file = "nb-cli-1.2.4.tar.gz", hash = "sha256:5af5dd5967716ccf939a2927f8067f4b320692c399e059f8952c7b22240690c9"}, + {file = "nb_cli-1.2.4-py3-none-any.whl", hash = "sha256:714b0a0c731b2f5e9ddedd41d5f983c9f61def6956d137a2e1e040107ae61d3a"}, ] [package.dependencies] @@ -1666,19 +1698,33 @@ wcwidth = ">=0.2,<1.0" [[package]] name = "nepattern" -version = "0.5.13" +version = "0.5.14" description = "a complex pattern, support typing" optional = false python-versions = ">=3.8" files = [ - {file = "nepattern-0.5.13-py3-none-any.whl", hash = "sha256:cbd536df2119f1552e416baa11f4bc50627fb97308c84f8a31405eda2c5ba97e"}, - {file = "nepattern-0.5.13.tar.gz", hash = "sha256:1d0c6d7f7f122037c2342c7160aa3a4350cfa7c4726da39216cb823a07261720"}, + {file = "nepattern-0.5.14-py3-none-any.whl", hash = "sha256:2dd1bdb9b722da8099d52794a1d1c7fa7ace220badac10b91ec8b6ff053a7c51"}, + {file = "nepattern-0.5.14.tar.gz", hash = "sha256:d9cda6e6033645a57bec70e3e5f8998cbca278a78e38b55f96d9c2f419fdcc24"}, ] [package.dependencies] tarina = ">=0.3.3" typing-extensions = ">=4.5.0" +[[package]] +name = "nonebot-adapter-kaiheila" +version = "0.2.12" +description = "kaiheila adapter for nonebot2" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "nonebot_adapter_kaiheila-0.2.12-py3-none-any.whl", hash = "sha256:3ddc7e47b7213807d652428b95663e03991d1d89e79f434fbcf8b73a95a7a5ee"}, + {file = "nonebot_adapter_kaiheila-0.2.12.tar.gz", hash = "sha256:102e00642e693f859b3263f53dc41675e4c8ec6c123e253fdddecf3b3f2ec61f"}, +] + +[package.dependencies] +nonebot2 = ">=2.0.0,<3.0.0" + [[package]] name = "nonebot-adapter-onebot" version = "2.2.4" @@ -1695,6 +1741,35 @@ msgpack = ">=1.0.3,<2.0.0" nonebot2 = ">=2.0.0-beta.3,<3.0.0" typing-extensions = ">=4.0.0,<5.0.0" +[[package]] +name = "nonebot-adapter-qqguild" +version = "0.2.5" +description = "QQ Guild adapter for nonebot2" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "nonebot_adapter_qqguild-0.2.5-py3-none-any.whl", hash = "sha256:9fa3e1a86d5ba6bb49bd8f6b1766e0d70204a97026316c4f6426bc09abb8e9fb"}, + {file = "nonebot_adapter_qqguild-0.2.5.tar.gz", hash = "sha256:b6fef5daf54a4ce41221945a6fb3c5c63763a9950377f1e3e50191602b8b8098"}, +] + +[package.dependencies] +nonebot2 = ">=2.0.0-beta.1,<3.0.0" +pydantic = ">=1.9.0,<2.0.0" + +[[package]] +name = "nonebot-adapter-red" +version = "0.1.0" +description = "Red Protocol Adapter for Nonebot2" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nonebot_adapter_red-0.1.0-py3-none-any.whl", hash = "sha256:f98b931515ab2a839010d270828785ad525cb5c1dd36f05e4b25cf0ca01294d1"}, + {file = "nonebot_adapter_red-0.1.0.tar.gz", hash = "sha256:56b1fcd671e300124664acc17bce156b6f37fb4a28e811bc6d79576e4c1a79e1"}, +] + +[package.dependencies] +nonebot2 = ">=2.0.1" + [[package]] name = "nonebot-bison" version = "0.8.2" @@ -1724,19 +1799,19 @@ tinydb = ">=4.3.0,<5.0.0" [[package]] name = "nonebot-plugin-alconna" -version = "0.20.0" +version = "0.22.6" description = "Alconna Adapter for Nonebot" optional = false python-versions = ">=3.8" files = [ - {file = "nonebot_plugin_alconna-0.20.0-py3-none-any.whl", hash = "sha256:7b715470b90d9d9963f980c2ba32ef5f4731cb34fcf6513c0535c223a42d547a"}, - {file = "nonebot_plugin_alconna-0.20.0.tar.gz", hash = "sha256:55f6cfd2b5485124eacf0e9dc150c37dd7c45eeb515bb6dc25c08d66377f76ca"}, + {file = "nonebot_plugin_alconna-0.22.6-py3-none-any.whl", hash = "sha256:04869c44dbb9c75a903e55248d8aacf2499011fdbf8bf22a9ed67cc82fb59211"}, + {file = "nonebot_plugin_alconna-0.22.6.tar.gz", hash = "sha256:9e3d202bf6909f02784dd59ad6c6a94c4f3e882d83f751e0b0e5bebafd8f34c9"}, ] [package.dependencies] -arclet-alconna = ">=1.7.19,<2.0.0" -arclet-alconna-tools = ">=0.6.6,<0.7.0" -nepattern = ">=0.5.13,<0.6.0" +arclet-alconna = ">=1.7.21,<2.0.0" +arclet-alconna-tools = ">=0.6.7,<0.7.0" +nepattern = ">=0.5.14,<0.6.0" nonebot2 = ">=2.0.0rc4" [[package]] @@ -1991,6 +2066,24 @@ files = [ [package.dependencies] nonebot2 = ">=2.0.0,<3.0.0" +[[package]] +name = "nonebot-plugin-userinfo" +version = "0.1.0" +description = "Nonebot2 用户信息获取插件" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "nonebot_plugin_userinfo-0.1.0-py3-none-any.whl", hash = "sha256:a6f3791cbfbcc32005ea3b3344bdc04b91b9ddecbff68d5ae31c662c53508b24"}, + {file = "nonebot_plugin_userinfo-0.1.0.tar.gz", hash = "sha256:7222b54ff6a1949e8991a1a3697ed2995f5366f6f9e6526a9895bd31cc6b889f"}, +] + +[package.dependencies] +cachetools = ">=5.0.0,<6.0.0" +emoji = ">=2.0.0,<3.0.0" +httpx = ">=0.20.0,<1.0.0" +nonebot-plugin-session = ">=0.1.0,<0.2.0" +nonebot2 = {version = ">=2.0.0,<3.0.0", extras = ["fastapi"]} + [[package]] name = "nonebot-plugin-wordcloud" version = "0.5.2" @@ -2017,13 +2110,13 @@ wordcloud = ">=1.8.1,<2.0.0" [[package]] name = "nonebot2" -version = "2.0.1" +version = "2.1.0" description = "An asynchronous python bot framework." optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "nonebot2-2.0.1-py3-none-any.whl", hash = "sha256:58111068df7a6c13cca2a412dd0f6f88d7bf2a2af3e92ae770fd913a9421743e"}, - {file = "nonebot2-2.0.1.tar.gz", hash = "sha256:c61294644aef08f2b427301ca1c358d34e6cfaa7025d694a502ad66e9508e7c2"}, + {file = "nonebot2-2.1.0-py3-none-any.whl", hash = "sha256:e14bfbb962df72a9beac5035291594ad6e549c004b2ff5d28db97d0fdc3abf45"}, + {file = "nonebot2-2.1.0.tar.gz", hash = "sha256:f29cb773833ab5000557090edcbc5a6eabaf6e04a224c86761a4ddb6b1e0bd18"}, ] [package.dependencies] @@ -2032,8 +2125,9 @@ httpx = {version = ">=0.20.0,<1.0.0", extras = ["http2"], optional = true, marke loguru = ">=0.6.0,<1.0.0" pydantic = {version = ">=1.10.0,<2.0.0", extras = ["dotenv"]} pygtrie = ">=2.4.1,<3.0.0" -typing-extensions = ">=4.0.0,<5.0.0" +typing-extensions = ">=4.4.0,<5.0.0" uvicorn = {version = ">=0.20.0,<1.0.0", extras = ["standard"], optional = true, markers = "extra == \"quart\" or extra == \"fastapi\" or extra == \"all\""} +websockets = {version = ">=10.0", optional = true, markers = "extra == \"websockets\" or extra == \"all\""} yarl = ">=1.7.2,<2.0.0" [package.extras] @@ -2473,13 +2567,13 @@ extra = ["pygments (>=2.12)"] [[package]] name = "pyparsing" -version = "3.0.9" +version = "3.1.1" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" files = [ - {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.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, + {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, ] [package.extras] @@ -2498,13 +2592,13 @@ files = [ [[package]] name = "pytest" -version = "7.4.1" +version = "7.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.1-py3-none-any.whl", hash = "sha256:460c9a59b14e27c602eb5ece2e47bec99dc5fc5f6513cf924a7d03a578991b1f"}, - {file = "pytest-7.4.1.tar.gz", hash = "sha256:2f2301e797521b23e4d2585a0a3d7b5e50fdddaaf7e7d6773ea26ddb17c213ab"}, + {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, + {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, ] [package.dependencies] @@ -2619,15 +2713,18 @@ cli = ["click (>=5.0)"] [[package]] name = "python-engineio" -version = "4.7.0" +version = "4.7.1" description = "Engine.IO server and client for Python" optional = false python-versions = ">=3.6" files = [ - {file = "python-engineio-4.7.0.tar.gz", hash = "sha256:8b4f30ba58a5bff001801127227f44a1da0c0a22045f798c1a13ebb0d618e2a3"}, - {file = "python_engineio-4.7.0-py3-none-any.whl", hash = "sha256:23b05b768d61c281104d8c69e069cea356b2e60aa982f718cdf1731719ce2803"}, + {file = "python-engineio-4.7.1.tar.gz", hash = "sha256:a8422e345cd9a21451303380b160742ff02197975b1c3a02cef115febe2b1b20"}, + {file = "python_engineio-4.7.1-py3-none-any.whl", hash = "sha256:52499e8ab94fea1a6525ffe872fe7028d04b575799c5fa8e2cf7880e032de42e"}, ] +[package.dependencies] +simple-websocket = ">=0.10.0" + [package.extras] asyncio-client = ["aiohttp (>=3.4)"] client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"] @@ -3024,13 +3121,13 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "sentry-sdk" -version = "1.30.0" +version = "1.31.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.30.0.tar.gz", hash = "sha256:7dc873b87e1faf4d00614afd1058bfa1522942f33daef8a59f90de8ed75cd10c"}, - {file = "sentry_sdk-1.30.0-py2.py3-none-any.whl", hash = "sha256:2e53ad63f96bb9da6570ba2e755c267e529edcf58580a2c0d2a11ef26e1e678b"}, + {file = "sentry-sdk-1.31.0.tar.gz", hash = "sha256:6de2e88304873484207fed836388e422aeff000609b104c802749fd89d56ba5b"}, + {file = "sentry_sdk-1.31.0-py2.py3-none-any.whl", hash = "sha256:64a7141005fb775b9db298a30de93e3b83e0ddd1232dc6f36eb38aebc1553291"}, ] [package.dependencies] @@ -3040,10 +3137,12 @@ urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} [package.extras] aiohttp = ["aiohttp (>=3.5)"] arq = ["arq (>=0.23)"] +asyncpg = ["asyncpg (>=0.23)"] beam = ["apache-beam (>=2.12)"] bottle = ["bottle (>=0.12.13)"] celery = ["celery (>=3)"] chalice = ["chalice (>=1.16.0)"] +clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] django = ["django (>=1.8)"] falcon = ["falcon (>=1.4)"] fastapi = ["fastapi (>=0.79.0)"] @@ -3065,6 +3164,42 @@ starlette = ["starlette (>=0.19.1)"] starlite = ["starlite (>=1.48)"] tornado = ["tornado (>=5)"] +[[package]] +name = "setuptools" +version = "68.2.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, + {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "setuptools-scm" +version = "7.1.0" +description = "the blessed package to manage your versions by scm tags" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools_scm-7.1.0-py3-none-any.whl", hash = "sha256:73988b6d848709e2af142aa48c986ea29592bbcfca5375678064708205253d8e"}, + {file = "setuptools_scm-7.1.0.tar.gz", hash = "sha256:6c508345a771aad7d56ebff0e70628bf2b0ec7573762be9960214730de278f27"}, +] + +[package.dependencies] +packaging = ">=20.0" +setuptools = "*" +typing-extensions = "*" + +[package.extras] +test = ["pytest (>=6.2)", "virtualenv (>20)"] +toml = ["setuptools (>=42)"] + [[package]] name = "sgmllib3k" version = "1.0.0" @@ -3075,6 +3210,20 @@ files = [ {file = "sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9"}, ] +[[package]] +name = "simple-websocket" +version = "0.10.1" +description = "Simple WebSocket server and client for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "simple-websocket-0.10.1.tar.gz", hash = "sha256:0ab46c8ffa51a46dc95eed94608b3b722841c0bf849def71d465c5c356679c82"}, + {file = "simple_websocket-0.10.1-py3-none-any.whl", hash = "sha256:62c36bacfd75cc867927bb39d91951342a7234bdfe20f41dd969a3b8bb1413b7"}, +] + +[package.dependencies] +wsproto = "*" + [[package]] name = "six" version = "1.16.0" @@ -3666,6 +3815,20 @@ matplotlib = "*" numpy = ">=1.6.1" pillow = "*" +[[package]] +name = "wsproto" +version = "1.2.0" +description = "WebSockets state-machine based protocol implementation" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, + {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, +] + +[package.dependencies] +h11 = ">=0.9.0,<1" + [[package]] name = "yarl" version = "1.9.2" @@ -3756,4 +3919,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "87214284061f8aa473808cebf6de58c6672a6572664e3d18d25a59dd73de7984" +content-hash = "f95d21d33fbc95de0d1e48c858451eb6f4dc5f56482ad80e9813ba4150ee2905" diff --git a/pyproject.toml b/pyproject.toml index 908d715a..9d6d3ac1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,17 +11,26 @@ version = "0.16.0" python = "^3.11" eorzeaenv = "^2.2.8" matplotlib = "^3.7.1" +expiringdict = "^1.2.2" -nonebot2 = { extras = ["httpx", "fastapi"], version = "^2.0.0" } +nonebot2 = { extras = ["httpx", "fastapi", "websockets"], version = "^2.0.0" } nb-cli = "^1.2.3" nonebot-adapter-onebot = "2.2.4" onebot-qqguild-extension = "^0.1.1" +nonebot-adapter-qqguild = "^0.2.5" +nonebot-adapter-kaiheila = "^0.2.12" +nonebot-adapter-red = "^0.1.0" nonebot-plugin-datastore = "^1.1.1" nonebot-plugin-wordcloud = "^0.5.2" nonebot-plugin-treehelp = "^0.3.0" nonebot-plugin-apscheduler = "^0.3.0" +nonebot-plugin-send-anything-anywhere = "^0.3.0" +nonebot-plugin-alconna = "^0.22.3" +nonebot-plugin-session = "^0.1.0" +nonebot-plugin-userinfo = "^0.1.0" + nonebot-plugin-sentry = "^0.4.0" nonebot-plugin-memes = "0.4.7" nonebot-bison = "0.8.2" @@ -36,15 +45,27 @@ pytest-mock = "^3.6.1" pytest-xdist = "^3.0.2" pytest-asyncio = "^0.21.0" respx = "^0.20.1" +freezegun = "^1.2.2" [tool.nonebot] +adapters = [ + { name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11" }, + { name = "OneBot V12", module_name = "nonebot.adapters.onebot.v12" }, + { name = "开黑啦", module_name = "nonebot.adapters.kaiheila" }, + { name = "QQ 频道", module_name = "nonebot.adapters.qqguild" }, + { name = "RedProtocol", module_name = "nonebot.adapters.red" }, +] plugin_dirs = ["src/plugins"] plugins = [ "onebot_qqguild_extension", "nonebot_plugin_apscheduler", + "nonebot_plugin_saa", + "nonebot_plugin_alconna", "nonebot_plugin_sentry", "nonebot_plugin_treehelp", "nonebot_plugin_datastore", + "nonebot_plugin_session", + "nonebot_plugin_userinfo", "nonebot_plugin_wordcloud", "nonebot_plugin_memes", "nonebot_bison", diff --git a/src/plugins/user/__init__.py b/src/plugins/user/__init__.py new file mode 100644 index 00000000..def1369d --- /dev/null +++ b/src/plugins/user/__init__.py @@ -0,0 +1,97 @@ +import random +from typing import cast + +from expiringdict import ExpiringDict +from nonebot_plugin_alconna import ( + Alconna, + AlconnaQuery, + Args, + Option, + Query, + on_alconna, +) +from nonebot_plugin_session import SessionLevel + +from .annotated import UserSession +from .utils import get_user, remove_bind, set_bind + +user_cmd = on_alconna(Alconna("user"), use_cmd_start=True) + + +@user_cmd.handle() +async def _(session: UserSession): + await user_cmd.finish( + "\n".join( + [ + f"用户 ID:{session.uid}", + f"用户名:{session.name}", + f"用户创建日期:{session.created_at}", + f"用户所在平台 ID:{session.pid}", + f"用户所在平台:{session.platform}", + ] + ) + ) + + +tokens = cast( + dict[str, tuple[str, str, int, SessionLevel | None]], + ExpiringDict(max_len=100, max_age_seconds=300), +) + + +bind_cmd = on_alconna( + Alconna("bind", Option("-r"), Args["token?", str]), use_cmd_start=True +) + + +@bind_cmd.handle() +async def _( + session: UserSession, + token: str | None = None, + remove: Query[bool] = AlconnaQuery("r.value", default=False), +): + if remove.result: + result = await remove_bind(session.pid, session.platform) + if result: + await bind_cmd.finish("解绑成功") + else: + await bind_cmd.finish("不能解绑最初绑定的账号") + + # 生成令牌 + if not token: + token = f"nonebot/{random.randint(100000, 999999)}" + tokens[token] = (session.pid, session.platform, session.uid, session.level) + await bind_cmd.finish( + f"命令 bind 可用于在多个平台间绑定用户数据。绑定过程中,原始平台的用户数据将完全保留,而目标平台的用户数据将被原始平台的数据所覆盖。\n请确认当前平台是你的目标平台,并在 5 分钟内使用你的账号在原始平台内向机器人发送以下文本:\n/bind {token}\n绑定完成后,你可以随时使用「bind -r」来解除绑定状态。" + ) + + # 绑定流程 + if token in tokens: + # 平台的相关信息 + pid, platform, user_id, level = tokens.pop(token) + # 群内绑定的第一步,会在原始平台发送令牌 + # 此时 pid 和 platform 为目标平台的信息 + if level == SessionLevel.LEVEL2 or level == SessionLevel.LEVEL3: + token = f"nonebot/{random.randint(100000, 999999)}" + tokens[token] = (session.pid, session.platform, user_id, None) + await bind_cmd.finish( + f"令牌核验成功!下面将进行第二步操作。\n请在 5 分钟内使用你的账号在目标平台内向机器人发送以下文本:\n/bind {token}\n注意:当前平台是你的原始平台,这里的用户数据将覆盖目标平台的数据。" + ) + # 群内绑定的第二步,会在目标平台发送令牌 + # 此时 pid 和 platform 为原始平台的信息 + # 需要重新获取其用户信息,然后将目标平台绑定至原始平台 + elif level is None: + if session.uid != user_id: + await bind_cmd.finish("请使用最开始要绑定账号进行操作") + + user = await get_user(pid, platform) + await set_bind(session.pid, session.platform, user.id) + await bind_cmd.finish("绑定成功") + # 私聊绑定时,会在原始平台发送令牌 + # 此时 pid 和 platform 为目标平台的信息 + # 直接将目标平台绑定至原始平台 + elif level == SessionLevel.LEVEL1: + await set_bind(pid, platform, session.uid) + await bind_cmd.finish("绑定成功") + else: + await bind_cmd.finish("令牌不存在或已过期") diff --git a/src/plugins/user/annotated.py b/src/plugins/user/annotated.py new file mode 100644 index 00000000..81e50e08 --- /dev/null +++ b/src/plugins/user/annotated.py @@ -0,0 +1,10 @@ +from typing import Annotated + +from nonebot.params import Depends + +from .depends import UserSession as _UserSession +from .depends import get_or_create_user, get_user_session +from .models import User as _User + +User = Annotated[_User, Depends(get_or_create_user)] +UserSession = Annotated[_UserSession, Depends(get_user_session)] diff --git a/src/plugins/user/depends.py b/src/plugins/user/depends.py new file mode 100644 index 00000000..362087fd --- /dev/null +++ b/src/plugins/user/depends.py @@ -0,0 +1,43 @@ +from nonebot.matcher import Matcher +from nonebot.params import Depends +from nonebot_plugin_session import Session, SessionLevel, extract_session +from nonebot_plugin_userinfo import EventUserInfo, UserInfo + +from . import utils +from .models import UserSession + + +async def get_or_create_user( + matcher: Matcher, + session: Session = Depends(extract_session), + user_info: UserInfo | None = EventUserInfo(), +): + """获取一个用户,如果不存在则创建""" + if ( + session.platform == "unknown" + or session.level == SessionLevel.LEVEL0 + or not session.id1 + ): + await matcher.finish("用户相关功能暂不支持当前平台") + raise ValueError("用户相关功能暂不支持当前平台") + + try: + user = await utils.get_user(session.id1, session.platform) + except ValueError: + user = await utils.create_user( + session.id1, + session.platform, + user_info and user_info.user_name or session.id1, + ) + + return user + + +async def get_user_session( + matcher: Matcher, + session: Session = Depends(extract_session), + user_info: UserInfo | None = EventUserInfo(), +): + """获取用户会话""" + user = await get_or_create_user(matcher, session, user_info) + return UserSession(session, user_info, user) diff --git a/src/plugins/user/migrations/5cb13347bece_init_db.py b/src/plugins/user/migrations/5cb13347bece_init_db.py new file mode 100644 index 00000000..15d38b37 --- /dev/null +++ b/src/plugins/user/migrations/5cb13347bece_init_db.py @@ -0,0 +1,50 @@ +"""init db + +Revision ID: 5cb13347bece +Revises: +Create Date: 2023-09-12 17:07:22.228191 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "5cb13347bece" +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "user_user", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("created_at", sa.DateTime(), nullable=False), + sa.PrimaryKeyConstraint("id"), + ) + op.create_table( + "user_bind", + sa.Column("pid", sa.String(length=64), nullable=False), + sa.Column("platform", sa.String(length=32), nullable=False), + sa.Column("aid", sa.Integer(), nullable=False), + sa.Column("bid", sa.Integer(), nullable=False), + sa.ForeignKeyConstraint( + ["aid"], + ["user_user.id"], + ), + sa.ForeignKeyConstraint( + ["bid"], + ["user_user.id"], + ), + sa.PrimaryKeyConstraint("pid", "platform"), + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("user_bind") + op.drop_table("user_user") + # ### end Alembic commands ### diff --git a/src/plugins/user/models.py b/src/plugins/user/models.py new file mode 100644 index 00000000..f8a712e8 --- /dev/null +++ b/src/plugins/user/models.py @@ -0,0 +1,83 @@ +from dataclasses import dataclass +from datetime import datetime + +from nonebot_plugin_datastore import get_plugin_data +from nonebot_plugin_session import Session, SessionLevel +from nonebot_plugin_userinfo import UserInfo +from sqlalchemy import DateTime, ForeignKey, String +from sqlalchemy.orm import Mapped, mapped_column, relationship + +plugin_data = get_plugin_data() +plugin_data.use_global_registry() + +Model = plugin_data.Model + + +class User(Model): + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(255)) + created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) + + binds: Mapped[list["Bind"]] = relationship(back_populates="auser") + """当前绑定的平台""" + bind: Mapped["Bind"] = relationship(back_populates="buser") + """初始时绑定的平台""" + + +class Bind(Model): + pid: Mapped[str] = mapped_column(String(64), primary_key=True) + """平台 ID""" + platform: Mapped[str] = mapped_column(String(32), primary_key=True) + """平台名称""" + aid: Mapped[int] = mapped_column(ForeignKey("user_user.id")) + """当前绑定的账号 ID""" + bid: Mapped[int] = mapped_column(ForeignKey("user_user.id")) + """初始时绑定的账号 ID""" + auser: Mapped[User] = relationship(back_populates="binds", foreign_keys=[aid]) + """当前绑定的账号""" + buser: Mapped[User] = relationship(back_populates="bind", foreign_keys=[bid]) + """初始时绑定的账号""" + + +User.binds = relationship( + Bind, uselist=True, back_populates="auser", foreign_keys=[Bind.aid] +) +User.bind = relationship(Bind, back_populates="buser", foreign_keys=[Bind.bid]) + + +@dataclass +class UserSession: + session: Session + info: UserInfo | None + user: User + + @property + def uid(self) -> int: + """用户 ID""" + return self.user.id + + @property + def name(self) -> str: + """用户名""" + return self.user.name + + @property + def created_at(self) -> datetime: + """用户创建日期""" + return self.user.created_at.astimezone() + + @property + def pid(self) -> str: + """用户所在平台 ID""" + assert self.session.id1 + return self.session.id1 + + @property + def platform(self) -> str: + """用户所在平台""" + return self.session.platform + + @property + def level(self) -> SessionLevel: + """用户会话级别""" + return self.session.level diff --git a/src/plugins/user/utils.py b/src/plugins/user/utils.py new file mode 100644 index 00000000..b5350d66 --- /dev/null +++ b/src/plugins/user/utils.py @@ -0,0 +1,73 @@ +from nonebot_plugin_datastore import create_session +from sqlalchemy import select +from sqlalchemy.orm import selectinload + +from .models import Bind, User + + +async def create_user(pid: str, platform: str, nickname: str): + """创建账号""" + async with create_session() as session: + user = User(name=nickname) + session.add(user) + bind = Bind( + pid=pid, + platform=platform, + auser=user, + buser=user, + ) + session.add(bind) + await session.commit() + await session.refresh(user) + return user + + +async def get_user(pid: str, platform: str): + """获取账号""" + async with create_session() as session: + bind = ( + await session.scalars( + select(Bind) + .where(Bind.pid == pid) + .where(Bind.platform == platform) + .options(selectinload(Bind.auser)) + ) + ).one_or_none() + + if not bind: + raise ValueError("找不到用户信息") + + return bind.auser + + +async def set_bind(pid: str, platform: str, aid: int): + """设置账号绑定""" + async with create_session() as session: + bind = ( + await session.scalars( + select(Bind).where(Bind.pid == pid).where(Bind.platform == platform) + ) + ).one_or_none() + + if not bind: + raise ValueError("找不到用户信息") + + bind.aid = aid + await session.commit() + + +async def remove_bind(pid: str, platform: str): + """解除账号绑定""" + async with create_session() as db_session: + bind = ( + await db_session.scalars( + select(Bind).where(Bind.pid == pid).where(Bind.platform == platform) + ) + ).one() + + if bind.aid == bind.bid: + return False + else: + bind.aid = bind.bid + await db_session.commit() + return True diff --git a/tests/conftest.py b/tests/conftest.py index 38d9f4b9..f97ee15f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,4 @@ from pathlib import Path -from typing import TYPE_CHECKING import nonebot import pytest @@ -7,9 +6,6 @@ from nonebug import NONEBOT_INIT_KWARGS from nonebug.app import App -if TYPE_CHECKING: - from nonebot.plugin import Plugin - def pytest_configure(config: pytest.Config) -> None: config.stash[NONEBOT_INIT_KWARGS] = { @@ -18,8 +14,20 @@ def pytest_configure(config: pytest.Config) -> None: @pytest.fixture(scope="session", autouse=True) -def load_plugin(nonebug_init: None) -> set["Plugin"]: - return nonebot.load_plugins(str(Path(__file__).parent.parent / "src" / "plugins")) +def load_plugin(nonebug_init: None): + from nonebot.adapters.onebot.v11 import Adapter + + driver = nonebot.get_driver() + driver.register_adapter(Adapter) + + nonebot.load_plugin("nonebot_plugin_datastore") + nonebot.load_plugin("nonebot_plugin_apscheduler") + nonebot.load_plugin("nonebot_plugin_saa") + nonebot.load_plugin("nonebot_plugin_alconna") + nonebot.load_plugin("nonebot_plugin_session") + nonebot.load_plugin("nonebot_plugin_userinfo") + + nonebot.load_plugins(str(Path(__file__).parent.parent / "src" / "plugins")) @pytest.fixture diff --git a/tests/plugins/user/conftest.py b/tests/plugins/user/conftest.py new file mode 100644 index 00000000..abed3d03 --- /dev/null +++ b/tests/plugins/user/conftest.py @@ -0,0 +1,56 @@ +import datetime +from contextlib import contextmanager + +import pytest +from freezegun import freeze_time +from nonebug import App +from sqlalchemy import delete, event + + +@pytest.fixture +async def app(app: App): + yield app + + # 清理数据库 + from nonebot_plugin_datastore.db import create_session + + from src.plugins.user.models import Bind, User + + async with create_session() as session, session.begin(): + await session.execute(delete(Bind)) + await session.execute(delete(User)) + + # UserInfo 有自己的缓存,所以要清理 + from nonebot_plugin_userinfo.getter import _user_info_cache + + _user_info_cache.clear() + + +@pytest.fixture +async def session(app: App): + from nonebot_plugin_datastore.db import create_session + + async with create_session() as session: + yield session + + +# https://stackoverflow.com/questions/29116718/how-to-mocking-created-time-in-sqlalchemy +@contextmanager +def patch_time(time_to_freeze, tick=True): + from src.plugins.user.models import User + + with freeze_time(time_to_freeze, tick=tick) as frozen_time: + + def set_timestamp(mapper, connection, target): + now = datetime.datetime.utcnow() + if hasattr(target, "created_at"): + target.created_at = now + + event.listen(User, "before_insert", set_timestamp, propagate=True) + yield frozen_time + event.remove(User, "before_insert", set_timestamp) + + +@pytest.fixture(scope="function") +def patch_current_time(): + return patch_time diff --git a/tests/plugins/user/test_bind_group.py b/tests/plugins/user/test_bind_group.py new file mode 100644 index 00000000..cd7e2c55 --- /dev/null +++ b/tests/plugins/user/test_bind_group.py @@ -0,0 +1,244 @@ +from nonebot import get_adapter +from nonebot.adapters.onebot.v11 import Adapter, Bot, Message +from nonebug import App +from pytest_mock import MockerFixture + +from tests.fake import fake_group_message_event_v11 + + +async def test_bind_group(app: App, patch_current_time, mocker: MockerFixture): + """群聊绑定用户""" + from src.plugins.user import bind_cmd, user_cmd + + mocked_random = mocker.patch("src.plugins.user.random.randint") + mocked_random.return_value = 123456 + + with patch_current_time("2023-09-14 10:46:10.416389", tick=False): + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/user"), user_id=1) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_group_member_info", + {"group_id": 10000, "user_id": 1}, + { + "user_id": 1, + "nickname": "nickname1", + "card": "card1", + }, + ) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:1\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_group_member_info", + {"group_id": 10000, "user_id": 10}, + { + "user_id": 10, + "nickname": "nickname10", + "card": "card10", + }, + ) + ctx.should_call_send( + event, + "用户 ID:2\n用户名:nickname10\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/bind")) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "命令 bind 可用于在多个平台间绑定用户数据。绑定过程中,原始平台的用户数据将完全保留,而目标平台的用户数据将被原始平台的数据所覆盖。\n请确认当前平台是你的目标平台,并在 5 分钟内使用你的账号在原始平台内向机器人发送以下文本:\n/bind nonebot/123456\n绑定完成后,你可以随时使用「bind -r」来解除绑定状态。", + True, + ) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11( + message=Message("/bind nonebot/123456"), user_id=1 + ) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "令牌核验成功!下面将进行第二步操作。\n请在 5 分钟内使用你的账号在目标平台内向机器人发送以下文本:\n/bind nonebot/123456\n注意:当前平台是你的原始平台,这里的用户数据将覆盖目标平台的数据。", + True, + ) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11( + message=Message("/bind nonebot/123456") + ) + + ctx.receive_event(bot, event) + ctx.should_call_send(event, "绑定成功", True) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/user"), user_id=1) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:1\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + +async def test_bind_group_different_user( + app: App, patch_current_time, mocker: MockerFixture +): + """群聊绑定用户,不是最开始发送绑定命令的用户""" + from src.plugins.user import bind_cmd, user_cmd + + mocked_random = mocker.patch("src.plugins.user.random.randint") + mocked_random.return_value = 123456 + + with patch_current_time("2023-09-14 10:46:10.416389", tick=False): + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/user"), user_id=1) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_group_member_info", + {"group_id": 10000, "user_id": 1}, + { + "user_id": 1, + "nickname": "nickname1", + "card": "card1", + }, + ) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:1\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_group_member_info", + {"group_id": 10000, "user_id": 10}, + { + "user_id": 10, + "nickname": "nickname10", + "card": "card10", + }, + ) + ctx.should_call_send( + event, + "用户 ID:2\n用户名:nickname10\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/bind")) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "命令 bind 可用于在多个平台间绑定用户数据。绑定过程中,原始平台的用户数据将完全保留,而目标平台的用户数据将被原始平台的数据所覆盖。\n请确认当前平台是你的目标平台,并在 5 分钟内使用你的账号在原始平台内向机器人发送以下文本:\n/bind nonebot/123456\n绑定完成后,你可以随时使用「bind -r」来解除绑定状态。", + True, + ) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11( + message=Message("/bind nonebot/123456"), user_id=1 + ) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "令牌核验成功!下面将进行第二步操作。\n请在 5 分钟内使用你的账号在目标平台内向机器人发送以下文本:\n/bind nonebot/123456\n注意:当前平台是你的原始平台,这里的用户数据将覆盖目标平台的数据。", + True, + ) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11( + message=Message("/bind nonebot/123456"), user_id=1 + ) + + ctx.receive_event(bot, event) + ctx.should_call_send(event, "请使用最开始要绑定账号进行操作", True) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/user"), user_id=1) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:1\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "用户 ID:2\n用户名:nickname10\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) diff --git a/tests/plugins/user/test_bind_private.py b/tests/plugins/user/test_bind_private.py new file mode 100644 index 00000000..8d5ea3e8 --- /dev/null +++ b/tests/plugins/user/test_bind_private.py @@ -0,0 +1,210 @@ +from nonebot import get_adapter +from nonebot.adapters.onebot.v11 import Adapter, Bot, Message +from nonebug import App +from pytest_mock import MockerFixture + +from tests.fake import fake_private_message_event_v11 + + +async def test_bind_private(app: App, patch_current_time, mocker: MockerFixture): + """私聊绑定用户""" + from src.plugins.user import bind_cmd, user_cmd + + mocked_random = mocker.patch("src.plugins.user.random.randint") + mocked_random.return_value = 123456 + + with patch_current_time("2023-09-14 10:46:10.416389", tick=False): + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/user"), user_id=1) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_stranger_info", + {"user_id": 1}, + { + "user_id": 1, + "nickname": "nickname1", + }, + ) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:1\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_stranger_info", + {"user_id": 10}, + { + "user_id": 10, + "nickname": "nickname10", + }, + ) + ctx.should_call_send( + event, + "用户 ID:2\n用户名:nickname10\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/bind")) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "命令 bind 可用于在多个平台间绑定用户数据。绑定过程中,原始平台的用户数据将完全保留,而目标平台的用户数据将被原始平台的数据所覆盖。\n请确认当前平台是你的目标平台,并在 5 分钟内使用你的账号在原始平台内向机器人发送以下文本:\n/bind nonebot/123456\n绑定完成后,你可以随时使用「bind -r」来解除绑定状态。", + True, + ) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11( + message=Message("/bind nonebot/123456"), user_id=1 + ) + + ctx.receive_event(bot, event) + ctx.should_call_send(event, "绑定成功", True) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/user"), user_id=1) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:1\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + +async def test_bind_private_invalid_token( + app: App, patch_current_time, mocker: MockerFixture +): + """私聊绑定用户,无效的令牌""" + from src.plugins.user import bind_cmd, user_cmd + + mocked_random = mocker.patch("src.plugins.user.random.randint") + mocked_random.return_value = 123456 + + with patch_current_time("2023-09-14 10:46:10.416389", tick=False): + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/user"), user_id=1) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_stranger_info", + {"user_id": 1}, + { + "user_id": 1, + "nickname": "nickname1", + }, + ) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:1\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_stranger_info", + {"user_id": 10}, + { + "user_id": 10, + "nickname": "nickname10", + }, + ) + ctx.should_call_send( + event, + "用户 ID:2\n用户名:nickname10\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/bind")) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "命令 bind 可用于在多个平台间绑定用户数据。绑定过程中,原始平台的用户数据将完全保留,而目标平台的用户数据将被原始平台的数据所覆盖。\n请确认当前平台是你的目标平台,并在 5 分钟内使用你的账号在原始平台内向机器人发送以下文本:\n/bind nonebot/123456\n绑定完成后,你可以随时使用「bind -r」来解除绑定状态。", + True, + ) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11( + message=Message("/bind nonebot/1"), user_id=1 + ) + + ctx.receive_event(bot, event) + ctx.should_call_send(event, "令牌不存在或已过期", True) + ctx.should_finished(bind_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/user"), user_id=1) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname1\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:1\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_send( + event, + "用户 ID:2\n用户名:nickname10\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) diff --git a/tests/plugins/user/test_remove_bind.py b/tests/plugins/user/test_remove_bind.py new file mode 100644 index 00000000..0b380680 --- /dev/null +++ b/tests/plugins/user/test_remove_bind.py @@ -0,0 +1,79 @@ +from nonebot import get_adapter +from nonebot.adapters.onebot.v11 import Adapter, Bot, Message +from nonebug import App +from pytest_mock import MockerFixture +from sqlalchemy import select + +from tests.fake import fake_private_message_event_v11 + + +async def test_remove_bind(app: App, patch_current_time, mocker: MockerFixture): + """解除绑定""" + from nonebot_plugin_datastore import create_session + + from src.plugins.user import bind_cmd + from src.plugins.user.models import Bind, User + + with patch_current_time("2023-09-14 10:46:10.416389", tick=False): + async with create_session() as session: + user = User(id=1, name="nickname") + user2 = User(id=2, name="nickname2") + session.add(user) + session.add(user2) + bind = Bind(pid=1, platform="qq", auser=user, buser=user) + bind2 = Bind(pid=10, platform="qq", auser=user, buser=user2) + session.add(bind) + session.add(bind2) + await session.commit() + + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/bind -r")) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_stranger_info", + {"user_id": 10}, + { + "user_id": 10, + "nickname": "nickname10", + }, + ) + ctx.should_call_send( + event, + "解绑成功", + True, + ) + ctx.should_finished(bind_cmd) + + async with create_session() as session: + bind = (await session.scalars(select(Bind).where(Bind.pid == 10))).one() + assert bind.aid == 2 + + +async def test_remove_bind_self(app: App, patch_current_time, mocker: MockerFixture): + """解除最初的绑定""" + from src.plugins.user import bind_cmd + + with patch_current_time("2023-09-14 10:46:10.416389", tick=False): + async with app.test_matcher(bind_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/bind -r")) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_stranger_info", + {"user_id": 10}, + { + "user_id": 10, + "nickname": "nickname10", + }, + ) + ctx.should_call_send( + event, + "不能解绑最初绑定的账号", + True, + ) + ctx.should_finished(bind_cmd) diff --git a/tests/plugins/user/test_user.py b/tests/plugins/user/test_user.py new file mode 100644 index 00000000..88acf94a --- /dev/null +++ b/tests/plugins/user/test_user.py @@ -0,0 +1,54 @@ +from nonebot import get_adapter +from nonebot.adapters.onebot.v11 import Adapter, Bot, Message +from nonebug import App + +from tests.fake import fake_group_message_event_v11, fake_private_message_event_v11 + + +async def test_user(app: App, patch_current_time): + """获取用户信息""" + from src.plugins.user import user_cmd + + with patch_current_time("2023-09-14 10:46:10.416389", tick=False): + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_group_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_group_member_info", + {"group_id": 10000, "user_id": 10}, + { + "user_id": 10, + "nickname": "nickname", + "card": "card", + }, + ) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd) + + async with app.test_matcher(user_cmd) as ctx: + adapter = get_adapter(Adapter) + bot = ctx.create_bot(base=Bot, adapter=adapter) + event = fake_private_message_event_v11(message=Message("/user")) + + ctx.receive_event(bot, event) + ctx.should_call_api( + "get_stranger_info", + {"user_id": 10}, + { + "user_id": 10, + "nickname": "nickname10", + }, + ) + ctx.should_call_send( + event, + "用户 ID:1\n用户名:nickname\n用户创建日期:2023-09-14 10:46:10.416389+08:00\n用户所在平台 ID:10\n用户所在平台:qq", + True, + ) + ctx.should_finished(user_cmd)