diff --git a/.gitignore b/.gitignore index 7947925..2342902 100644 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,6 @@ dmypy.json # End of https://www.toptal.com/developers/gitignore/api/python /.idea/ +.env.* +*.db +bot.py \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 16ad8fe..10106bc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,6 +6,23 @@ category = "main" optional = false python-versions = ">=3.7" +[[package]] +name = "anyio" +version = "3.6.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16,<0.22)"] + [[package]] name = "apscheduler" version = "3.10.0" @@ -94,6 +111,17 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -113,6 +141,24 @@ python-versions = ">=3.7" [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "fastapi" +version = "0.92.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" +starlette = ">=0.25.0,<0.26.0" + +[package.extras] +all = ["email-validator (>=1.1.1)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (>=0.12.0,<0.21.0)"] +doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer[all] (>=0.6.1,<0.8.0)"] +test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.10.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.6.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] + [[package]] name = "flake8" version = "5.0.4" @@ -138,6 +184,25 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" docs = ["Sphinx", "docutils (<0.18)"] test = ["objgraph", "psutil"] +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "httptools" +version = "0.5.0" +description = "A collection of framework independent HTTP protocol utils." +category = "dev" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + [[package]] name = "idna" version = "3.4" @@ -251,11 +316,13 @@ optional = false python-versions = ">=3.8,<4.0" [package.dependencies] +fastapi = {version = ">=0.87.0,<0.89.0 || >0.89.0,<1.0.0", optional = true, markers = "extra == \"fastapi\" or extra == \"all\""} loguru = ">=0.6.0,<0.7.0" pydantic = {version = ">=1.10.0,<2.0.0", extras = ["dotenv"]} pygtrie = ">=2.4.1,<3.0.0" tomlkit = ">=0.10.0,<1.0.0" typing-extensions = ">=3.10.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\""} yarl = ">=1.7.2,<2.0.0" [package.extras] @@ -391,6 +458,14 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" [package.dependencies] tzdata = {version = "*", markers = "python_version >= \"3.6\""} +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "requests" version = "2.28.2" @@ -430,6 +505,14 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "sqlalchemy" version = "1.4.46" @@ -473,6 +556,21 @@ python-versions = ">=3.6" [package.dependencies] typing-extensions = ">=3.7.4" +[[package]] +name = "starlette" +version = "0.25.0" +description = "The little ASGI library that shines." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] + [[package]] name = "tomli" version = "2.0.1" @@ -534,6 +632,60 @@ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +[[package]] +name = "uvicorn" +version = "0.20.0" +description = "The lightning-fast ASGI server." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "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\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.17.0" +description = "Fast implementation of asyncio event loop on top of libuv" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +dev = ["Cython (>=0.29.32,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.32,<0.30.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=22.0.0,<22.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"] + +[[package]] +name = "watchfiles" +version = "0.18.1" +description = "Simple, modern and high performance file watching and code reload in python." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "10.4" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "win32-setctime" version = "1.1.0" @@ -560,13 +712,17 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "c4ece84e9f74083f69e278f76aa274935a885afdeda51e741f9a42f2b814458f" +content-hash = "26d21ffd6770ec22c5f9cb64de7013f6f2a0be66d2d20a2534a32dc59aa61fe4" [metadata.files] aiosqlite = [ {file = "aiosqlite-0.18.0-py3-none-any.whl", hash = "sha256:c3511b841e3a2c5614900ba1d179f366826857586f78abd75e7cbeb88e75a557"}, {file = "aiosqlite-0.18.0.tar.gz", hash = "sha256:faa843ef5fb08bafe9a9b3859012d3d9d6f77ce3637899de20606b7fc39aa213"}, ] +anyio = [ + {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, + {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, +] apscheduler = [ {file = "APScheduler-3.10.0-py3-none-any.whl", hash = "sha256:575299f20073c60a2cc9d4fa5906024cdde33c5c0ce6087c4e3c14be3b50fdd4"}, {file = "APScheduler-3.10.0.tar.gz", hash = "sha256:a49fc23269218416f0e41890eea7a75ed6b284f10630dcfe866ab659621a3696"}, @@ -680,6 +836,10 @@ charset-normalizer = [ {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"}, {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, ] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] colorama = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -688,6 +848,10 @@ exceptiongroup = [ {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"}, {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"}, ] +fastapi = [ + {file = "fastapi-0.92.0-py3-none-any.whl", hash = "sha256:ae7b97c778e2f2ec3fb3cb4fb14162129411d99907fb71920f6d69a524340ebf"}, + {file = "fastapi-0.92.0.tar.gz", hash = "sha256:023a0f5bd2c8b2609014d3bba1e14a1d7df96c6abea0a73070621c9862b9a4de"}, +] flake8 = [ {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, @@ -754,6 +918,53 @@ greenlet = [ {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, ] +h11 = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] +httptools = [ + {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f470c79061599a126d74385623ff4744c4e0f4a0997a353a44923c0b561ee51"}, + {file = "httptools-0.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e90491a4d77d0cb82e0e7a9cb35d86284c677402e4ce7ba6b448ccc7325c5421"}, + {file = "httptools-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1d2357f791b12d86faced7b5736dea9ef4f5ecdc6c3f253e445ee82da579449"}, + {file = "httptools-0.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f90cd6fd97c9a1b7fe9215e60c3bd97336742a0857f00a4cb31547bc22560c2"}, + {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5230a99e724a1bdbbf236a1b58d6e8504b912b0552721c7c6b8570925ee0ccde"}, + {file = "httptools-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3a47a34f6015dd52c9eb629c0f5a8a5193e47bf2a12d9a3194d231eaf1bc451a"}, + {file = "httptools-0.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:24bb4bb8ac3882f90aa95403a1cb48465de877e2d5298ad6ddcfdebec060787d"}, + {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e67d4f8734f8054d2c4858570cc4b233bf753f56e85217de4dfb2495904cf02e"}, + {file = "httptools-0.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e5eefc58d20e4c2da82c78d91b2906f1a947ef42bd668db05f4ab4201a99f49"}, + {file = "httptools-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0297822cea9f90a38df29f48e40b42ac3d48a28637368f3ec6d15eebefd182f9"}, + {file = "httptools-0.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:557be7fbf2bfa4a2ec65192c254e151684545ebab45eca5d50477d562c40f986"}, + {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:54465401dbbec9a6a42cf737627fb0f014d50dc7365a6b6cd57753f151a86ff0"}, + {file = "httptools-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4d9ebac23d2de960726ce45f49d70eb5466725c0087a078866043dad115f850f"}, + {file = "httptools-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:e8a34e4c0ab7b1ca17b8763613783e2458e77938092c18ac919420ab8655c8c1"}, + {file = "httptools-0.5.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f659d7a48401158c59933904040085c200b4be631cb5f23a7d561fbae593ec1f"}, + {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1616b3ba965cd68e6f759eeb5d34fbf596a79e84215eeceebf34ba3f61fdc7"}, + {file = "httptools-0.5.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3625a55886257755cb15194efbf209584754e31d336e09e2ffe0685a76cb4b60"}, + {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:72ad589ba5e4a87e1d404cc1cb1b5780bfcb16e2aec957b88ce15fe879cc08ca"}, + {file = "httptools-0.5.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:850fec36c48df5a790aa735417dca8ce7d4b48d59b3ebd6f83e88a8125cde324"}, + {file = "httptools-0.5.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f222e1e9d3f13b68ff8a835574eda02e67277d51631d69d7cf7f8e07df678c86"}, + {file = "httptools-0.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3cb8acf8f951363b617a8420768a9f249099b92e703c052f9a51b66342eea89b"}, + {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550059885dc9c19a072ca6d6735739d879be3b5959ec218ba3e013fd2255a11b"}, + {file = "httptools-0.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a04fe458a4597aa559b79c7f48fe3dceabef0f69f562daf5c5e926b153817281"}, + {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d0c1044bce274ec6711f0770fd2d5544fe392591d204c68328e60a46f88843b"}, + {file = "httptools-0.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c6eeefd4435055a8ebb6c5cc36111b8591c192c56a95b45fe2af22d9881eee25"}, + {file = "httptools-0.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b65be160adcd9de7a7e6413a4966665756e263f0d5ddeffde277ffeee0576a5"}, + {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fe9c766a0c35b7e3d6b6939393c8dfdd5da3ac5dec7f971ec9134f284c6c36d6"}, + {file = "httptools-0.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:85b392aba273566c3d5596a0a490978c085b79700814fb22bfd537d381dd230c"}, + {file = "httptools-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e3088f4ed33947e16fd865b8200f9cfae1144f41b64a8cf19b599508e096bc"}, + {file = "httptools-0.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c2a56b6aad7cc8f5551d8e04ff5a319d203f9d870398b94702300de50190f63"}, + {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b571b281a19762adb3f48a7731f6842f920fa71108aff9be49888320ac3e24d"}, + {file = "httptools-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa47ffcf70ba6f7848349b8a6f9b481ee0f7637931d91a9860a1838bfc586901"}, + {file = "httptools-0.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:bede7ee075e54b9a5bde695b4fc8f569f30185891796b2e4e09e2226801d09bd"}, + {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:64eba6f168803a7469866a9c9b5263a7463fa8b7a25b35e547492aa7322036b6"}, + {file = "httptools-0.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4b098e4bb1174096a93f48f6193e7d9aa7071506a5877da09a783509ca5fff42"}, + {file = "httptools-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9423a2de923820c7e82e18980b937893f4aa8251c43684fa1772e341f6e06887"}, + {file = "httptools-0.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca1b7becf7d9d3ccdbb2f038f665c0f4857e08e1d8481cbcc1a86a0afcfb62b2"}, + {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:50d4613025f15f4b11f1c54bbed4761c0020f7f921b95143ad6d58c151198142"}, + {file = "httptools-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8ffce9d81c825ac1deaa13bc9694c0562e2840a48ba21cfc9f3b4c922c16f372"}, + {file = "httptools-0.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:1af91b3650ce518d226466f30bbba5b6376dbd3ddb1b2be8b0658c6799dd450b"}, + {file = "httptools-0.5.0.tar.gz", hash = "sha256:295874861c173f9101960bba332429bb77ed4dcd8cdf5cee9922eb00e4f6bc09"}, +] idna = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, @@ -998,6 +1209,48 @@ pytz-deprecation-shim = [ {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, ] +pyyaml = [ + {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-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] requests = [ {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, @@ -1010,6 +1263,10 @@ six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +sniffio = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] sqlalchemy = [ {file = "SQLAlchemy-1.4.46-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:7001f16a9a8e06488c3c7154827c48455d1c1507d7228d43e781afbc8ceccf6d"}, {file = "SQLAlchemy-1.4.46-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c7a46639ba058d320c9f53a81db38119a74b8a7a1884df44d09fbe807d028aaf"}, @@ -1057,6 +1314,10 @@ sqlalchemy2-stubs = [ {file = "sqlalchemy2-stubs-0.0.2a32.tar.gz", hash = "sha256:2a2cfab71d35ac63bf21ad841d8610cd93a3bd4c6562848c538fa975585c2739"}, {file = "sqlalchemy2_stubs-0.0.2a32-py3-none-any.whl", hash = "sha256:7f5fb30b0cf7c6b74c50c1d94df77ff32007afee8d80499752eb3fedffdbdfb8"}, ] +starlette = [ + {file = "starlette-0.25.0-py3-none-any.whl", hash = "sha256:774f1df1983fd594b9b6fb3ded39c2aa1979d10ac45caac0f4255cbe2acb8628"}, + {file = "starlette-0.25.0.tar.gz", hash = "sha256:854c71e73736c429c2bdb07801f2c76c9cba497e7c3cf4988fde5e95fe4cdb3c"}, +] tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -1081,6 +1342,133 @@ urllib3 = [ {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, ] +uvicorn = [ + {file = "uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"}, + {file = "uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"}, +] +uvloop = [ + {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718"}, + {file = "uvloop-0.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:68532f4349fd3900b839f588972b3392ee56042e440dd5873dfbbcd2cc67617c"}, + {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0949caf774b9fcefc7c5756bacbbbd3fc4c05a6b7eebc7c7ad6f825b23998d6d"}, + {file = "uvloop-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff3d00b70ce95adce264462c930fbaecb29718ba6563db354608f37e49e09024"}, + {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a5abddb3558d3f0a78949c750644a67be31e47936042d4f6c888dd6f3c95f4aa"}, + {file = "uvloop-0.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8efcadc5a0003d3a6e887ccc1fb44dec25594f117a94e3127954c05cf144d811"}, + {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3378eb62c63bf336ae2070599e49089005771cc651c8769aaad72d1bd9385a7c"}, + {file = "uvloop-0.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6aafa5a78b9e62493539456f8b646f85abc7093dd997f4976bb105537cf2635e"}, + {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c686a47d57ca910a2572fddfe9912819880b8765e2f01dc0dd12a9bf8573e539"}, + {file = "uvloop-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:864e1197139d651a76c81757db5eb199db8866e13acb0dfe96e6fc5d1cf45fc4"}, + {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2a6149e1defac0faf505406259561bc14b034cdf1d4711a3ddcdfbaa8d825a05"}, + {file = "uvloop-0.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6708f30db9117f115eadc4f125c2a10c1a50d711461699a0cbfaa45b9a78e376"}, + {file = "uvloop-0.17.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:23609ca361a7fc587031429fa25ad2ed7242941adec948f9d10c045bfecab06b"}, + {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2deae0b0fb00a6af41fe60a675cec079615b01d68beb4cc7b722424406b126a8"}, + {file = "uvloop-0.17.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45cea33b208971e87a31c17622e4b440cac231766ec11e5d22c76fab3bf9df62"}, + {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9b09e0f0ac29eee0451d71798878eae5a4e6a91aa275e114037b27f7db72702d"}, + {file = "uvloop-0.17.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dbbaf9da2ee98ee2531e0c780455f2841e4675ff580ecf93fe5c48fe733b5667"}, + {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a4aee22ece20958888eedbad20e4dbb03c37533e010fb824161b4f05e641f738"}, + {file = "uvloop-0.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:307958f9fc5c8bb01fad752d1345168c0abc5d62c1b72a4a8c6c06f042b45b20"}, + {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ebeeec6a6641d0adb2ea71dcfb76017602ee2bfd8213e3fcc18d8f699c5104f"}, + {file = "uvloop-0.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1436c8673c1563422213ac6907789ecb2b070f5939b9cbff9ef7113f2b531595"}, + {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8887d675a64cfc59f4ecd34382e5b4f0ef4ae1da37ed665adba0c2badf0d6578"}, + {file = "uvloop-0.17.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3db8de10ed684995a7f34a001f15b374c230f7655ae840964d51496e2f8a8474"}, + {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7d37dccc7ae63e61f7b96ee2e19c40f153ba6ce730d8ba4d3b4e9738c1dccc1b"}, + {file = "uvloop-0.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cbbe908fda687e39afd6ea2a2f14c2c3e43f2ca88e3a11964b297822358d0e6c"}, + {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d97672dc709fa4447ab83276f344a165075fd9f366a97b712bdd3fee05efae8"}, + {file = "uvloop-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e507c9ee39c61bfddd79714e4f85900656db1aec4d40c6de55648e85c2799c"}, + {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c092a2c1e736086d59ac8e41f9c98f26bbf9b9222a76f21af9dfe949b99b2eb9"}, + {file = "uvloop-0.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30babd84706115626ea78ea5dbc7dd8d0d01a2e9f9b306d24ca4ed5796c66ded"}, + {file = "uvloop-0.17.0.tar.gz", hash = "sha256:0ddf6baf9cf11a1a22c71487f39f15b2cf78eb5bde7e5b45fbb99e8a9d91b9e1"}, +] +watchfiles = [ + {file = "watchfiles-0.18.1-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:9891d3c94272108bcecf5597a592e61105279def1313521e637f2d5acbe08bc9"}, + {file = "watchfiles-0.18.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:7102342d60207fa635e24c02a51c6628bf0472e5fef067f78a612386840407fc"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:00ea0081eca5e8e695cffbc3a726bb90da77f4e3f78ce29b86f0d95db4e70ef7"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8e6db99e49cd7125d8a4c9d33c0735eea7b75a942c6ad68b75be3e91c242fb"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc7c726855f04f22ac79131b51bf0c9f728cb2117419ed830a43828b2c4a5fcb"}, + {file = "watchfiles-0.18.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbaff354d12235002e62d9d3fa8bcf326a8490c1179aa5c17195a300a9e5952f"}, + {file = "watchfiles-0.18.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:888db233e06907c555eccd10da99b9cd5ed45deca47e41766954292dc9f7b198"}, + {file = "watchfiles-0.18.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:dde79930d1b28f15994ad6613aa2865fc7a403d2bb14585a8714a53233b15717"}, + {file = "watchfiles-0.18.1-cp37-abi3-win32.whl", hash = "sha256:e2b2bdd26bf8d6ed90763e6020b475f7634f919dbd1730ea1b6f8cb88e21de5d"}, + {file = "watchfiles-0.18.1-cp37-abi3-win_amd64.whl", hash = "sha256:c541e0f2c3e95e83e4f84561c893284ba984e9d0025352057396d96dceb09f44"}, + {file = "watchfiles-0.18.1-cp37-abi3-win_arm64.whl", hash = "sha256:9a26272ef3e930330fc0c2c148cc29706cc2c40d25760c7ccea8d768a8feef8b"}, + {file = "watchfiles-0.18.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:9fb12a5e2b42e0b53769455ff93546e6bc9ab14007fbd436978d827a95ca5bd1"}, + {file = "watchfiles-0.18.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:548d6b42303d40264118178053c78820533b683b20dfbb254a8706ca48467357"}, + {file = "watchfiles-0.18.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0d8fdfebc50ac7569358f5c75f2b98bb473befccf9498cf23b3e39993bb45a"}, + {file = "watchfiles-0.18.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0f9a22fff1745e2bb930b1e971c4c5b67ea3b38ae17a6adb9019371f80961219"}, + {file = "watchfiles-0.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b02e7fa03cd4059dd61ff0600080a5a9e7a893a85cb8e5178943533656eec65e"}, + {file = "watchfiles-0.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a868ce2c7565137f852bd4c863a164dc81306cae7378dbdbe4e2aca51ddb8857"}, + {file = "watchfiles-0.18.1.tar.gz", hash = "sha256:4ec0134a5e31797eb3c6c624dbe9354f2a8ee9c720e0b46fc5b7bab472b7c6d4"}, +] +websockets = [ + {file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"}, + {file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"}, + {file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"}, + {file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"}, + {file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"}, + {file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"}, + {file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"}, + {file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"}, + {file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"}, + {file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"}, + {file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"}, + {file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"}, + {file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"}, + {file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"}, + {file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"}, + {file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"}, + {file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"}, + {file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"}, + {file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"}, + {file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"}, + {file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"}, + {file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"}, + {file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"}, + {file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"}, + {file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"}, + {file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"}, + {file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"}, + {file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"}, + {file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"}, + {file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"}, + {file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"}, + {file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"}, + {file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"}, + {file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"}, + {file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"}, + {file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"}, + {file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"}, + {file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"}, + {file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"}, + {file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"}, +] win32-setctime = [ {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, diff --git a/pyproject.toml b/pyproject.toml index e69752e..9014bd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nonebot-plugin-mahjong-scoreboard" -version = "0.1.10" +version = "0.1.11" description = "日麻寄分器(NoneBot插件)" authors = ["ssttkkl "] readme = "README.MD" @@ -25,6 +25,7 @@ nonebot-plugin-gocqhttp-cross-machine-upload-file = "^0.1.0" nonebug = "^0.2.1" flake8 = "^5.0.4" sqlalchemy2-stubs = "^0.0.2a29" +nonebot2 = {extras = ["fastapi"], version = "^2.0.0rc3"} [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/src/nonebot_plugin_mahjong_scoreboard/__init__.py b/src/nonebot_plugin_mahjong_scoreboard/__init__.py index 22a6155..8f93f38 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/__init__.py +++ b/src/nonebot_plugin_mahjong_scoreboard/__init__.py @@ -33,11 +33,12 @@ - {default_cmd_start}结束赛季 - {default_cmd_start}删除赛季 [<代号>] -赛季PT: -- {default_cmd_start}设置赛季PT [@<用户>] +赛季查询: +- {default_cmd_start}查询榜单 [<赛季代号>] +- {default_cmd_start}导出榜单 [<赛季代号>] +- {default_cmd_start}查询最近走势 [@<用户>] - {default_cmd_start}查询PT [@<用户>] -- {default_cmd_start}查询榜单 -- {default_cmd_start}导出榜单 +- {default_cmd_start}设置PT [@<用户>] 以上命令格式中,以<>包裹的表示一个参数,以[]包裹的表示一个可选项。 @@ -49,8 +50,7 @@ __plugin_meta__ = PluginMetadata( name='日麻寄分器', description='为群友提供日麻计分及榜单统计功能', - usage=help_text, - extra={'version': '0.1.0'} + usage=help_text ) from . import controller diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/game_record.py b/src/nonebot_plugin_mahjong_scoreboard/controller/game_record.py index 2bb7dfa..264b38b 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/game_record.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/game_record.py @@ -12,7 +12,8 @@ require_game_code_from_context, require_parse_unary_integer_arg from nonebot_plugin_mahjong_scoreboard.controller.interceptor import general_interceptor from nonebot_plugin_mahjong_scoreboard.controller.mapper.game_mapper import map_game -from nonebot_plugin_mahjong_scoreboard.controller.utils import split_message, parse_int_or_error, try_parse_wind +from nonebot_plugin_mahjong_scoreboard.controller.utils import split_message, parse_int_or_error, try_parse_wind, \ + parse_float_or_error from nonebot_plugin_mahjong_scoreboard.errors import BadRequestError from nonebot_plugin_mahjong_scoreboard.model.enums import PlayerAndWind, GameState from nonebot_plugin_mahjong_scoreboard.service import game_service, group_service, user_service @@ -196,7 +197,7 @@ async def parse_record_point_args(event: GroupMessageEvent, matcher: Matcher): elif arg.type == 'at': user_id = int(arg.data["qq"]) - point = parse_int_or_error(point, 'PT') + point = parse_float_or_error(point, 'PT') if game_code is None: raise BadRequestError("请指定对局编号") diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/general_handlers.py b/src/nonebot_plugin_mahjong_scoreboard/controller/general_handlers.py index 1f4d6b7..81e27c6 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/general_handlers.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/general_handlers.py @@ -6,7 +6,7 @@ from nonebot_plugin_mahjong_scoreboard.controller.context import get_context from nonebot_plugin_mahjong_scoreboard.controller.interceptor import general_interceptor from nonebot_plugin_mahjong_scoreboard.controller.utils import get_group_info, split_message, \ - parse_int_or_error + parse_int_or_error, parse_float_or_error from nonebot_plugin_mahjong_scoreboard.errors import BadRequestError from nonebot_plugin_mahjong_scoreboard.service.group_service import get_group_by_binding_qq, ensure_group_admin from nonebot_plugin_mahjong_scoreboard.service.user_service import get_user_by_binding_qq @@ -82,6 +82,23 @@ async def receive(event: MessageEvent, matcher: Matcher): return matcher_type +def require_float(matcher_type: Type[Matcher], arg_name: str, desc: str): + @matcher_type.handle() + async def check(matcher: Matcher): + if arg_name not in matcher.state: + await matcher.pause(desc + "?") + + @matcher_type.handle() + @general_interceptor(matcher_type) + async def receive(event: MessageEvent, matcher: Matcher): + if arg_name not in matcher.state: + arg = event.message.extract_plain_text() + arg = parse_float_or_error(arg, desc) + matcher.state[arg_name] = arg + + return matcher_type + + def require_str(matcher_type: Type[Matcher], arg_name: str, desc: str): @matcher_type.handle() async def check(matcher: Matcher): diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/game_mapper.py b/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/game_mapper.py index 4350eeb..5546940 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/game_mapper.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/game_mapper.py @@ -34,49 +34,35 @@ async def map_game(game: GameOrm, *, detailed: bool = False) -> Message: session = data_source.session() group = await session.get(GroupOrm, game.group_id) + season = await session.get(SeasonOrm, game.season_id) with StringIO() as io: # 对局22090901 四人南 - io.write('对局') - io.write(str(game.code)) - io.write(' ') - io.write(player_and_wind_mapping[game.player_and_wind]) - io.write('\n') + io.write(f'对局{game.code} {player_and_wind_mapping[game.player_and_wind]}\n') if detailed: # 所属赛季:Season Name - io.write('所属赛季:') - if game.season_id is None: - io.write('无') - else: - season = await session.get(SeasonOrm, game.season_id) - io.write(season.name) - io.write('\n') + season_name = '无' + if game.season_id is not None: + season_name = season.name + io.write(f'所属赛季:{season_name}\n') promoter = await session.get(UserOrm, game.promoter_user_id) - io.write('创建者:') - io.write(await get_user_nickname(promoter, group)) - io.write('\n') + io.write(f'创建者:{await get_user_nickname(promoter, group)}\n') # 状态:未完成 - io.write('状态:') - io.write(game_state_mapping[GameState(game.state)]) + io.write(f'状态:{game_state_mapping[GameState(game.state)]}') if game.state != GameState.completed: - io.write(" (合计") - io.write(str(sum(map(lambda r: r.score, game.records)))) - io.write("点)") + sum_score = sum(map(lambda r: r.score, game.records)) + io.write(f" (合计{sum_score}点)") io.write('\n') if detailed and game.state == GameState.completed: - io.write('完成时间:') - io.write(map_datetime(game.complete_time)) - io.write('\n') + io.write(f'完成时间:{map_datetime(game.complete_time)}\n') progress = await session.get(GameProgressOrm, game.id) if progress is not None: - io.write('进度:') - io.write(map_game_progress(progress)) - io.write('\n') + io.write(f'进度:{map_game_progress(progress)}\n') if len(game.records) > 0: # [空行] @@ -87,26 +73,18 @@ async def map_game(game: GameOrm, *, detailed: bool = False) -> Message: for rank, r in ranked(game.records, key=lambda r: r.point, reverse=True): user = await session.get(UserOrm, r.user_id) name = await get_user_nickname(user, group) - io.write('#') - io.write(str(rank)) + io.write(f'#{rank}') if r.wind is not None: - io.write(' [') - io.write(wind_mapping[r.wind]) - io.write(']') - io.write(' ') - io.write(name) - io.write(' ') - io.write(str(r.score)) - io.write('点') + io.write(f' [{wind_mapping[r.wind]}]') + io.write(f' {name} {r.score}点') if game.state == GameState.completed: - io.write(' (') + point_text = str(r.point) if r.point > 0: - io.write('+') + point_text = f'+{point_text}' elif r.point == 0: - io.write('±') - io.write(str(r.point)) - io.write(')') + point_text = f'±{point_text}' + io.write(f' ({point_text})') io.write('\n') diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/season_mapper.py b/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/season_mapper.py index 955139a..f522b15 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/season_mapper.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/season_mapper.py @@ -3,7 +3,7 @@ from nonebot.adapters.onebot.v11 import Message, MessageSegment -from nonebot_plugin_mahjong_scoreboard.controller.mapper import season_state_mapping, datetime_format, map_datetime +from nonebot_plugin_mahjong_scoreboard.controller.mapper import season_state_mapping, map_datetime from nonebot_plugin_mahjong_scoreboard.model.orm.season import SeasonOrm @@ -43,11 +43,11 @@ def map_season(season: SeasonOrm, *, group_info: Optional[dict] = None) -> Messa # 半庄战:返点:30000 马点:50 30 -10 -30 io.write('半庄战:') - if season.config["south_game_enabled"]: + if season.config.south_game_enabled: io.write('返点:') - io.write(str(season.config["south_game_origin_point"])) + io.write(str(season.config.south_game_origin_point)) io.write(' 马点:') - for i in season.config["south_game_horse_point"]: + for i in season.config.south_game_horse_point: io.write(str(i)) io.write(' ') else: @@ -56,15 +56,20 @@ def map_season(season: SeasonOrm, *, group_info: Optional[dict] = None) -> Messa # 东风战:关闭 io.write('东风战:') - if season.config["east_game_enabled"]: + if season.config.east_game_enabled: io.write('返点:') - io.write(str(season.config["east_game_origin_point"])) + io.write(str(season.config.east_game_origin_point)) io.write(' 马点:') - for i in season.config["east_game_horse_point"]: + for i in season.config.east_game_horse_point: io.write(str(i)) io.write(' ') else: io.write('关闭') io.write('\n') + # PT精度:1 + io.write('PT精度:') + io.write(str(10 ** season.config.point_precision)) + io.write('\n') + return Message(MessageSegment.text(io.getvalue().strip())) diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/season_user_point_mapper.py b/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/season_user_point_mapper.py index a46ab51..bfbd549 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/season_user_point_mapper.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/mapper/season_user_point_mapper.py @@ -3,7 +3,7 @@ from nonebot.adapters.onebot.v11 import Message, MessageSegment -from nonebot_plugin_mahjong_scoreboard.controller.mapper import season_state_mapping, digit_mapping +from nonebot_plugin_mahjong_scoreboard.controller.mapper import season_state_mapping from nonebot_plugin_mahjong_scoreboard.model.orm import data_source from nonebot_plugin_mahjong_scoreboard.model.orm.game import GameRecordOrm, GameOrm from nonebot_plugin_mahjong_scoreboard.model.orm.group import GroupOrm @@ -14,6 +14,15 @@ from nonebot_plugin_mahjong_scoreboard.utils.rank import ranked +def map_point(point: int, precision: int) -> str: + point_text = str(point * 10 ** precision) + if point > 0: + point_text = f'+{point_text}' + elif point == 0: + point_text = f'±{point_text}' + return point_text + + async def map_season_user_point(sup: SeasonUserPointOrm, rank: Optional[int] = None, total: Optional[int] = None) -> Message: @@ -28,27 +37,17 @@ async def map_season_user_point(sup: SeasonUserPointOrm, with StringIO() as io: # [用户名]在赛季[赛季名] # PT:+114 - io.write(name) - io.write("在赛季") - io.write(season.name) - io.write('\n') - - io.write("PT:") - if sup.point > 0: - io.write('+') - elif sup.point == 0: - io.write('±') - io.write(str(sup.point)) + # 位次:30/36 + io.write(f"[{name}]在赛季[{season.name}]\n") + io.write(f"PT:{map_point(sup.point, season.config.point_precision)}\n") if rank is not None: # 位次:+114 - io.write('\n位次:') - io.write(str(rank)) + io.write(f'位次:{rank}') if total is not None: - io.write('/') - io.write(str(total)) + io.write(f'/{total}') - return Message(MessageSegment.text(io.getvalue())) + return Message(MessageSegment.text(io.getvalue().strip())) async def map_season_user_points(group: GroupOrm, season: SeasonOrm, sups: List[SeasonUserPointOrm]) -> List[Message]: @@ -61,24 +60,14 @@ async def map_season_user_points(group: GroupOrm, season: SeasonOrm, sups: List[ # 赛季:[赛季名] # 状态:进行中 - pending_message.write("赛季:") - pending_message.write(season.name) - pending_message.write("\n状态:") - pending_message.write(season_state_mapping[season.state]) - pending_message.write("\n\n") + pending_message.write(f"赛季:{season.name}\n") + pending_message.write(f"状态:{season_state_mapping[season.state]}\n\n") for rank, sup in ranked(sups, key=lambda sup: sup.point, reverse=True): user = await session.get(UserOrm, sup.user_id) name = await get_user_nickname(user, group) - point_text = "" - if sup.point > 0: - point_text = '+' - elif sup.point == 0: - point_text = '±' - point_text += str(sup.point) - - line = f"#{rank} {name} {point_text}\n" + line = f"#{rank} {name} {map_point(sup.point, season.config.point_precision)}\n" pending_message.write(line) pending += 1 @@ -96,29 +85,11 @@ async def map_season_user_points(group: GroupOrm, season: SeasonOrm, sups: List[ async def map_season_user_trend(group: GroupOrm, user: UserOrm, season: SeasonOrm, result: List[Tuple[SeasonUserPointChangeLogOrm, GameOrm, GameRecordOrm]]) -> Message: with StringIO() as sio: - sio.write("用户[") - sio.write(await get_user_nickname(user, group)) - sio.write("]在赛季[") - sio.write(season.name) - sio.write("]的最近走势如下:\n") + sio.write(f"用户[{await get_user_nickname(user, group)}]在赛季[{season.name}]的最近走势如下:\n") for log, game, record in result: - sio.write(" ") - - sio.write(digit_mapping[record.rank]) - sio.write("位 ") - - sio.write(str(record.score)) - sio.write('点 (') - if record.point > 0: - sio.write('+') - elif record.point == 0: - sio.write('±') - sio.write(str(record.point)) - sio.write(') ') - - sio.write("对局") - sio.write(str(game.code)) - sio.write("\n") + sio.write(f" {record.rank}位 {record.score}点 " + f"({map_point(record.raw_point, record.point_scale)}) " + f"对局{game.code}\n") return Message(MessageSegment.text(sio.getvalue().strip())) diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/season_manage.py b/src/nonebot_plugin_mahjong_scoreboard/controller/season_manage.py index 77f7bba..a1ca6aa 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/season_manage.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/season_manage.py @@ -12,7 +12,7 @@ from nonebot_plugin_mahjong_scoreboard.controller.utils import parse_int_or_reject from nonebot_plugin_mahjong_scoreboard.errors import BadRequestError from nonebot_plugin_mahjong_scoreboard.model.enums import SeasonState -from nonebot_plugin_mahjong_scoreboard.model.orm.season import SeasonOrm +from nonebot_plugin_mahjong_scoreboard.model.orm.season import SeasonOrm, SeasonConfig from nonebot_plugin_mahjong_scoreboard.service import season_service from nonebot_plugin_mahjong_scoreboard.service.group_service import get_group_by_binding_qq from nonebot_plugin_mahjong_scoreboard.service.user_service import get_user_by_binding_qq @@ -66,7 +66,7 @@ async def new_season_got_south_game_origin_point(matcher: Matcher, matcher.state["south_game_origin_point"] = None return - pt = await parse_int_or_reject(raw_arg, "半庄战原点") + pt = await parse_int_or_reject(raw_arg, "半庄战返点") matcher.state["south_game_origin_point"] = pt @@ -78,7 +78,7 @@ async def new_season_got_south_game_horse_point(matcher: Matcher, return try: - li = list(map(int, raw_arg.split(' '))) + li = list(map(float, raw_arg.split(' '))) except ValueError: await matcher.reject("输入的半庄战马点不合法。请重新输入") return @@ -111,7 +111,7 @@ async def new_season_got_south_game_origin_point(matcher: Matcher, matcher.state["east_game_origin_point"] = None return - pt = await parse_int_or_reject(raw_arg, "东风战原点") + pt = await parse_int_or_reject(raw_arg, "东风战返点") matcher.state["east_game_origin_point"] = pt @@ -123,7 +123,7 @@ async def new_season_got_east_game_horse_point(matcher: Matcher, return try: - li = list(map(int, raw_arg.split(' '))) + li = list(map(float, raw_arg.split(' '))) except ValueError: await matcher.reject("输入的东风战马点不合法。请重新输入") return @@ -133,19 +133,28 @@ async def new_season_got_east_game_horse_point(matcher: Matcher, matcher.state["east_game_horse_point"] = li +@new_season_matcher.got("point_precision", "PT精度?(输入x,则精度为10^x。例如:输入0,保留整数部分;输入-1,保留小数点后一位)") +@general_interceptor(new_season_matcher) +async def new_season_got_point_precision(matcher: Matcher, + raw_arg=ArgPlainText("point_precision")): + precision = await parse_int_or_reject(raw_arg, "PT精度", max=4, min=-1) + matcher.state["point_precision"] = precision + + @new_season_matcher.handle() @general_interceptor(new_season_matcher) async def new_season_confirm(matcher: Matcher): season = SeasonOrm(code=matcher.state["code"], name=matcher.state["name"], - config={ + config=SeasonConfig({ "south_game_enabled": matcher.state["south_game_enabled"], "south_game_origin_point": matcher.state.get("south_game_origin_point"), "south_game_horse_point": matcher.state.get("south_game_horse_point"), "east_game_enabled": matcher.state["east_game_enabled"], "east_game_origin_point": matcher.state.get("east_game_origin_point"), - "east_game_horse_point": matcher.state.get("east_game_horse_point") - }) + "east_game_horse_point": matcher.state.get("east_game_horse_point"), + "point_precision": matcher.state["point_precision"] + })) matcher.state["season"] = season msg = map_season(season, group_info=matcher.state["group_info"]) diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/season_query.py b/src/nonebot_plugin_mahjong_scoreboard/controller/season_query.py index 676f923..5f233b9 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/season_query.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/season_query.py @@ -53,7 +53,7 @@ async def query_all_seasons(bot: Bot, event: MessageEvent, matcher: Matcher): if len(seasons) != 0: group_info = await bot.get_group_info(group_id=group.binding_qq) header = Message([ - MessageSegment.text(f"以下是群组{group_info['group_name']}({group_info['group_id']})的最近对局") + MessageSegment.text(f"以下是群组{group_info['group_name']}({group_info['group_id']})的所有赛季") ]) messages = [header] for s in seasons: diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/season_user_point_manage.py b/src/nonebot_plugin_mahjong_scoreboard/controller/season_user_point_manage.py index 4fd82ff..2900852 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/season_user_point_manage.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/season_user_point_manage.py @@ -3,21 +3,16 @@ from nonebot.internal.matcher import Matcher from nonebot_plugin_mahjong_scoreboard.controller.general_handlers import require_group_binding_qq, \ - require_user_binding_qq, require_integer + require_user_binding_qq, require_float from nonebot_plugin_mahjong_scoreboard.controller.interceptor import general_interceptor from nonebot_plugin_mahjong_scoreboard.controller.mapper.season_user_point_mapper import map_season_user_point -from nonebot_plugin_mahjong_scoreboard.controller.utils import split_message, parse_int_or_error +from nonebot_plugin_mahjong_scoreboard.controller.utils import split_message, parse_int_or_error, parse_float_or_error from nonebot_plugin_mahjong_scoreboard.errors import BadRequestError from nonebot_plugin_mahjong_scoreboard.service import season_user_point_service, user_service from nonebot_plugin_mahjong_scoreboard.service.group_service import get_group_by_binding_qq from nonebot_plugin_mahjong_scoreboard.service.season_service import get_season_by_id -# ========== 设置赛季PT ========== -set_season_user_point_matcher = on_command("设置赛季PT", aliases={"设置PT", "设置pt", "设置赛季pt"}, priority=5) - -@set_season_user_point_matcher.handle() -@general_interceptor(set_season_user_point_matcher) async def parse_args(event: MessageEvent, matcher: Matcher): user_id = None point = None @@ -33,13 +28,23 @@ async def parse_args(event: MessageEvent, matcher: Matcher): matcher.state["user_binding_qq"] = user_id if point is not None: - point = parse_int_or_error(point, "PT") + point = parse_float_or_error(point, "PT") matcher.state["point"] = point +# ========== 设置用户PT ========== +set_season_user_point_matcher = on_command("设置用户PT", aliases={"设置用户pt", "设置PT", "设置pt"}, priority=5) + + +@set_season_user_point_matcher.handle() +@general_interceptor(set_season_user_point_matcher) +async def parse_set_season_user_point_args(event: MessageEvent, matcher: Matcher): + await parse_args(event, matcher) + + require_group_binding_qq(set_season_user_point_matcher) require_user_binding_qq(set_season_user_point_matcher) -require_integer(set_season_user_point_matcher, "point", "PT") +require_float(set_season_user_point_matcher, "point", "PT") @set_season_user_point_matcher.handle() @@ -57,7 +62,40 @@ async def set_season_user_point(event: MessageEvent, matcher: Matcher): season = await get_season_by_id(group.running_season_id) sup = await season_user_point_service.change_season_user_point_manually(season, user, point, operator) msg = await map_season_user_point(sup) - msg.append(MessageSegment.text("\n\n设置赛季PT成功")) + msg.append(MessageSegment.text("\n\n设置用户PT成功")) await matcher.send(msg) else: raise BadRequestError("当前没有运行中的赛季") + +# # ========== 初始化用户PT ========== +# initialize_season_user_point_matcher = on_command("初始化用户PT", aliases={"初始化用户pt"}, priority=5) +# +# +# @initialize_season_user_point_matcher.handle() +# @general_interceptor(initialize_season_user_point_matcher) +# async def parse_initialize_season_user_point_args(event: MessageEvent, matcher: Matcher): +# await parse_args(event, matcher) +# +# +# require_group_binding_qq(initialize_season_user_point_matcher) +# require_user_binding_qq(initialize_season_user_point_matcher) +# +# +# @initialize_season_user_point_matcher.handle() +# @general_interceptor(initialize_season_user_point_matcher) +# async def set_season_user_point(event: MessageEvent, matcher: Matcher): +# group_binding_qq = matcher.state["binding_qq"] +# user_binding_qq = matcher.state["user_binding_qq"] +# +# group = await get_group_by_binding_qq(group_binding_qq) +# user = await user_service.get_user_by_binding_qq(user_binding_qq) +# operator = await user_service.get_user_by_binding_qq(event.user_id) +# +# if group.running_season_id is not None: +# season = await get_season_by_id(group.running_season_id) +# sup = await season_user_point_service.change_season_user_point_manually(season, user, point, operator) +# msg = await map_season_user_point(sup) +# msg.append(MessageSegment.text("\n\n初始化用户PT成功")) +# await matcher.send(msg) +# else: +# raise BadRequestError("当前没有运行中的赛季") diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/season_user_point_query.py b/src/nonebot_plugin_mahjong_scoreboard/controller/season_user_point_query.py index a2d731a..cce0b64 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/season_user_point_query.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/season_user_point_query.py @@ -3,13 +3,13 @@ from nonebot.internal.matcher import Matcher from nonebot_plugin_mahjong_scoreboard.controller.general_handlers import require_group_binding_qq, \ - require_user_binding_qq, require_parse_unary_at_arg + require_user_binding_qq, require_parse_unary_at_arg, require_parse_unary_text_arg from nonebot_plugin_mahjong_scoreboard.controller.interceptor import general_interceptor from nonebot_plugin_mahjong_scoreboard.controller.mapper.season_user_point_mapper import map_season_user_point, \ map_season_user_points, map_season_user_trend from nonebot_plugin_mahjong_scoreboard.controller.utils import send_group_forward_msg, send_private_forward_msg from nonebot_plugin_mahjong_scoreboard.errors import BadRequestError -from nonebot_plugin_mahjong_scoreboard.service import season_user_point_service +from nonebot_plugin_mahjong_scoreboard.service import season_user_point_service, season_service from nonebot_plugin_mahjong_scoreboard.service.group_service import get_group_by_binding_qq from nonebot_plugin_mahjong_scoreboard.service.season_service import get_season_by_id from nonebot_plugin_mahjong_scoreboard.service.season_user_point_service import get_season_user_point_rank, \ @@ -51,30 +51,36 @@ async def query_season_point(matcher: Matcher): # ========== 查询榜单 ========== query_season_ranking_matcher = on_command("查询榜单", aliases={"榜单"}, priority=5) +require_parse_unary_text_arg(query_season_ranking_matcher, "season_code") require_group_binding_qq(query_season_ranking_matcher) @query_season_ranking_matcher.handle() @general_interceptor(query_season_ranking_matcher) async def query_season_ranking(bot: Bot, event: MessageEvent, matcher: Matcher): - group_binding_qq = matcher.state["binding_qq"] + group = await get_group_by_binding_qq(matcher.state["binding_qq"]) - group = await get_group_by_binding_qq(group_binding_qq) + season_code = matcher.state.get("season_code", None) + if season_code: + season = await season_service.get_season_by_code(season_code, group) + if season is None: + raise BadRequestError("找不到指定赛季") + else: + if group.running_season_id: + season = await season_service.get_season_by_id(group.running_season_id) + else: + raise BadRequestError("当前没有运行中的赛季") - if group.running_season_id is not None: - season = await get_season_by_id(group.running_season_id) - sups = await season_user_point_service.get_season_user_points(season) + sups = await season_user_point_service.get_season_user_points(season) - msgs = await map_season_user_points(group, season, sups) - if len(msgs) == 1: - await matcher.send(msgs[0]) - else: - if isinstance(event, GroupMessageEvent): - await send_group_forward_msg(bot, event.group_id, msgs) - else: - await send_private_forward_msg(bot, event.user_id, msgs) + msgs = await map_season_user_points(group, season, sups) + if len(msgs) == 1: + await matcher.send(msgs[0]) else: - raise BadRequestError("当前没有运行中的赛季") + if isinstance(event, GroupMessageEvent): + await send_group_forward_msg(bot, event.group_id, msgs) + else: + await send_private_forward_msg(bot, event.user_id, msgs) # ========== 查询最近走势 ========== diff --git a/src/nonebot_plugin_mahjong_scoreboard/controller/utils.py b/src/nonebot_plugin_mahjong_scoreboard/controller/utils.py index 39f5eeb..9999f3e 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/controller/utils.py +++ b/src/nonebot_plugin_mahjong_scoreboard/controller/utils.py @@ -42,31 +42,65 @@ async def get_group_info(group_binding_qq: int): raise BadRequestError(e.info["wording"]) -def parse_int_or_error(raw: Union[int, str, None], desc: str, allow_chinese: bool = False) -> int: +def parse_int_or_error(raw: Union[int, str, None], + desc: str, + allow_chinese: bool = False, + max: Optional[int] = None, + min: Optional[int] = None) -> int: if not raw: raise BadRequestError(f"请指定{desc}") try: if allow_chinese: - return decode_integer(raw) + x = decode_integer(raw) else: - return int(raw) - except ValueError: - raise BadRequestError(f"输入的{desc}不合法") + x = int(raw) + if max is not None and x > max or min is not None and x < min: + raise ValueError(x) -async def parse_int_or_reject(raw: Union[int, str, None], desc: str, allow_chinese: bool = False) -> int: + return x + except ValueError: + comment = [] + if min is not None: + comment.append(f"最小值:{min}") + if max is not None: + comment.append(f"最大值:{max}") + comment = ','.join(comment) + if len(comment) != 0: + comment = f"({comment})" + raise BadRequestError(comment) + + +async def parse_int_or_reject(raw: Union[int, str, None], + desc: str, + allow_chinese: bool = False, + max: Optional[int] = None, + min: Optional[int] = None) -> int: matcher = current_matcher.get() + try: + return parse_int_or_error(raw, desc, allow_chinese, max, min) + except BadRequestError as e: + await matcher.reject(e.message) + + +def parse_float_or_error(raw: Union[float, int, str, None], desc: str) -> float: if not raw: - await matcher.reject(f"请指定{desc}") + raise BadRequestError(f"请指定{desc}") try: - if allow_chinese: - return decode_integer(raw) - else: - return int(raw) + return float(raw) except ValueError: - await matcher.reject(f"输入的{desc}不合法。请重新输入") + raise BadRequestError(f"输入的{desc}不合法") + + +async def parse_float_or_reject(raw: Union[float, int, str, None], desc: str) -> float: + matcher = current_matcher.get() + + try: + return parse_float_or_error(raw, desc) + except BadRequestError as e: + await matcher.reject(e.message) def try_parse_wind(text: str) -> Optional[Wind]: diff --git a/src/nonebot_plugin_mahjong_scoreboard/model/orm/game.py b/src/nonebot_plugin_mahjong_scoreboard/model/orm/game.py index cf1e866..55660d5 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/model/orm/game.py +++ b/src/nonebot_plugin_mahjong_scoreboard/model/orm/game.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import List, TYPE_CHECKING, Optional -from sqlalchemy import Column, Integer, Enum, DateTime, Boolean, ForeignKey, Text, Index +from sqlalchemy import Column, Integer, Enum, Boolean, ForeignKey, Text, Index, DateTime from sqlalchemy.orm import relationship from nonebot_plugin_mahjong_scoreboard.model.enums import Wind @@ -72,7 +72,15 @@ class GameRecordOrm: wind: Optional[Wind] = Column(Enum(Wind)) score: int = Column(Integer, nullable=False) # 分数 - point: int = Column(Integer, nullable=False, default=0) # pt + + # 真正PT=raw_point*10^point_scale,例如point_scale=0时缩放1x,point_scale=-1时缩放0.1x + raw_point: int = Column('point', Integer, nullable=False, default=0) # pt + point_scale: int = Column(Integer, nullable=False, default=0) # pt缩放比例 + + @property + def point(self) -> float: + return self.raw_point * 10 ** self.point_scale + rank: Optional[int] = Column("rnk", Integer) # 排名 diff --git a/src/nonebot_plugin_mahjong_scoreboard/model/orm/season.py b/src/nonebot_plugin_mahjong_scoreboard/model/orm/season.py index dfcdf92..d523a28 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/model/orm/season.py +++ b/src/nonebot_plugin_mahjong_scoreboard/model/orm/season.py @@ -1,11 +1,14 @@ +from collections import UserDict from datetime import datetime -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Optional, List -from sqlalchemy import Column, Integer, DateTime, String, Enum, ForeignKey, Boolean, Index, JSON +from sqlalchemy import Column, Integer, DateTime, String, Enum, ForeignKey, Boolean, Index from sqlalchemy.orm import relationship from . import data_source +from .types.userdict import UserDict as SqlUserDict from ..enums import SeasonState, SeasonUserPointChangeType +from ...utils.userdict import DictField if TYPE_CHECKING: from .game import GameOrm @@ -13,6 +16,16 @@ from .user import UserOrm +class SeasonConfig(UserDict): + south_game_enabled: bool = DictField() + south_game_origin_point: Optional[int] = DictField(default=None) + south_game_horse_point: Optional[List[int]] = DictField(default_factory=list) + east_game_enabled: bool = DictField() + east_game_origin_point: Optional[int] = DictField(default=None) + east_game_horse_point: Optional[List[int]] = DictField(default_factory=list) + point_precision: int = DictField(default=0) # PT精确到10^point_precision + + @data_source.registry.mapped class SeasonOrm: __tablename__ = 'seasons' @@ -30,12 +43,12 @@ class SeasonOrm: start_time: Optional[datetime] = Column(DateTime) finish_time: Optional[datetime] = Column(DateTime) - config: dict = Column(JSON, nullable=False) + config: SeasonConfig = Column(SqlUserDict(SeasonConfig), nullable=False) accessible: bool = Column(Boolean, nullable=False, default=True) - create_time: datetime = Column('create_time', DateTime, nullable=False, default=datetime.utcnow) - update_time: datetime = Column('update_time', DateTime, nullable=False, default=datetime.utcnow) - delete_time: Optional[datetime] = Column('delete_time', DateTime) + create_time: datetime = Column(DateTime, nullable=False, default=datetime.utcnow) + update_time: datetime = Column(DateTime, nullable=False, default=datetime.utcnow) + delete_time: Optional[datetime] = Column(DateTime) __table_args__ = ( Index("seasons_group_id_code_idx", "group_id", "code"), @@ -54,8 +67,8 @@ class SeasonUserPointOrm: point: int = Column(Integer, nullable=False, default=0) - create_time: datetime = Column('create_time', DateTime, nullable=False, default=datetime.utcnow) - update_time: datetime = Column('update_time', DateTime, nullable=False, default=datetime.utcnow) + create_time: datetime = Column(DateTime, nullable=False, default=datetime.utcnow) + update_time: datetime = Column(DateTime, nullable=False, default=datetime.utcnow) @data_source.registry.mapped @@ -77,7 +90,7 @@ class SeasonUserPointChangeLogOrm: related_game: Optional['GameOrm'] = relationship('GameOrm', foreign_keys='SeasonUserPointChangeLogOrm.related_game_id') - create_time: datetime = Column('create_time', DateTime, nullable=False, default=datetime.utcnow) + create_time: datetime = Column(DateTime, nullable=False, default=datetime.utcnow) __table_args__ = ( Index("seasons_related_game_id_idx", "related_game_id"), diff --git a/src/nonebot_plugin_mahjong_scoreboard/model/orm/types/__init__.py b/src/nonebot_plugin_mahjong_scoreboard/model/orm/types/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/nonebot_plugin_mahjong_scoreboard/model/orm/types/userdict.py b/src/nonebot_plugin_mahjong_scoreboard/model/orm/types/userdict.py new file mode 100644 index 0000000..69a0e20 --- /dev/null +++ b/src/nonebot_plugin_mahjong_scoreboard/model/orm/types/userdict.py @@ -0,0 +1,19 @@ +from collections import UserDict as UserDictType +from typing import Type + +from sqlalchemy import TypeDecorator, JSON + + +class UserDict(TypeDecorator): + impl = JSON + cache_ok = True + + def __init__(self, dict_type: Type[UserDictType], *args, **kwargs): + super().__init__(*args, **kwargs) + self.dict_type = dict_type + + def process_bind_param(self, value, dialect): + return value.data + + def process_result_value(self, value, dialect): + return self.dict_type(value) diff --git a/src/nonebot_plugin_mahjong_scoreboard/service/game_service.py b/src/nonebot_plugin_mahjong_scoreboard/service/game_service.py index 23c223b..171d403 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/service/game_service.py +++ b/src/nonebot_plugin_mahjong_scoreboard/service/game_service.py @@ -246,68 +246,71 @@ async def _handle_full_recorded_game(game: GameOrm): season = await session.get(SeasonOrm, game.season_id) if game.player_and_wind == PlayerAndWind.four_men_east: - horse_point = season.config["east_game_horse_point"] - origin_point = season.config["east_game_origin_point"] + horse_point = season.config.east_game_horse_point + origin_point = season.config.east_game_origin_point elif game.player_and_wind == PlayerAndWind.four_men_south: - horse_point = season.config["south_game_horse_point"] - origin_point = season.config["south_game_origin_point"] + horse_point = season.config.south_game_horse_point + origin_point = season.config.south_game_origin_point else: raise ValueError("invalid players and wind") + point_scale = season.config.point_precision + horse_point = list(map(lambda x: x * (10 ** -point_scale), horse_point)) + # 降序排序(带上原索引) indexed_record: List[Tuple[GameRecordOrm, int]] = [(r, i) for i, r in enumerate(game.records)] - indexed_record.sort(key=lambda tup: tup[0].score, reverse=True) + # 先按照score降序,若score相同则按照风排序(顺序:东南西北None) + indexed_record.sort(key=lambda tup: (-tup[0].score, tup[0].wind is None, tup[0].wind)) # 处理同分 # 四人幸终 if indexed_record[0][0].score == indexed_record[1][0].score == \ indexed_record[2][0].score == indexed_record[3][0].score: - _divide_horse_point(indexed_record, horse_point, 0, 3) + _divide_horse_point(horse_point, 0, 3) # 一二三位同分 elif indexed_record[0][0].score == indexed_record[1][0].score == indexed_record[2][0].score: - _divide_horse_point(indexed_record, horse_point, 0, 2) + _divide_horse_point(horse_point, 0, 2) # 二三四位同分 elif indexed_record[1][0].score == indexed_record[2][0].score == indexed_record[3][0].score: - _divide_horse_point(indexed_record, horse_point, 1, 3) + _divide_horse_point(horse_point, 1, 3) # 一二位同分 elif indexed_record[0][0].score == indexed_record[1][0].score: - _divide_horse_point(indexed_record, horse_point, 0, 1) + _divide_horse_point(horse_point, 0, 1) # 且三四位同分 if indexed_record[2][0].score == indexed_record[3][0].score: - _divide_horse_point(indexed_record, horse_point, 2, 3) + _divide_horse_point(horse_point, 2, 3) # 二三位同分 elif indexed_record[1][0].score == indexed_record[2][0].score: - _divide_horse_point(indexed_record, horse_point, 1, 2) + _divide_horse_point(horse_point, 1, 2) # 三四位同分 elif indexed_record[2][0].score == indexed_record[3][0].score: - _divide_horse_point(indexed_record, horse_point, 2, 3) + _divide_horse_point(horse_point, 2, 3) rank = 0 for i, (r, j) in enumerate(indexed_record): # (点数-返点+马点)/1000,切上 - r.point = horse_point[i] + ceil((r.score - origin_point) / 1000) + r.raw_point = ceil(horse_point[i] + (r.score - origin_point) * (10 ** (-point_scale - 3))) + r.point_scale = point_scale - if i == 0 or indexed_record[i - 1][0].point != r.point: + if i == 0 or indexed_record[i - 1][0].raw_point != r.raw_point: rank += 1 r.rank = rank await change_season_user_point_by_game(game) -def _divide_horse_point(indexed_record: List[Tuple[GameRecordOrm, int]], horse_point: List[int], start: int, end: int): +def _divide_horse_point(horse_point: List[int], start: int, end: int): sum_horse_point = sum(horse_point[start:end + 1]) divided_horse_point = sum_horse_point // (end - start + 1) for i in range(start, end + 1): horse_point[i] = divided_horse_point + # 除不尽的部分给第一个 if divided_horse_point * (end - start + 1) != sum_horse_point: - min_index = start - for i in range(start + 1, end + 1): - if indexed_record[i][1] < indexed_record[min_index][1]: - min_index = i - horse_point[min_index] += sum_horse_point - divided_horse_point * (end - start + 1) + rest_horse_point = sum_horse_point - divided_horse_point * (end - start + 1) + horse_point[start] += rest_horse_point async def revert_record(game_code: int, @@ -428,7 +431,7 @@ async def remove_game_progress(game_code: int, group: GroupOrm): return game -async def set_record_point(game_code: int, group: GroupOrm, user: UserOrm, point: int, operator: UserOrm): +async def set_record_point(game_code: int, group: GroupOrm, user: UserOrm, point: float, operator: UserOrm): session = data_source.session() game = await get_game_by_code(game_code, group) @@ -452,7 +455,11 @@ async def set_record_point(game_code: int, group: GroupOrm, user: UserOrm, point raise BadRequestError("这场对局不属于赛季") await revert_season_user_point_by_game(game) - record.point = point + + season = await session.get(SeasonOrm, group.season_id) + record.point_scale = season.config.point_precision + record.raw_point = int(point * (10 ** -season.config.point_precision)) + await change_season_user_point_by_game(game) game.update_time = datetime.utcnow() @@ -479,4 +486,5 @@ async def set_game_comment(game_code: int, group: GroupOrm, comment: str, operat __all__ = ("get_game_by_code", "get_games", "new_game", "delete_game", "record_game", "revert_record", "set_record_point", - "make_game_progress", "remove_game_progress") + "make_game_progress", "remove_game_progress", + "delete_uncompleted_season_games") diff --git a/src/nonebot_plugin_mahjong_scoreboard/service/season_user_point_service.py b/src/nonebot_plugin_mahjong_scoreboard/service/season_user_point_service.py index 3b7a891..3411883 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/service/season_user_point_service.py +++ b/src/nonebot_plugin_mahjong_scoreboard/service/season_user_point_service.py @@ -121,7 +121,7 @@ async def count_season_user_point(season: SeasonOrm) -> Optional[SeasonUserPoint async def change_season_user_point_manually(season: SeasonOrm, user: UserOrm, - point: int, + point: float, operator: UserOrm) -> SeasonUserPointOrm: session = data_source.session() group = await session.get(GroupOrm, season.group_id) @@ -133,7 +133,7 @@ async def change_season_user_point_manually(season: SeasonOrm, sup = SeasonUserPointOrm(season=season, user=user) session.add(sup) - sup.point = point + sup.point = int(point * (10 ** -season.config.point_precision)) log = SeasonUserPointChangeLogOrm(season=season, user=user, change_type=SeasonUserPointChangeType.manually, @@ -157,7 +157,9 @@ async def change_season_user_point_by_game(game: GameOrm): user_point = (await session.execute(stmt)).scalar_one_or_none() if user_point is None: - user_point = SeasonUserPointOrm(season_id=game.season_id, user_id=r.user_id, point=0) + user_point = SeasonUserPointOrm(season_id=game.season_id, + user_id=r.user_id, + point=0) session.add(user_point) user_point.point += r.point @@ -173,6 +175,55 @@ async def change_season_user_point_by_game(game: GameOrm): await session.commit() +# async def revert_season_user_point_manually(season: SeasonOrm, +# user: UserOrm, +# operator: UserOrm) -> bool: +# session = data_source.session() +# group = await session.get(GroupOrm, season.group_id) +# if not await is_group_admin(operator, group): +# raise BadRequestError("没有权限") +# +# stmt = select(SeasonUserPointChangeLogOrm).where( +# SeasonUserPointChangeLogOrm.season_id == season.id, +# SeasonUserPointChangeLogOrm.user_id == user.id +# ).order_by( +# SeasonUserPointChangeLogOrm.create_time.desc() +# ).limit(2) +# +# result = await session.scalars(stmt) +# if len(result) == 0: +# return False +# else: +# log: SeasonUserPointChangeLogOrm = result[0] +# prev_log: SeasonUserPointChangeLogOrm = result[1] if len(result) >= 2 else None +# +# if log.change_type != SeasonUserPointChangeType.manually \ +# or datetime.utcnow() - log.create_time > timedelta(days=1): +# return False +# +# stmt = delete(SeasonUserPointChangeLogOrm).where( +# SeasonUserPointChangeLogOrm.season_id == season.id, +# SeasonUserPointChangeLogOrm.user_id == user.id +# ) +# await session.execute(stmt) +# +# if prev_log is not None: +# stmt = update(SeasonUserPointOrm).where( +# SeasonUserPointOrm.season_id == season.id, +# SeasonUserPointOrm.user_id == user.id +# ).values( +# point=prev_log.create_time +# ) +# else: +# stmt = delete(SeasonUserPointOrm).where( +# SeasonUserPointOrm.season_id == season.id, +# SeasonUserPointOrm.user_id == user.id +# ) +# await session.execute(stmt) +# +# await session.commit() + + async def revert_season_user_point_by_game(game: GameOrm): session = data_source.session() diff --git a/src/nonebot_plugin_mahjong_scoreboard/utils/rank.py b/src/nonebot_plugin_mahjong_scoreboard/utils/rank.py index 874bad0..afc5558 100644 --- a/src/nonebot_plugin_mahjong_scoreboard/utils/rank.py +++ b/src/nonebot_plugin_mahjong_scoreboard/utils/rank.py @@ -1,4 +1,11 @@ -def ranked(__iterable, *, key=None, reverse=False): +from typing import Iterable, Tuple, TypeVar, Callable, Any, Optional + +T = TypeVar("T") + + +def ranked(__iterable: Iterable[T], *, + key: Optional[Callable[[T], Any]] = None, + reverse: bool = False) -> Iterable[Tuple[int, T]]: if key is None: key = lambda x: x diff --git a/src/nonebot_plugin_mahjong_scoreboard/utils/userdict.py b/src/nonebot_plugin_mahjong_scoreboard/utils/userdict.py new file mode 100644 index 0000000..312ee25 --- /dev/null +++ b/src/nonebot_plugin_mahjong_scoreboard/utils/userdict.py @@ -0,0 +1,25 @@ +from typing import Any, Callable, Optional + +from pydantic.fields import Undefined + + +class DictField: + def __init__(self, *, name: str = '', + default: Any = Undefined, + default_factory: Optional[Callable[[], Any]] = None): + self.name = name + self.default = default + self.default_factory = default_factory + + def __set_name__(self, owner, name): + if not self.name: + self.name = name + + def __get__(self, instance, owner): + x = instance.get(self.name, self.default) + if x == Undefined and self.default_factory: + return self.default_factory() + return x + + def __set__(self, instance, value): + instance[self.name] = value