From c0969ea947d110d24ab0b95ef94ea76e1c9615ba Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 13:41:11 -0400 Subject: [PATCH 001/146] feat(aurora): added a Moderation model --- .gitignore | 1 + aurora/info.json | 1 + aurora/models.py | 62 +++++ poetry.lock | 693 +++++++++++++++++++++++++++-------------------- pyproject.toml | 3 +- 5 files changed, 467 insertions(+), 293 deletions(-) create mode 100644 aurora/models.py diff --git a/.gitignore b/.gitignore index 429d31f..3f0e8b8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .vscode site .venv +__pycache__ diff --git a/aurora/info.json b/aurora/info.json index 52a577e..d232f7e 100644 --- a/aurora/info.json +++ b/aurora/info.json @@ -9,6 +9,7 @@ "disabled": false, "min_bot_version": "3.5.0", "min_python_version": [3, 10, 0], + "requirements": ["pydantic"], "tags": [ "mod", "moderate", diff --git a/aurora/models.py b/aurora/models.py new file mode 100644 index 0000000..ba69b60 --- /dev/null +++ b/aurora/models.py @@ -0,0 +1,62 @@ +from typing import Dict, List, Optional + + +from discord import Guild +from pydantic import BaseModel +from utilities.database import connect + + +class Moderation(BaseModel): + moderation_id: int + timestamp: int + moderation_type: str + target_type: str + target_id: int + moderator_id: int + resolved: bool + expired: bool + duration: str + end_timestamp: int + reason: str + changes: List[Dict] + metadata: Dict + resolved_by: Optional[int] = None + resolve_reason: Optional[str] = None + role_id: Optional[int] = None + + def __str__(self): + return f"{self.moderation_type} {self.target_type} {self.target_id} {self.reason}" + + async def from_sql(self, moderation_id: int, guild: Guild): + """""" + database = connect() + cursor = database.cursor() + + query = f"SELECT * FROM moderation_{guild.id} WHERE moderation_id = ?;" + cursor.execute(query, (moderation_id,)) + result = cursor.fetchone() + + cursor.close() + database.close() + + if result: + ( + self.moderation_id, + self.timestamp, + self.moderation_type, + self.target_type, + self.target_id, + self.moderator_id, + self.role_id, + self.duration, + self.end_timestamp, + self.reason, + self.resolved, + self.resolved_by, + self.resolve_reason, + self.expired, + self.changes, + self.metadata, + ) = result[0:16] + + return self diff --git a/poetry.lock b/poetry.lock index 2767d72..0a5918b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiohttp" @@ -123,6 +123,17 @@ files = [ [package.dependencies] frozenlist = ">=1.1.0" +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + [[package]] name = "apsw" version = "3.45.2.0" @@ -322,13 +333,13 @@ files = [ [[package]] name = "cairocffi" -version = "1.6.1" +version = "1.7.0" description = "cffi-based cairo bindings for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "cairocffi-1.6.1-py3-none-any.whl", hash = "sha256:aa78ee52b9069d7475eeac457389b6275aa92111895d78fbaa2202a52dac112e"}, - {file = "cairocffi-1.6.1.tar.gz", hash = "sha256:78e6bbe47357640c453d0be929fa49cd05cce2e1286f3d2a1ca9cbda7efdb8b7"}, + {file = "cairocffi-1.7.0-py3-none-any.whl", hash = "sha256:1f29a8d41dbda4090c0aa33bcdea64f3b493e95f74a43ea107c4a8a7b7f632ef"}, + {file = "cairocffi-1.7.0.tar.gz", hash = "sha256:7761863603894305f3160eca68452f373433ca8745ab7dd445bd2c6ce50dcab7"}, ] [package.dependencies] @@ -336,7 +347,7 @@ cffi = ">=1.1.0" [package.extras] doc = ["sphinx", "sphinx_rtd_theme"] -test = ["flake8", "isort", "numpy", "pikepdf", "pytest"] +test = ["numpy", "pikepdf", "pytest", "ruff"] xcb = ["xcffib (>=1.4.0)"] [[package]] @@ -766,30 +777,31 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.42" +version = "3.1.43" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.42-py3-none-any.whl", hash = "sha256:1bf9cd7c9e7255f77778ea54359e54ac22a72a5b51288c457c881057b7bb9ecd"}, - {file = "GitPython-3.1.42.tar.gz", hash = "sha256:2d99869e0fef71a73cbd242528105af1d6c1b108c60dfabd994bf292f76c3ceb"}, + {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, + {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" [package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar"] +doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"] +test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"] [[package]] name = "griffe" -version = "0.42.1" +version = "0.44.0" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ - {file = "griffe-0.42.1-py3-none-any.whl", hash = "sha256:7e805e35617601355edcac0d3511cedc1ed0cb1f7645e2d336ae4b05bbae7b3b"}, - {file = "griffe-0.42.1.tar.gz", hash = "sha256:57046131384043ed078692b85d86b76568a686266cc036b9b56b704466f803ce"}, + {file = "griffe-0.44.0-py3-none-any.whl", hash = "sha256:8a4471c469ba980b87c843f1168850ce39d0c1d0c7be140dca2480f76c8e5446"}, + {file = "griffe-0.44.0.tar.gz", hash = "sha256:34aee1571042f9bf00529bc715de4516fb6f482b164e90d030300601009e0223"}, ] [package.dependencies] @@ -1057,13 +1069,13 @@ pytz = "*" [[package]] name = "mkdocs-material" -version = "9.5.15" +version = "9.5.18" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.15-py3-none-any.whl", hash = "sha256:e5c96dec3d19491de49ca643fc1dbb92b278e43cdb816c775bc47db77d9b62fb"}, - {file = "mkdocs_material-9.5.15.tar.gz", hash = "sha256:39f03cca45e82bf54eb7456b5a18bd252eabfdd67f237a229471484a0a4d4635"}, + {file = "mkdocs_material-9.5.18-py3-none-any.whl", hash = "sha256:1e0e27fc9fe239f9064318acf548771a4629d5fd5dfd45444fd80a953fe21eb4"}, + {file = "mkdocs_material-9.5.18.tar.gz", hash = "sha256:a43f470947053fa2405c33995f282d24992c752a50114f23f30da9d8d0c57e62"}, ] [package.dependencies] @@ -1303,61 +1315,62 @@ files = [ [[package]] name = "orjson" -version = "3.9.15" +version = "3.10.0" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.9.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d61f7ce4727a9fa7680cd6f3986b0e2c732639f46a5e0156e550e35258aa313a"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4feeb41882e8aa17634b589533baafdceb387e01e117b1ec65534ec724023d04"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fbbeb3c9b2edb5fd044b2a070f127a0ac456ffd079cb82746fc84af01ef021a4"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b66bcc5670e8a6b78f0313bcb74774c8291f6f8aeef10fe70e910b8040f3ab75"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2973474811db7b35c30248d1129c64fd2bdf40d57d84beed2a9a379a6f57d0ab"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fe41b6f72f52d3da4db524c8653e46243c8c92df826ab5ffaece2dba9cccd58"}, - {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4228aace81781cc9d05a3ec3a6d2673a1ad0d8725b4e915f1089803e9efd2b99"}, - {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f7b65bfaf69493c73423ce9db66cfe9138b2f9ef62897486417a8fcb0a92bfe"}, - {file = "orjson-3.9.15-cp310-none-win32.whl", hash = "sha256:2d99e3c4c13a7b0fb3792cc04c2829c9db07838fb6973e578b85c1745e7d0ce7"}, - {file = "orjson-3.9.15-cp310-none-win_amd64.whl", hash = "sha256:b725da33e6e58e4a5d27958568484aa766e825e93aa20c26c91168be58e08cbb"}, - {file = "orjson-3.9.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c8e8fe01e435005d4421f183038fc70ca85d2c1e490f51fb972db92af6e047c2"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87f1097acb569dde17f246faa268759a71a2cb8c96dd392cd25c668b104cad2f"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff0f9913d82e1d1fadbd976424c316fbc4d9c525c81d047bbdd16bd27dd98cfc"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8055ec598605b0077e29652ccfe9372247474375e0e3f5775c91d9434e12d6b1"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6768a327ea1ba44c9114dba5fdda4a214bdb70129065cd0807eb5f010bfcbb5"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12365576039b1a5a47df01aadb353b68223da413e2e7f98c02403061aad34bde"}, - {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:71c6b009d431b3839d7c14c3af86788b3cfac41e969e3e1c22f8a6ea13139404"}, - {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e18668f1bd39e69b7fed19fa7cd1cd110a121ec25439328b5c89934e6d30d357"}, - {file = "orjson-3.9.15-cp311-none-win32.whl", hash = "sha256:62482873e0289cf7313461009bf62ac8b2e54bc6f00c6fabcde785709231a5d7"}, - {file = "orjson-3.9.15-cp311-none-win_amd64.whl", hash = "sha256:b3d336ed75d17c7b1af233a6561cf421dee41d9204aa3cfcc6c9c65cd5bb69a8"}, - {file = "orjson-3.9.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:82425dd5c7bd3adfe4e94c78e27e2fa02971750c2b7ffba648b0f5d5cc016a73"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c51378d4a8255b2e7c1e5cc430644f0939539deddfa77f6fac7b56a9784160a"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae4e06be04dc00618247c4ae3f7c3e561d5bc19ab6941427f6d3722a0875ef7"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcef128f970bb63ecf9a65f7beafd9b55e3aaf0efc271a4154050fc15cdb386e"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b72758f3ffc36ca566ba98a8e7f4f373b6c17c646ff8ad9b21ad10c29186f00d"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c57bc7b946cf2efa67ac55766e41764b66d40cbd9489041e637c1304400494"}, - {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:946c3a1ef25338e78107fba746f299f926db408d34553b4754e90a7de1d44068"}, - {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2f256d03957075fcb5923410058982aea85455d035607486ccb847f095442bda"}, - {file = "orjson-3.9.15-cp312-none-win_amd64.whl", hash = "sha256:5bb399e1b49db120653a31463b4a7b27cf2fbfe60469546baf681d1b39f4edf2"}, - {file = "orjson-3.9.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b17f0f14a9c0ba55ff6279a922d1932e24b13fc218a3e968ecdbf791b3682b25"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f6cbd8e6e446fb7e4ed5bac4661a29e43f38aeecbf60c4b900b825a353276a1"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76bc6356d07c1d9f4b782813094d0caf1703b729d876ab6a676f3aaa9a47e37c"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdfa97090e2d6f73dced247a2f2d8004ac6449df6568f30e7fa1a045767c69a6"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7413070a3e927e4207d00bd65f42d1b780fb0d32d7b1d951f6dc6ade318e1b5a"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cf1596680ac1f01839dba32d496136bdd5d8ffb858c280fa82bbfeb173bdd40"}, - {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:809d653c155e2cc4fd39ad69c08fdff7f4016c355ae4b88905219d3579e31eb7"}, - {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:920fa5a0c5175ab14b9c78f6f820b75804fb4984423ee4c4f1e6d748f8b22bc1"}, - {file = "orjson-3.9.15-cp38-none-win32.whl", hash = "sha256:2b5c0f532905e60cf22a511120e3719b85d9c25d0e1c2a8abb20c4dede3b05a5"}, - {file = "orjson-3.9.15-cp38-none-win_amd64.whl", hash = "sha256:67384f588f7f8daf040114337d34a5188346e3fae6c38b6a19a2fe8c663a2f9b"}, - {file = "orjson-3.9.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6fc2fe4647927070df3d93f561d7e588a38865ea0040027662e3e541d592811e"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34cbcd216e7af5270f2ffa63a963346845eb71e174ea530867b7443892d77180"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f541587f5c558abd93cb0de491ce99a9ef8d1ae29dd6ab4dbb5a13281ae04cbd"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92255879280ef9c3c0bcb327c5a1b8ed694c290d61a6a532458264f887f052cb"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05a1f57fb601c426635fcae9ddbe90dfc1ed42245eb4c75e4960440cac667262"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede0bde16cc6e9b96633df1631fbcd66491d1063667f260a4f2386a098393790"}, - {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e88b97ef13910e5f87bcbc4dd7979a7de9ba8702b54d3204ac587e83639c0c2b"}, - {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57d5d8cf9c27f7ef6bc56a5925c7fbc76b61288ab674eb352c26ac780caa5b10"}, - {file = "orjson-3.9.15-cp39-none-win32.whl", hash = "sha256:001f4eb0ecd8e9ebd295722d0cbedf0748680fb9998d3993abaed2f40587257a"}, - {file = "orjson-3.9.15-cp39-none-win_amd64.whl", hash = "sha256:ea0b183a5fe6b2b45f3b854b0d19c4e932d6f5934ae1f723b07cf9560edd4ec7"}, - {file = "orjson-3.9.15.tar.gz", hash = "sha256:95cae920959d772f30ab36d3b25f83bb0f3be671e986c72ce22f8fa700dae061"}, + {file = "orjson-3.10.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47af5d4b850a2d1328660661f0881b67fdbe712aea905dadd413bdea6f792c33"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c90681333619d78360d13840c7235fdaf01b2b129cb3a4f1647783b1971542b6"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:400c5b7c4222cb27b5059adf1fb12302eebcabf1978f33d0824aa5277ca899bd"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5dcb32e949eae80fb335e63b90e5808b4b0f64e31476b3777707416b41682db5"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7d507c7493252c0a0264b5cc7e20fa2f8622b8a83b04d819b5ce32c97cf57b"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e286a51def6626f1e0cc134ba2067dcf14f7f4b9550f6dd4535fd9d79000040b"}, + {file = "orjson-3.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8acd4b82a5f3a3ec8b1dc83452941d22b4711964c34727eb1e65449eead353ca"}, + {file = "orjson-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:30707e646080dd3c791f22ce7e4a2fc2438765408547c10510f1f690bd336217"}, + {file = "orjson-3.10.0-cp310-none-win32.whl", hash = "sha256:115498c4ad34188dcb73464e8dc80e490a3e5e88a925907b6fedcf20e545001a"}, + {file = "orjson-3.10.0-cp310-none-win_amd64.whl", hash = "sha256:6735dd4a5a7b6df00a87d1d7a02b84b54d215fb7adac50dd24da5997ffb4798d"}, + {file = "orjson-3.10.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9587053e0cefc284e4d1cd113c34468b7d3f17666d22b185ea654f0775316a26"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bef1050b1bdc9ea6c0d08468e3e61c9386723633b397e50b82fda37b3563d72"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d16c6963ddf3b28c0d461641517cd312ad6b3cf303d8b87d5ef3fa59d6844337"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4251964db47ef090c462a2d909f16c7c7d5fe68e341dabce6702879ec26d1134"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73bbbdc43d520204d9ef0817ac03fa49c103c7f9ea94f410d2950755be2c349c"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:414e5293b82373606acf0d66313aecb52d9c8c2404b1900683eb32c3d042dbd7"}, + {file = "orjson-3.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:feaed5bb09877dc27ed0d37f037ddef6cb76d19aa34b108db270d27d3d2ef747"}, + {file = "orjson-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5127478260db640323cea131ee88541cb1a9fbce051f0b22fa2f0892f44da302"}, + {file = "orjson-3.10.0-cp311-none-win32.whl", hash = "sha256:b98345529bafe3c06c09996b303fc0a21961820d634409b8639bc16bd4f21b63"}, + {file = "orjson-3.10.0-cp311-none-win_amd64.whl", hash = "sha256:658ca5cee3379dd3d37dbacd43d42c1b4feee99a29d847ef27a1cb18abdfb23f"}, + {file = "orjson-3.10.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4329c1d24fd130ee377e32a72dc54a3c251e6706fccd9a2ecb91b3606fddd998"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef0f19fdfb6553342b1882f438afd53c7cb7aea57894c4490c43e4431739c700"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4f60db24161534764277f798ef53b9d3063092f6d23f8f962b4a97edfa997a0"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1de3fd5c7b208d836f8ecb4526995f0d5877153a4f6f12f3e9bf11e49357de98"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f93e33f67729d460a177ba285002035d3f11425ed3cebac5f6ded4ef36b28344"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:237ba922aef472761acd697eef77fef4831ab769a42e83c04ac91e9f9e08fa0e"}, + {file = "orjson-3.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98c1bfc6a9bec52bc8f0ab9b86cc0874b0299fccef3562b793c1576cf3abb570"}, + {file = "orjson-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30d795a24be16c03dca0c35ca8f9c8eaaa51e3342f2c162d327bd0225118794a"}, + {file = "orjson-3.10.0-cp312-none-win32.whl", hash = "sha256:6a3f53dc650bc860eb26ec293dfb489b2f6ae1cbfc409a127b01229980e372f7"}, + {file = "orjson-3.10.0-cp312-none-win_amd64.whl", hash = "sha256:983db1f87c371dc6ffc52931eb75f9fe17dc621273e43ce67bee407d3e5476e9"}, + {file = "orjson-3.10.0-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9a667769a96a72ca67237224a36faf57db0c82ab07d09c3aafc6f956196cfa1b"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade1e21dfde1d37feee8cf6464c20a2f41fa46c8bcd5251e761903e46102dc6b"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23c12bb4ced1c3308eff7ba5c63ef8f0edb3e4c43c026440247dd6c1c61cea4b"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2d014cf8d4dc9f03fc9f870de191a49a03b1bcda51f2a957943fb9fafe55aac"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eadecaa16d9783affca33597781328e4981b048615c2ddc31c47a51b833d6319"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd583341218826f48bd7c6ebf3310b4126216920853cbc471e8dbeaf07b0b80e"}, + {file = "orjson-3.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:90bfc137c75c31d32308fd61951d424424426ddc39a40e367704661a9ee97095"}, + {file = "orjson-3.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13b5d3c795b09a466ec9fcf0bd3ad7b85467d91a60113885df7b8d639a9d374b"}, + {file = "orjson-3.10.0-cp38-none-win32.whl", hash = "sha256:5d42768db6f2ce0162544845facb7c081e9364a5eb6d2ef06cd17f6050b048d8"}, + {file = "orjson-3.10.0-cp38-none-win_amd64.whl", hash = "sha256:33e6655a2542195d6fd9f850b428926559dee382f7a862dae92ca97fea03a5ad"}, + {file = "orjson-3.10.0-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4050920e831a49d8782a1720d3ca2f1c49b150953667eed6e5d63a62e80f46a2"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1897aa25a944cec774ce4a0e1c8e98fb50523e97366c637b7d0cddabc42e6643"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9bf565a69e0082ea348c5657401acec3cbbb31564d89afebaee884614fba36b4"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6ebc17cfbbf741f5c1a888d1854354536f63d84bee537c9a7c0335791bb9009"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2817877d0b69f78f146ab305c5975d0618df41acf8811249ee64231f5953fee"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57d017863ec8aa4589be30a328dacd13c2dc49de1c170bc8d8c8a98ece0f2925"}, + {file = "orjson-3.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:22c2f7e377ac757bd3476ecb7480c8ed79d98ef89648f0176deb1da5cd014eb7"}, + {file = "orjson-3.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e62ba42bfe64c60c1bc84799944f80704e996592c6b9e14789c8e2a303279912"}, + {file = "orjson-3.10.0-cp39-none-win32.whl", hash = "sha256:60c0b1bdbccd959ebd1575bd0147bd5e10fc76f26216188be4a36b691c937077"}, + {file = "orjson-3.10.0-cp39-none-win_amd64.whl", hash = "sha256:175a41500ebb2fdf320bf78e8b9a75a1279525b62ba400b2b2444e274c2c8bee"}, + {file = "orjson-3.10.0.tar.gz", hash = "sha256:ba4d8cac5f2e2cff36bea6b6481cdb92b38c202bcec603d6f5ff91960595a1ed"}, ] [[package]] @@ -1537,15 +1550,125 @@ requests = ">=2.21.0" [[package]] name = "pycparser" -version = "2.21" +version = "2.22" description = "C parser in Python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] +[[package]] +name = "pydantic" +version = "2.7.1" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, + {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.18.2" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.18.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, + {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, + {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, + {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, + {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, + {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, + {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, + {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, + {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, + {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, + {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, + {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, + {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, + {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + [[package]] name = "pygments" version = "2.17.2" @@ -1587,17 +1710,17 @@ testutils = ["gitpython (>3)"] [[package]] name = "pymdown-extensions" -version = "10.7.1" +version = "10.8.1" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "pymdown_extensions-10.7.1-py3-none-any.whl", hash = "sha256:f5cc7000d7ff0d1ce9395d216017fa4df3dde800afb1fb72d1c7d3fd35e710f4"}, - {file = "pymdown_extensions-10.7.1.tar.gz", hash = "sha256:c70e146bdd83c744ffc766b4671999796aba18842b268510a329f7f64700d584"}, + {file = "pymdown_extensions-10.8.1-py3-none-any.whl", hash = "sha256:f938326115884f48c6059c67377c46cf631c733ef3629b6eed1349989d1b30cb"}, + {file = "pymdown_extensions-10.8.1.tar.gz", hash = "sha256:3ab1db5c9e21728dabf75192d71471f8e50f216627e9a1fa9535ecb0231b9940"}, ] [package.dependencies] -markdown = ">=3.5" +markdown = ">=3.6" pyyaml = "*" [package.extras] @@ -1704,101 +1827,101 @@ pyyaml = "*" [[package]] name = "rapidfuzz" -version = "3.6.2" +version = "3.7.0" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.8" files = [ - {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a5637e6bf11b15b5aff6ee818c76bdec99ad208511b78985e6209ba648a6e3ee"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:380586664f2f63807050ddb95e7702888b4f0b425abf17655940c411f39287ad"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3168ff565d4b8c239cf11fb604dd2507d30e9bcaac76a4077c0ac23cf2c866ed"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be69f7fd46b5c6467fe5e2fd4cff3816b0c03048eed8a4becb9a73e6000960e7"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cbd5894f23fdf5697499cf759523639838ac822bd1600e343fdce7313baa02ae"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85a5b6e026393fe39fb61146b9c17c5af66fffbe1410e992c4bb06d9ec327bd3"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab269adfc64480f209e99f253391a10735edd5c09046e04899adab5fb132f20e"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35aeac852bca06023d6bbd50c1fc504ca5a9a3613d5e75a140f0be7601fa34ef"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e706f302c6a3ae0d74edd0d6ace46aee1ae07c563b436ccf5ff04db2b3571e60"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bec353f022011e6e5cd28ccb8700fbd2a33918197af0d4e0abb3c3f4845cc864"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ef3925daaa93eed20401012e219f569ff0c039ed5bf4ce2d3737b4f75d441622"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6ee98d88ae9ccc77ff61992ed33b2496478def5dc0da55c9a9aa06fcb725a352"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:423c7c588b09d618601097b7a0017dfcb91132a2076bef29023c5f3cd2dc3de1"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-win32.whl", hash = "sha256:c17c5efee347a40a6f4c1eec59e3d7d1e22f7613a97f8b8a07733ef723483a04"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:4209816626d8d6ff8ae7dc248061c6059e618b70c6e6f6e4d7444ae3740b2b85"}, - {file = "rapidfuzz-3.6.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c54d3c85e522d3ac9ee39415f183c8fa184c4f87e7e5a37938f15a6d50e853a"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e06f6d270112f5db001f1cba5a97e1a48aee3d3dbdcbea3ec027c230462dbf9b"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:080cb71b50cb6aff11d1c6aeb157f273e2da0b2bdb3f9d7b01257e49e69a8576"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7895e04a22d6515bc91a850e0831f2405547605aa311d1ffec51e4818abc3c1"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd82f9838519136b7083dd1e3149ee80344521f3dc37f744f227505ff0883efb"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a945567c2b0b6e069454c9782d5234b0b6795718adf7a9f868bd3144afa6a023"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:673ba2c343644805acdae1cb949c6a4de71aa2f62a998978551ebea59603af3f"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d457c89bac1471442002e70551e8268e639b3870b4a4521eae363c07253be87"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:495c0d8e14e6f12520eb7fc71b9ba9fcaafb47fc23a654e6e89b6c7985ec0020"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6d67b649bf3e1b1722d04eca44d37919aef88305ce7ad05564502d013cf550fd"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e48dde8ca83d11daa00900cf6a5d281a1297aef9b7bfa73801af6e8822be5019"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:824cc381cf81cbf8d158f6935664ec2a69e6ac3b1d39fa201988bf81a257f775"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1dfe4c24957474ce0ac75d886387e30e292b4be39228a6d71f76de414dc187db"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d57b98013b802621bbc8b12a46bfc9d36ac552ab51ca207f7ce167ad46adabeb"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-win32.whl", hash = "sha256:9a07dffac439223b4f1025dbfc68f4445a3460a859309c9858c2a3fa29617cdc"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:95a49c6b8bf1229743ae585dd5b7d57f0d15a7eb6e826866d5c9965ba958503c"}, - {file = "rapidfuzz-3.6.2-cp311-cp311-win_arm64.whl", hash = "sha256:af7c19ec86e11488539380d3db1755be5d561a3c0e7b04ff9d07abd7f9a8e9d8"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:de8adc12161bf282c60f12dc9233bb31632f71d446a010fe7469a69b8153427f"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:337e357f693130c4c6be740652542b260e36f622c59e01fa33d58f1d2750c930"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6468f8bc8c3c50604f43631550ef9cfec873515dba5023ca34d461be94669fc8"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74c6773b11445b5e5cf93ca383171cd0ac0cdeafea11a7b2a5688f8bf8d813e6"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1507fc5769aa109dda4de3a15f822a0f6a03e18d627bd0ba3ddbb253cf70e07"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:617949a70150e6fffdaed19253dd49f7a53528411dc8bf7663d499ba21e0f61e"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8b77779174b1b40aa70827692571ab457061897846255ad7d5d559e2edb1932"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80e51b22a7da83f9c87a97e92df07ed0612c74c35496590255f4b5d5b4212dfe"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3ae7c86914cb6673e97e187ba431b9c4cf4177d9ae77f8a1e5b2ba9a5628839e"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ddc380ffaa90f204cc9ddcb779114b9ab6f015246d549de9d47871a97ef9f18a"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3c1dc078ef371fce09f9f3eec2ca4eaa2a8cd412ec53941015b4f39f14d34407"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:9a74102fc5a2534fe91f7507838623e1f3a149d8e05648389c42bb42e14b1c3f"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:48e1eaea8fcd522fca7f04f0480663f0f0cfb77957092cce60a93f4462864996"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-win32.whl", hash = "sha256:66b008bf2972740cd2dda5d382eb8bdb87265cd88198e71c7797bdc0d1f79d20"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:87ac3a87f2251ae2e95fc9478ca5c759de6d141d04c84d3fec9f9cdcfc167b33"}, - {file = "rapidfuzz-3.6.2-cp312-cp312-win_arm64.whl", hash = "sha256:b593cc51aed887e93b78c2f94dfae9008be2b23d17afd3b1f1d3eb3913b58f26"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7d830bc7a9b586a374147ec60b08b1f9ae5996b43f75cc514f37faef3866b519"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dbee7f5ff11872b76505cbd87c814abc823e8757f11c69062eb3b25130a283da"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c011fb31f2c3f82f503aedd6097d3d3854e574e327a119a3b7eb2cf90b79ca"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cda81d0e0ce0c13abfa46b24e10c1e85f9c6acb628f0a9a948f5779f9c2076a2"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c279928651ce0e9e5220dcb25a00cc53b65e592a0861336a38299bcdca3a596"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35bd4bc9c40e6994c5d6edea4b9319388b4d9711c13c66d543bb4c37624b4184"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d07899506a5a8760448d9df036d528b55a554bf571714173635c79eef4a86e58"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb2e51d01b9c6d6954a3e055c57a80d4685b4fc82719db5519fc153566bcd6bb"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:153d065e353371cc0aeff32b99999a5758266a64e958d1364189367c1c9f6814"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4edcceebb85ebfa49a3ddcde20ad891d36c08dc0fd592efdab0e7d313a4e36af"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3549123fca5bb817341025f98e8e49ca99f84596c7c4f92b658f8e5836040d4a"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:84c1032ae42628465b7a5cc35249906061e18a8193c9c27cbd2db54e9823a9a6"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9bcc91ebd8fc69a6bd3b5711c8250f5f4e70606b4da75ef415f57ad209978205"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-win32.whl", hash = "sha256:f3a70f341c4c111bad910d2df69c78577a98af140319a996af24c9385939335d"}, - {file = "rapidfuzz-3.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:354ad5fe655beb7b279390cb58334903931c5452ecbad1b1666ffb06786498e2"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1b86b93d93020c2b3edc1665d75c8855784845fc0a739b312c26c3a4bf0c80d5"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28243086ed0e50808bb56632e5442c457241646aeafafd501ac87901f40a3237"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed52461ae5a9ea4c400d38e2649c74a413f1a6d8fb8308b66f1fbd122514732f"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a46220f86a5f9cb016af31525e0d0865cad437d02239aa0d8aed2ab8bff1f1c"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81a630ed2fc3ec5fc7400eb66bab1f87e282b4d47f0abe3e48c6634dfa13b5e4"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8e5a437b9089df6242a718d9c31ab1742989e9400a0977af012ef483b63b4c2"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16270b5529de83b7bae7457e952e4d9cf3fbf029a837dd32d415bb9e0eb8e599"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5378c04102c7f084cde30a100154fa6d7e2baf0d51a6bdd2f912545559c1fb35"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7f18397c8d6a65fc0b288d2fc29bc7baeea6ba91eeb95163a3cd98f23cd3bc85"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2acd2514defce81e6ff4bbff50252d5e7df8e85a731442c4b83e44c86cf1c916"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:1df2faf80201952e252413b6fac6f3e146080dcebb87bb1bb722508e67558ed8"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6440ed0b3007c1c9286b0b88fe2ab2d9e83edd60cd62293b3dfabb732b4e8a30"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4fcfa23b5553b27f4016df77c53172ea743454cf12c28cfa7c35a309a2be93b3"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-win32.whl", hash = "sha256:2d580d937146e803c8e5e1b87916cab8d6f84013b6392713e201efcda335c7d8"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:fe2a68be734e8e88af23385c68d6467e15818b6b1df1cbfebf7bff577226c957"}, - {file = "rapidfuzz-3.6.2-cp39-cp39-win_arm64.whl", hash = "sha256:6478f7803efebf5f644d0b758439c5b25728550fdfbb19783d150004c46a75a9"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:36ce7b68a7b90b787cdd73480a68d2f1ca63c31a3a9d5a79a8736f978e1e9344"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53597fd72a9340bcdd80d3620f4957c2b92f9b569313b969a3abdaffd193aae6"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4f6de745fe6ce46a422d353ee10599013631d7d714a36d025f164b2d4e8c000"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62df2136068e2515ed8beb01756381ff62c29384d785e3bf46e3111d4ea3ba1e"}, - {file = "rapidfuzz-3.6.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7382c90170f60c846c81a07ddd80bb2e8c43c8383754486fa37f67391a571897"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f31314fd2e2f3dc3e519e6f93669462ce7953df2def1c344aa8f5345976d0eb2"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012221629d54d3bee954148247f711eb86d4d390b589ebfe03172ea0b37a7531"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d41dd59a70decfce6595315367a2fea2af660d92a9d144acc6479030501014d7"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f9fa14136a5b0cba1ec42531f7c3e0b0d3edb7fd6bc5e5ae7b498541f3855ab"}, - {file = "rapidfuzz-3.6.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:259364199cbfeca33b1af369fc7951f71717aa285184a3fa5a7b1772da1b89db"}, - {file = "rapidfuzz-3.6.2.tar.gz", hash = "sha256:cf911e792ab0c431694c9bf2648afabfd92099103f2e31492893e078ddca5e1a"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:860f438238f1807532aa5c5c25e74c284232ccc115fe84697b78e25d48f364f7"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bb9285abeb0477cdb2f8ea0cf7fd4b5f72ed5a9a7d3f0c0bb4a5239db2fc1ed"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:08671280e0c04d2bb3f39511f13cae5914e6690036fd1eefc3d47a47f9fae634"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04bae4d9c16ce1bab6447d196fb8258d98139ed8f9b288a38b84887985e4227b"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1efa2268b51b68156fb84d18ca1720311698a58051c4a19c40d670057ce60519"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:600b4d4315f33ec0356c0dab3991a5d5761102420bcff29e0773706aa48936e8"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18bc2f13c73d5d34499ff6ada55b052c445d3aa64d22c2639e5ab45472568046"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e11c5e6593be41a555475c9c20320342c1f5585d635a064924956944c465ad4"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d7878025248b99ccca3285891899373f98548f2ca13835d83619ffc42241c626"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b4a7e37fe136022d944374fcd8a2f72b8a19f7b648d2cdfb946667e9ede97f9f"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b5881856f830351aaabd869151124f64a80bf61560546d9588a630a4e933a5de"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:c788b11565cc176fab8fab6dfcd469031e906927db94bf7e422afd8ef8f88a5a"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9e17a3092e74025d896ef1d67ac236c83494da37a78ef84c712e4e2273c115f1"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-win32.whl", hash = "sha256:e499c823206c9ffd9d89aa11f813a4babdb9219417d4efe4c8a6f8272da00e98"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:91f798cc00cd94a0def43e9befc6e867c9bd8fa8f882d1eaa40042f528b7e2c7"}, + {file = "rapidfuzz-3.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:d5a3872f35bec89f07b993fa1c5401d11b9e68bcdc1b9737494e279308a38a5f"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ef6b6ab64c4c91c57a6b58e1d690b59453bfa1f1e9757a7e52e59b4079e36631"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f9070b42c0ba030b045bba16a35bdb498a0d6acb0bdb3ff4e325960e685e290"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:63044c63565f50818d885bfcd40ac369947da4197de56b4d6c26408989d48edf"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b0c47860c733a3d73a4b70b97b35c8cbf24ef24f8743732f0d1c412a8c85de"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1b14489b038f007f425a06fcf28ac6313c02cb603b54e3a28d9cfae82198cc0"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be08f39e397a618aab907887465d7fabc2d1a4d15d1a67cb8b526a7fb5202a3e"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16895dc62a7b92028f9c8b6d22830f1cbc77306ee794f461afc6028e1a8d7539"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:579cce49dfa57ffd8c8227b3fb53cced54b4df70cec502e63e9799b4d1f44004"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:40998c8dc35fdd221790b8b5134a8d7499adbfab9a5dd9ec626c7e92e17a43ed"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:dc3fdb4738a6b83ae27f1d8923b00d3a9c2b5c50da75b9f8b81841839c6e3e1f"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:92b8146fbfb37ac358ef7e0f6b79619e4f793fbbe894b99ea87920f9c0a9d77d"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1dfceaa7c2914585bb8a043265c39ec09078f13fbf53b5525722fc074306b6fa"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f332d61f51b0b9c8b55a0fb052b4764b6ad599ea8ce948ac47a4388e9083c35e"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-win32.whl", hash = "sha256:dfd1e4819f1f3c47141f86159b44b7360ecb19bf675080b3b40437bf97273ab9"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:594b9c33fc1a86784962043ee3fbaaed875fbaadff72e467c2f7a83cd6c5d69d"}, + {file = "rapidfuzz-3.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b13a6823a1b83ae43f8bf35955df35032bee7bec0daf9b5ab836e0286067434"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:075a419a0ec29be44b3d7f4bcfa5cb7e91e419379a85fc05eb33de68315bd96f"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:51a5b96d2081c3afbef1842a61d63e55d0a5a201473e6975a80190ff2d6f22ca"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9460d8fddac7ea46dff9298eee9aa950dbfe79f2eb509a9f18fbaefcd10894c"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f39eb1513ee139ba6b5c01fe47ddf2d87e9560dd7fdee1068f7f6efbae70de34"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eace9fdde58a425d4c9a93021b24a0cac830df167a5b2fc73299e2acf9f41493"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cc77237242303733de47829028a0a8b6ab9188b23ec9d9ff0a674fdcd3c8e7f"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74e692357dd324dff691d379ef2c094c9ec526c0ce83ed43a066e4e68fe70bf6"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f2075ac9ee5c15d33d24a1efc8368d095602b5fd9634c5b5f24d83e41903528"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5a8ba64d72329a940ff6c74b721268c2004eecc48558f648a38e96915b5d1c1b"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a1f268a2a37cd22573b4a06eccd481c04504b246d3cadc2d8e8dfa64b575636d"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:42c2e8a2341363c7caf276efdbe1a673fc5267a02568c47c8e980f12e9bc8727"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a9acca34b34fb895ee6a84c436bb919f3b9cd8f43e7003d43e9573a1d990ff74"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9bad6a0fe3bc1753dacaa6229a8ba7d9844eb7ae24d44d17c5f4c51c91a8a95e"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-win32.whl", hash = "sha256:c86bc4b1d2380739e6485396195e30021df509b4923f3f757914e171587bce7c"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:d7361608c8e73a1dc0203a87d151cddebdade0098a047c46da43c469c07df964"}, + {file = "rapidfuzz-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:8fdc26e7863e0f63c2185d53bb61f5173ad4451c1c8287b535b30ea25a419a5a"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9b6167468f76779a14b9af66210f68741af94d32d086f19118de4e919f00585c"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bd394e28ff221557ea4d8152fcec3e66d9f620557feca5f2bedc4c21f8cf2f9"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8e70f876ca89a6df344f8157ac60384e8c05a0dfb442da2490c3f1c45238ccf5"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c837f89d86a5affe9ee6574dad6b195475676a6ab171a67920fc99966f2ab2c"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cda4550a98658f9a8bcdc03d0498ed1565c1563880e3564603a9eaae28d51b2a"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecd70212fd9f1f8b1d3bdd8bcb05acc143defebd41148bdab43e573b043bb241"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187db4cc8fb54f8c49c67b7f38ef3a122ce23be273032fa2ff34112a2694c3d8"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4604dfc1098920c4eb6d0c6b5cc7bdd4bf95b48633e790c1d3f100a25870691d"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01581b688c5f4f6665b779135e32db0edab1d78028abf914bb91469928efa383"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0828b55ec8ad084febdf4ab0c942eb1f81c97c0935f1cb0be0b4ea84ce755988"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:150c98b65faff17b917b9d36bff8a4d37b6173579c6bc2e38ff2044e209d37a4"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7e4eea225d2bff1aff4c85fcc44716596d3699374d99eb5906b7a7560297460e"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7bc944d7e830cfce0f8b4813875f05904207017b66e25ab7ee757507001310a9"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-win32.whl", hash = "sha256:3e55f02105c451ab6ff0edaaba57cab1b6c0a0241cfb2b306d4e8e1503adba50"}, + {file = "rapidfuzz-3.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:41851620d2900791d66d9b6092fc163441d7dd91a460c73b07957ff1c517bc30"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e8041c6b2d339766efe6298fa272f79d6dd799965df364ef4e50f488c101c899"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4e09d81008e212fc824ea23603ff5270d75886e72372fa6c7c41c1880bcb57ed"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:419c8961e861fb5fc5590056c66a279623d1ea27809baea17e00cdc313f1217a"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1522eaab91b9400b3ef16eebe445940a19e70035b5bc5d98aef23d66e9ac1df0"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:611278ce3136f4544d596af18ab8849827d64372e1d8888d9a8d071bf4a3f44d"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4efa9bfc5b955b6474ee077eee154e240441842fa304f280b06e6b6aa58a1d1e"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0cc9d3c8261457af3f8756b1f71a9fdc4892978a9e8b967976d2803e08bf972"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce728e2b582fd396bc2559160ee2e391e6a4b5d2e455624044699d96abe8a396"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a6a36c9299e059e0bee3409218bc5235a46570c20fc980cdee5ed21ea6110ad"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9ea720db8def684c1eb71dadad1f61c9b52f4d979263eb5d443f2b22b0d5430a"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:358692f1df3f8aebcd48e69c77c948c9283b44c0efbaf1eeea01739efe3cd9a6"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:faded69ffe79adcefa8da08f414a0fd52375e2b47f57be79471691dad9656b5a"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7f9f3dc14fadbd553975f824ac48c381f42192cec9d7e5711b528357662a8d8e"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-win32.whl", hash = "sha256:7be5f460ff42d7d27729115bfe8a02e83fa0284536d8630ee900d17b75c29e65"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:dd5ad2c12dab2b98340c4b7b9592c8f349730bda9a2e49675ea592bbcbc1360b"}, + {file = "rapidfuzz-3.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:aa163257a0ac4e70f9009d25e5030bdd83a8541dfa3ba78dc86b35c9e16a80b4"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4e50840a8a8e0229563eeaf22e21a203359859557db8829f4d0285c17126c5fb"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:632f09e19365ace5ff2670008adc8bf23d03d668b03a30230e5b60ff9317ee93"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:209dda6ae66b702f74a78cef555397cdc2a83d7f48771774a20d2fc30808b28c"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bc0b78572626af6ab134895e4dbfe4f4d615d18dcc43b8d902d8e45471aabba"}, + {file = "rapidfuzz-3.7.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ba14850cc8258b3764ea16b8a4409ac2ba16d229bde7a5f495dd479cd9ccd56"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b917764fd2b267addc9d03a96d26f751f6117a95f617428c44a069057653b528"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1252ca156e1b053e84e5ae1c8e9e062ee80468faf23aa5c543708212a42795fd"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86c7676a32d7524e40bc73546e511a408bc831ae5b163029d325ea3a2027d089"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20e7d729af2e5abb29caa070ec048aba042f134091923d9ca2ac662b5604577e"}, + {file = "rapidfuzz-3.7.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86eea3e6c314a9238de568254a9c591ec73c2985f125675ed5f171d869c47773"}, + {file = "rapidfuzz-3.7.0.tar.gz", hash = "sha256:620df112c39c6d27316dc1e22046dc0382d6d91fd60d7c51bd41ca0333d867e9"}, ] [package.extras] @@ -1820,13 +1943,13 @@ dev = ["black (==22.1.0)", "flake8 (==4.0.1)", "isort (==5.10.1)"] [[package]] name = "red-discordbot" -version = "3.5.7" +version = "3.5.9" description = "A highly customisable Discord bot" optional = false python-versions = "<3.12,>=3.8.1" files = [ - {file = "Red-DiscordBot-3.5.7.tar.gz", hash = "sha256:7673ea794016b6d3d7b96eb07a46cd92c52974e15b3a6ad95608df69dc320838"}, - {file = "Red_DiscordBot-3.5.7-py3-none-any.whl", hash = "sha256:3b9df02cd8efd35170c9505e3f0a8c9b1bd415bcc2ca451a19733d28b1b3a5b4"}, + {file = "Red_DiscordBot-3.5.9-py3-none-any.whl", hash = "sha256:d392c4947f95151435792e99cc74afb7c440b11fb516fa59927f5e462c61385f"}, + {file = "red_discordbot-3.5.9.tar.gz", hash = "sha256:1f50e508b6923868ea4d7bb0eb80a2b1dbdeae248cf0a57717539ca6f5c5874f"}, ] [package.dependencies] @@ -1848,14 +1971,14 @@ markdown = "3.6" markdown-it-py = "3.0.0" mdurl = "0.1.2" multidict = "6.0.5" -orjson = "3.9.15" +orjson = "3.10.0" packaging = "24.0" platformdirs = "4.2.0" psutil = "5.9.8" pygments = "2.17.2" python-dateutil = "2.9.0.post0" pyyaml = "6.0.1" -rapidfuzz = "3.6.2" +rapidfuzz = "3.7.0" red-commons = "1.0.0" red-lavalink = "0.11.0" rich = "13.7.1" @@ -1867,11 +1990,11 @@ yarl = "1.9.4" [package.extras] all = ["async-timeout (==4.0.3)", "asyncpg (==0.29.0)"] -dev = ["alabaster (==0.7.13)", "astroid (==3.1.0)", "async-timeout (==4.0.3)", "asyncpg (==0.29.0)", "black (==23.12.1)", "certifi (==2024.2.2)", "charset-normalizer (==3.3.2)", "dill (==0.3.8)", "docutils (==0.20.1)", "exceptiongroup (==1.2.0)", "imagesize (==1.4.1)", "importlib-metadata (==7.1.0)", "iniconfig (==2.0.0)", "isort (==5.13.2)", "jinja2 (==3.1.3)", "markupsafe (==2.1.5)", "mccabe (==0.7.0)", "mypy-extensions (==1.0.0)", "pathspec (==0.12.1)", "pluggy (==1.4.0)", "pylint (==3.1.0)", "pytest (==7.4.4)", "pytest-asyncio (==0.21.1)", "pytest-mock (==3.12.0)", "pytz (==2024.1)", "requests (==2.31.0)", "snowballstemmer (==2.2.0)", "sphinx (==7.1.2)", "sphinx-prompt (==1.7.0)", "sphinx-rtd-theme (==2.0.0)", "sphinxcontrib-applehelp (==1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (==2.0.1)", "sphinxcontrib-jquery (==4.1)", "sphinxcontrib-jsmath (==1.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)", "sphinxcontrib-trio (==1.1.2)", "tomli (==2.0.1)", "tomli (==2.0.1)", "tomlkit (==0.12.4)", "urllib3 (==2.2.1)", "zipp (==3.18.1)"] +dev = ["alabaster (==0.7.13)", "astroid (==3.1.0)", "async-timeout (==4.0.3)", "asyncpg (==0.29.0)", "black (==23.12.1)", "certifi (==2024.2.2)", "charset-normalizer (==3.3.2)", "dill (==0.3.8)", "docutils (==0.20.1)", "exceptiongroup (==1.2.0)", "imagesize (==1.4.1)", "importlib-metadata (==7.1.0)", "iniconfig (==2.0.0)", "isort (==5.13.2)", "jinja2 (==3.1.3)", "markupsafe (==2.1.5)", "mccabe (==0.7.0)", "mypy-extensions (==1.0.0)", "pathspec (==0.12.1)", "pluggy (==1.4.0)", "pylint (==3.1.0)", "pytest (==7.4.4)", "pytest-asyncio (==0.21.1)", "pytest-mock (==3.14.0)", "pytz (==2024.1)", "requests (==2.31.0)", "snowballstemmer (==2.2.0)", "sphinx (==7.1.2)", "sphinx-prompt (==1.7.0)", "sphinx-rtd-theme (==2.0.0)", "sphinxcontrib-applehelp (==1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (==2.0.1)", "sphinxcontrib-jquery (==4.1)", "sphinxcontrib-jsmath (==1.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)", "sphinxcontrib-trio (==1.1.2)", "tomli (==2.0.1)", "tomli (==2.0.1)", "tomlkit (==0.12.4)", "urllib3 (==2.2.1)", "zipp (==3.18.1)"] doc = ["alabaster (==0.7.13)", "certifi (==2024.2.2)", "charset-normalizer (==3.3.2)", "docutils (==0.20.1)", "imagesize (==1.4.1)", "importlib-metadata (==7.1.0)", "jinja2 (==3.1.3)", "markupsafe (==2.1.5)", "pytz (==2024.1)", "requests (==2.31.0)", "snowballstemmer (==2.2.0)", "sphinx (==7.1.2)", "sphinx-prompt (==1.7.0)", "sphinx-rtd-theme (==2.0.0)", "sphinxcontrib-applehelp (==1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (==2.0.1)", "sphinxcontrib-jquery (==4.1)", "sphinxcontrib-jsmath (==1.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)", "sphinxcontrib-trio (==1.1.2)", "urllib3 (==2.2.1)", "zipp (==3.18.1)"] postgres = ["async-timeout (==4.0.3)", "asyncpg (==0.29.0)"] style = ["black (==23.12.1)", "mypy-extensions (==1.0.0)", "pathspec (==0.12.1)", "tomli (==2.0.1)"] -test = ["astroid (==3.1.0)", "dill (==0.3.8)", "exceptiongroup (==1.2.0)", "iniconfig (==2.0.0)", "isort (==5.13.2)", "mccabe (==0.7.0)", "pluggy (==1.4.0)", "pylint (==3.1.0)", "pytest (==7.4.4)", "pytest-asyncio (==0.21.1)", "pytest-mock (==3.12.0)", "tomli (==2.0.1)", "tomlkit (==0.12.4)"] +test = ["astroid (==3.1.0)", "dill (==0.3.8)", "exceptiongroup (==1.2.0)", "iniconfig (==2.0.0)", "isort (==5.13.2)", "mccabe (==0.7.0)", "pluggy (==1.4.0)", "pylint (==3.1.0)", "pytest (==7.4.4)", "pytest-asyncio (==0.21.1)", "pytest-mock (==3.14.0)", "tomli (==2.0.1)", "tomlkit (==0.12.4)"] [[package]] name = "red-lavalink" @@ -1895,104 +2018,90 @@ test = ["pytest (>=7)", "pytest-asyncio (>=0.19)"] [[package]] name = "regex" -version = "2023.12.25" +version = "2024.4.28" description = "Alternative regular expression module, to replace re." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, - {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, - {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, - {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, - {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, - {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, - {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, - {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, - {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, - {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, - {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, - {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, - {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, - {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, - {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, + {file = "regex-2024.4.28-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd196d056b40af073d95a2879678585f0b74ad35190fac04ca67954c582c6b61"}, + {file = "regex-2024.4.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8bb381f777351bd534462f63e1c6afb10a7caa9fa2a421ae22c26e796fe31b1f"}, + {file = "regex-2024.4.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:47af45b6153522733aa6e92543938e97a70ce0900649ba626cf5aad290b737b6"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d6a550425cc51c656331af0e2b1651e90eaaa23fb4acde577cf15068e2e20f"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf29304a8011feb58913c382902fde3395957a47645bf848eea695839aa101b7"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:92da587eee39a52c91aebea8b850e4e4f095fe5928d415cb7ed656b3460ae79a"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6277d426e2f31bdbacb377d17a7475e32b2d7d1f02faaecc48d8e370c6a3ff31"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28e1f28d07220c0f3da0e8fcd5a115bbb53f8b55cecf9bec0c946eb9a059a94c"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aaa179975a64790c1f2701ac562b5eeb733946eeb036b5bcca05c8d928a62f10"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6f435946b7bf7a1b438b4e6b149b947c837cb23c704e780c19ba3e6855dbbdd3"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:19d6c11bf35a6ad077eb23852827f91c804eeb71ecb85db4ee1386825b9dc4db"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:fdae0120cddc839eb8e3c15faa8ad541cc6d906d3eb24d82fb041cfe2807bc1e"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e672cf9caaf669053121f1766d659a8813bd547edef6e009205378faf45c67b8"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f57515750d07e14743db55d59759893fdb21d2668f39e549a7d6cad5d70f9fea"}, + {file = "regex-2024.4.28-cp310-cp310-win32.whl", hash = "sha256:a1409c4eccb6981c7baabc8888d3550df518add6e06fe74fa1d9312c1838652d"}, + {file = "regex-2024.4.28-cp310-cp310-win_amd64.whl", hash = "sha256:1f687a28640f763f23f8a9801fe9e1b37338bb1ca5d564ddd41619458f1f22d1"}, + {file = "regex-2024.4.28-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:84077821c85f222362b72fdc44f7a3a13587a013a45cf14534df1cbbdc9a6796"}, + {file = "regex-2024.4.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b45d4503de8f4f3dc02f1d28a9b039e5504a02cc18906cfe744c11def942e9eb"}, + {file = "regex-2024.4.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:457c2cd5a646dd4ed536c92b535d73548fb8e216ebee602aa9f48e068fc393f3"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b51739ddfd013c6f657b55a508de8b9ea78b56d22b236052c3a85a675102dc6"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:459226445c7d7454981c4c0ce0ad1a72e1e751c3e417f305722bbcee6697e06a"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:670fa596984b08a4a769491cbdf22350431970d0112e03d7e4eeaecaafcd0fec"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe00f4fe11c8a521b173e6324d862ee7ee3412bf7107570c9b564fe1119b56fb"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36f392dc7763fe7924575475736bddf9ab9f7a66b920932d0ea50c2ded2f5636"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:23a412b7b1a7063f81a742463f38821097b6a37ce1e5b89dd8e871d14dbfd86b"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f1d6e4b7b2ae3a6a9df53efbf199e4bfcff0959dbdb5fd9ced34d4407348e39a"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:499334ad139557de97cbc4347ee921c0e2b5e9c0f009859e74f3f77918339257"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:0940038bec2fe9e26b203d636c44d31dd8766abc1fe66262da6484bd82461ccf"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:66372c2a01782c5fe8e04bff4a2a0121a9897e19223d9eab30c54c50b2ebeb7f"}, + {file = "regex-2024.4.28-cp311-cp311-win32.whl", hash = "sha256:c77d10ec3c1cf328b2f501ca32583625987ea0f23a0c2a49b37a39ee5c4c4630"}, + {file = "regex-2024.4.28-cp311-cp311-win_amd64.whl", hash = "sha256:fc0916c4295c64d6890a46e02d4482bb5ccf33bf1a824c0eaa9e83b148291f90"}, + {file = "regex-2024.4.28-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:08a1749f04fee2811c7617fdd46d2e46d09106fa8f475c884b65c01326eb15c5"}, + {file = "regex-2024.4.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b8eb28995771c087a73338f695a08c9abfdf723d185e57b97f6175c5051ff1ae"}, + {file = "regex-2024.4.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd7ef715ccb8040954d44cfeff17e6b8e9f79c8019daae2fd30a8806ef5435c0"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb0315a2b26fde4005a7c401707c5352df274460f2f85b209cf6024271373013"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2fc053228a6bd3a17a9b0a3f15c3ab3cf95727b00557e92e1cfe094b88cc662"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fe9739a686dc44733d52d6e4f7b9c77b285e49edf8570754b322bca6b85b4cc"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74fcf77d979364f9b69fcf8200849ca29a374973dc193a7317698aa37d8b01c"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:965fd0cf4694d76f6564896b422724ec7b959ef927a7cb187fc6b3f4e4f59833"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2fef0b38c34ae675fcbb1b5db760d40c3fc3612cfa186e9e50df5782cac02bcd"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bc365ce25f6c7c5ed70e4bc674f9137f52b7dd6a125037f9132a7be52b8a252f"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:ac69b394764bb857429b031d29d9604842bc4cbfd964d764b1af1868eeebc4f0"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:144a1fc54765f5c5c36d6d4b073299832aa1ec6a746a6452c3ee7b46b3d3b11d"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2630ca4e152c221072fd4a56d4622b5ada876f668ecd24d5ab62544ae6793ed6"}, + {file = "regex-2024.4.28-cp312-cp312-win32.whl", hash = "sha256:7f3502f03b4da52bbe8ba962621daa846f38489cae5c4a7b5d738f15f6443d17"}, + {file = "regex-2024.4.28-cp312-cp312-win_amd64.whl", hash = "sha256:0dd3f69098511e71880fb00f5815db9ed0ef62c05775395968299cb400aeab82"}, + {file = "regex-2024.4.28-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:374f690e1dd0dbdcddea4a5c9bdd97632cf656c69113f7cd6a361f2a67221cb6"}, + {file = "regex-2024.4.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f87ae6b96374db20f180eab083aafe419b194e96e4f282c40191e71980c666"}, + {file = "regex-2024.4.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5dbc1bcc7413eebe5f18196e22804a3be1bfdfc7e2afd415e12c068624d48247"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f85151ec5a232335f1be022b09fbbe459042ea1951d8a48fef251223fc67eee1"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57ba112e5530530fd175ed550373eb263db4ca98b5f00694d73b18b9a02e7185"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:224803b74aab56aa7be313f92a8d9911dcade37e5f167db62a738d0c85fdac4b"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a54a047b607fd2d2d52a05e6ad294602f1e0dec2291152b745870afc47c1397"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a2a512d623f1f2d01d881513af9fc6a7c46e5cfffb7dc50c38ce959f9246c94"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c06bf3f38f0707592898428636cbb75d0a846651b053a1cf748763e3063a6925"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1031a5e7b048ee371ab3653aad3030ecfad6ee9ecdc85f0242c57751a05b0ac4"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d7a353ebfa7154c871a35caca7bfd8f9e18666829a1dc187115b80e35a29393e"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7e76b9cfbf5ced1aca15a0e5b6f229344d9b3123439ffce552b11faab0114a02"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5ce479ecc068bc2a74cb98dd8dba99e070d1b2f4a8371a7dfe631f85db70fe6e"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d77b6f63f806578c604dca209280e4c54f0fa9a8128bb8d2cc5fb6f99da4150"}, + {file = "regex-2024.4.28-cp38-cp38-win32.whl", hash = "sha256:d84308f097d7a513359757c69707ad339da799e53b7393819ec2ea36bc4beb58"}, + {file = "regex-2024.4.28-cp38-cp38-win_amd64.whl", hash = "sha256:2cc1b87bba1dd1a898e664a31012725e48af826bf3971e786c53e32e02adae6c"}, + {file = "regex-2024.4.28-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7413167c507a768eafb5424413c5b2f515c606be5bb4ef8c5dee43925aa5718b"}, + {file = "regex-2024.4.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:108e2dcf0b53a7c4ab8986842a8edcb8ab2e59919a74ff51c296772e8e74d0ae"}, + {file = "regex-2024.4.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f1c5742c31ba7d72f2dedf7968998730664b45e38827637e0f04a2ac7de2f5f1"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecc6148228c9ae25ce403eade13a0961de1cb016bdb35c6eafd8e7b87ad028b1"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7d893c8cf0e2429b823ef1a1d360a25950ed11f0e2a9df2b5198821832e1947"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4290035b169578ffbbfa50d904d26bec16a94526071ebec3dadbebf67a26b25e"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a22ae1cfd82e4ffa2066eb3390777dc79468f866f0625261a93e44cdf6482b"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd24fd140b69f0b0bcc9165c397e9b2e89ecbeda83303abf2a072609f60239e2"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:39fb166d2196413bead229cd64a2ffd6ec78ebab83fff7d2701103cf9f4dfd26"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9301cc6db4d83d2c0719f7fcda37229691745168bf6ae849bea2e85fc769175d"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c3d389e8d76a49923683123730c33e9553063d9041658f23897f0b396b2386f"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:99ef6289b62042500d581170d06e17f5353b111a15aa6b25b05b91c6886df8fc"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b91d529b47798c016d4b4c1d06cc826ac40d196da54f0de3c519f5a297c5076a"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:43548ad74ea50456e1c68d3c67fff3de64c6edb85bcd511d1136f9b5376fc9d1"}, + {file = "regex-2024.4.28-cp39-cp39-win32.whl", hash = "sha256:05d9b6578a22db7dedb4df81451f360395828b04f4513980b6bd7a1412c679cc"}, + {file = "regex-2024.4.28-cp39-cp39-win_amd64.whl", hash = "sha256:3986217ec830c2109875be740531feb8ddafe0dfa49767cdcd072ed7e8927962"}, + {file = "regex-2024.4.28.tar.gz", hash = "sha256:83ab366777ea45d58f72593adf35d36ca911ea8bd838483c1823b883a121b0e4"}, ] [[package]] @@ -2036,28 +2145,28 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.3.4" +version = "0.3.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4"}, - {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"}, - {file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"}, - {file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"}, - {file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"}, - {file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"}, + {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"}, + {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"}, + {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"}, + {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"}, ] [[package]] @@ -2098,13 +2207,13 @@ files = [ [[package]] name = "tinycss2" -version = "1.2.1" +version = "1.3.0" description = "A tiny CSS parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tinycss2-1.2.1-py3-none-any.whl", hash = "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847"}, - {file = "tinycss2-1.2.1.tar.gz", hash = "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"}, + {file = "tinycss2-1.3.0-py3-none-any.whl", hash = "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"}, + {file = "tinycss2-1.3.0.tar.gz", hash = "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d"}, ] [package.dependencies] @@ -2112,7 +2221,7 @@ webencodings = ">=0.4" [package.extras] doc = ["sphinx", "sphinx_rtd_theme"] -test = ["flake8", "isort", "pytest"] +test = ["pytest", "ruff"] [[package]] name = "tomlkit" @@ -2436,4 +2545,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "42f6d2a053929b5a71b673e30f222c3e5914c723d074b453afd349e11118590f" +content-hash = "67eb5e616951979332b6f32bcb39d85171cbf8377f566ea1862c51b5068b52f3" diff --git a/pyproject.toml b/pyproject.toml index 93bdd53..91c350e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,11 +9,12 @@ package-mode = false [tool.poetry.dependencies] python = ">=3.11,<3.12" -Red-DiscordBot = "^3.5.5" +Red-DiscordBot = "^3.5.9" py-dactyl = "^2.0.4" websockets = "^12.0" pillow = "^10.3.0" numpy = "^1.26.4" +pydantic = "^2.7.1" [tool.poetry.group.dev] optional = true -- 2.45.2 From b6d1510698a7ad3913c8f4c54f74219d5417ae1e Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 13:42:58 -0400 Subject: [PATCH 002/146] fix(aurora): fixed a broken import --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index ba69b60..694c9fd 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,9 +1,9 @@ from typing import Dict, List, Optional - from discord import Guild from pydantic import BaseModel -from utilities.database import connect + +from aurora.utilities.database import connect class Moderation(BaseModel): -- 2.45.2 From f3d6244a175ab5b22992b5100b3cc4416118f435 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 13:47:07 -0400 Subject: [PATCH 003/146] fix(aurora): optimizing the from_sql method --- aurora/models.py | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 694c9fd..dca0422 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -27,36 +27,17 @@ class Moderation(BaseModel): def __str__(self): return f"{self.moderation_type} {self.target_type} {self.target_id} {self.reason}" - async def from_sql(self, moderation_id: int, guild: Guild): - """""" - database = connect() - cursor = database.cursor() - + @classmethod + def from_sql(cls, moderation_id: int, guild: Guild): query = f"SELECT * FROM moderation_{guild.id} WHERE moderation_id = ?;" - cursor.execute(query, (moderation_id,)) - result = cursor.fetchone() - cursor.close() - database.close() + with connect() as database, database.cursor() as cursor: + cursor.execute(query, (moderation_id,)) + result = cursor.fetchone() - if result: - ( - self.moderation_id, - self.timestamp, - self.moderation_type, - self.target_type, - self.target_id, - self.moderator_id, - self.role_id, - self.duration, - self.end_timestamp, - self.reason, - self.resolved, - self.resolved_by, - self.resolve_reason, - self.expired, - self.changes, - self.metadata, - ) = result[0:16] + if result: + moderation_data = dict(zip(cls.model_fields, result)) + moderation = cls(**moderation_data) + return moderation - return self + return None -- 2.45.2 From 14a04cff59a8117e84920b07cdeaae888777e9ae Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 13:48:57 -0400 Subject: [PATCH 004/146] fix(aurora): fixed an error --- aurora/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index dca0422..90463d4 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -31,13 +31,15 @@ class Moderation(BaseModel): def from_sql(cls, moderation_id: int, guild: Guild): query = f"SELECT * FROM moderation_{guild.id} WHERE moderation_id = ?;" - with connect() as database, database.cursor() as cursor: + with connect() as database: + cursor = database.cursor() cursor.execute(query, (moderation_id,)) result = cursor.fetchone() if result: moderation_data = dict(zip(cls.model_fields, result)) moderation = cls(**moderation_data) + cursor.close() return moderation return None -- 2.45.2 From e5cdd3893fc15ce177ae56b267a8d16c2e21444f Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 14:08:53 -0400 Subject: [PATCH 005/146] fix(aurora): cleaned up the Moderation model --- aurora/models.py | 55 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 90463d4..2b8fcb8 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,35 +1,36 @@ +import json +from datetime import datetime, timedelta from typing import Dict, List, Optional -from discord import Guild from pydantic import BaseModel -from aurora.utilities.database import connect - class Moderation(BaseModel): moderation_id: int - timestamp: int + guild_id: int + timestamp: datetime moderation_type: str target_type: str target_id: int moderator_id: int + role_id: Optional[int] + duration: Optional[timedelta] + end_timestamp: Optional[datetime] + reason: Optional[str] resolved: bool + resolved_by: Optional[int] + resolve_reason: Optional[str] expired: bool - duration: str - end_timestamp: int - reason: str changes: List[Dict] metadata: Dict - resolved_by: Optional[int] = None - resolve_reason: Optional[str] = None - role_id: Optional[int] = None def __str__(self): return f"{self.moderation_type} {self.target_type} {self.target_id} {self.reason}" @classmethod - def from_sql(cls, moderation_id: int, guild: Guild): - query = f"SELECT * FROM moderation_{guild.id} WHERE moderation_id = ?;" + def from_sql(cls, moderation_id: int, guild_id: int): + from aurora.utilities.database import connect + query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;" with connect() as database: cursor = database.cursor() @@ -37,9 +38,33 @@ class Moderation(BaseModel): result = cursor.fetchone() if result: - moderation_data = dict(zip(cls.model_fields, result)) - moderation = cls(**moderation_data) + if result[7] != "NULL": + hours, minutes, seconds = map(int, result[7].split(':')) + duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) + else: + duration = None + case = { + "moderation_id": int(result[0]), + "guild_id": int(guild_id), + "timestamp": datetime.fromtimestamp(result[1]), + "moderation_type": str(result[2]), + "target_type": str(result[3]), + "target_id": int(result[4]), + "moderator_id": int(result[5]), + "role_id": int(result[6]) if result[6] != "0" else None, + "duration": duration, + "end_timestamp": datetime.fromtimestamp(result[8]) if result[8] != 0 else None, + "reason": result[9] if result[9] != "NULL" else None, + "resolved": bool(result[10]), + "resolved_by": result[11] if result[11] != "NULL" else None, + "resolve_reason": result[12] if result[12] != "NULL" else None, + "expired": bool(result[13]), + "changes": json.loads(result[14]), + "metadata": json.loads(result[15]), + } + cursor.close() - return moderation + + return cls(**case) return None -- 2.45.2 From 2da76eb51a459de9eba6d5095a94b58aee07a80c Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 14:18:19 -0400 Subject: [PATCH 006/146] feat(aurora): subclassed jsonencoder to allow for custom behavior --- aurora/models.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/aurora/models.py b/aurora/models.py index 2b8fcb8..f875ce7 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -68,3 +68,11 @@ class Moderation(BaseModel): return cls(**case) return None + +class JSONEncoder(json.JSONEncoder): + def default(self, o): + if isinstance(o, datetime): + return o.timestamp() + if isinstance(o, timedelta): + return str(o) + return super().default(o) -- 2.45.2 From e8ca0aeb1c54c26f12f3a97e2ae3de38403afd7b Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 14:19:48 -0400 Subject: [PATCH 007/146] fix(aurora): convert float timestamps to integers --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index f875ce7..d93c025 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -72,7 +72,7 @@ class Moderation(BaseModel): class JSONEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, datetime): - return o.timestamp() + return int(o.timestamp()) if isinstance(o, timedelta): return str(o) return super().default(o) -- 2.45.2 From afed1d6a37e5a5dc2ae28e4c8db1937bcace4134 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 14:49:07 -0400 Subject: [PATCH 008/146] feat(aurora): changed a lot of stuff. THIS IS A BREAKING CHANGE! VERY BREAKING! TAKE DATABASE BACKUPS BEFORE UPDATING TO THIS --- aurora/importers/aurora.py | 11 ++++++++--- aurora/models.py | 4 ++++ aurora/utilities/database.py | 36 +++++++++++++++++++++--------------- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/aurora/importers/aurora.py b/aurora/importers/aurora.py index 44cab98..29d153e 100644 --- a/aurora/importers/aurora.py +++ b/aurora/importers/aurora.py @@ -62,7 +62,12 @@ class ImportAuroraView(ui.View): case["target_type"] = "USER" if "role_id" not in case or not case["role_id"]: - case["role_id"] = 0 + case["role_id"] = None + else: + case["role_id"] = int(case["role_id"]) + + case["target_id"] = int(case["target_id"]) + case["moderator_id"] = int(case["moderator_id"]) if "changes" not in case or not case["changes"]: case["changes"] = [] @@ -74,11 +79,11 @@ class ImportAuroraView(ui.View): if not metadata.get("imported_from"): metadata.update({"imported_from": "Aurora"}) - if case["duration"] != "NULL": + if case["duration"] != "NULL" and case["duration"] is not None: hours, minutes, seconds = map(int, case["duration"].split(":")) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) else: - duration = "NULL" + duration = None await mysql_log( self.ctx.guild.id, diff --git a/aurora/models.py b/aurora/models.py index d93c025..c4013cb 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -75,4 +75,8 @@ class JSONEncoder(json.JSONEncoder): return int(o.timestamp()) if isinstance(o, timedelta): return str(o) + if isinstance(o, Moderation): + return o.model_dump() + if isinstance(o, None): + return "NULL" return super().default(o) diff --git a/aurora/utilities/database.py b/aurora/utilities/database.py index a2bcb92..95ad7c8 100644 --- a/aurora/utilities/database.py +++ b/aurora/utilities/database.py @@ -8,8 +8,7 @@ from discord import Guild from redbot.core import data_manager from .logger import logger -from .utils import (convert_timedelta_to_str, generate_dict, - get_next_case_number) +from .utils import convert_timedelta_to_str, generate_dict, get_next_case_number def connect() -> sqlite3.Connection: @@ -42,9 +41,9 @@ async def create_guild_table(guild: Guild): timestamp INTEGER NOT NULL, moderation_type TEXT NOT NULL, target_type TEXT NOT NULL, - target_id TEXT NOT NULL, - moderator_id TEXT NOT NULL, - role_id TEXT, + target_id INTEGER NOT NULL, + moderator_id INTEGER NOT NULL, + role_id INTEGER, duration TEXT, end_timestamp INTEGER, reason TEXT, @@ -52,8 +51,8 @@ async def create_guild_table(guild: Guild): resolved_by TEXT, resolve_reason TEXT, expired INTEGER NOT NULL, - changes TEXT NOT NULL, - metadata TEXT NOT NULL + changes JSON NOT NULL, + metadata JSON NOT NULL ) """ cursor.execute(query) @@ -111,8 +110,8 @@ async def mysql_log( target_type: str, target_id: int, role_id: int, - duration: timedelta, - reason: str, + duration: timedelta = None, + reason: str = None, database: sqlite3.Connection = None, timestamp: int = None, resolved: bool = False, @@ -125,13 +124,14 @@ async def mysql_log( if not timestamp: timestamp = int(time.time()) - if duration != "NULL": + if duration != "NULL" and duration is not None: end_timedelta = datetime.fromtimestamp(timestamp) + duration end_timestamp = int(end_timedelta.timestamp()) duration = convert_timedelta_to_str(duration) else: - end_timestamp = 0 + duration = None + end_timestamp = None if not expired: if int(time.time()) > end_timestamp: @@ -139,11 +139,17 @@ async def mysql_log( else: expired = 0 - if resolved_by is None: - resolved_by = "NULL" + if reason == "NULL": + reason = None - if resolved_reason is None: - resolved_reason = "NULL" + if resolved_by == "NULL": + resolved_by = None + + if resolved_reason == "NULL": + resolved_reason = None + + if role_id == 0: + role_id = None if not database: database = connect() -- 2.45.2 From ea65816c3270ea782afc72c9c4a11b9da978c024 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 14:51:22 -0400 Subject: [PATCH 009/146] fix(aurora): fixed an incorrect if statement --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index c4013cb..9ac8b90 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -38,7 +38,7 @@ class Moderation(BaseModel): result = cursor.fetchone() if result: - if result[7] != "NULL": + if result[7] is not None: hours, minutes, seconds = map(int, result[7].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) else: -- 2.45.2 From 1d825625f3a27d985b5887e9dd7bdd0db1d1ddee Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 14:53:04 -0400 Subject: [PATCH 010/146] fix(aurora): fixed some broken conditional statements --- aurora/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 9ac8b90..77ce650 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -51,13 +51,13 @@ class Moderation(BaseModel): "target_type": str(result[3]), "target_id": int(result[4]), "moderator_id": int(result[5]), - "role_id": int(result[6]) if result[6] != "0" else None, + "role_id": int(result[6]) if result[6] is not None else None, "duration": duration, - "end_timestamp": datetime.fromtimestamp(result[8]) if result[8] != 0 else None, - "reason": result[9] if result[9] != "NULL" else None, + "end_timestamp": datetime.fromtimestamp(result[8]) if result[8] is not None else None, + "reason": result[9], "resolved": bool(result[10]), - "resolved_by": result[11] if result[11] != "NULL" else None, - "resolve_reason": result[12] if result[12] != "NULL" else None, + "resolved_by": result[11], + "resolve_reason": result[12], "expired": bool(result[13]), "changes": json.loads(result[14]), "metadata": json.loads(result[15]), -- 2.45.2 From 92f9619cead2dfd009cc92d336863e5475220080 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 14:54:14 -0400 Subject: [PATCH 011/146] fix(aurora): added two conditional statements to fix an error --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 77ce650..31037ec 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -59,8 +59,8 @@ class Moderation(BaseModel): "resolved_by": result[11], "resolve_reason": result[12], "expired": bool(result[13]), - "changes": json.loads(result[14]), - "metadata": json.loads(result[15]), + "changes": json.loads(result[14]) if result[14] else [], + "metadata": json.loads(result[15]) if result[15] else {}, } cursor.close() -- 2.45.2 From 25d7101cb5d148c518fc714edaac6859a59a6b32 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 14:55:17 -0400 Subject: [PATCH 012/146] fix(aurora): testing a potential bugfix --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 31037ec..d876cf0 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -59,8 +59,8 @@ class Moderation(BaseModel): "resolved_by": result[11], "resolve_reason": result[12], "expired": bool(result[13]), - "changes": json.loads(result[14]) if result[14] else [], - "metadata": json.loads(result[15]) if result[15] else {}, + "changes": result[14] if result[14] else [], + "metadata": result[15] if result[15] else {}, } cursor.close() -- 2.45.2 From 69805b276f20a17347a58396427f03c5bf4b2fec Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 14:58:24 -0400 Subject: [PATCH 013/146] fix(aurora): fixed an issue with json decoding --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index d876cf0..391312d 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -59,8 +59,8 @@ class Moderation(BaseModel): "resolved_by": result[11], "resolve_reason": result[12], "expired": bool(result[13]), - "changes": result[14] if result[14] else [], - "metadata": result[15] if result[15] else {}, + "changes": json.loads(result[14].strip('"').replace('\\"', '"')) if result[14] else [], + "metadata": json.loads(result[15].strip('"').replace('\\"', '"')) if result[15] else {}, } cursor.close() -- 2.45.2 From ca1722fee33ebf41301c15526b85e7a7e67f86b5 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 15:05:50 -0400 Subject: [PATCH 014/146] feat(aurora): migrated to a custom json encoder --- aurora/aurora.py | 30 ++++++++++-------------------- aurora/models.py | 12 ------------ aurora/utilities/encoder.py | 15 +++++++++++++++ aurora/utilities/utils.py | 10 ++++++++++ 4 files changed, 35 insertions(+), 32 deletions(-) create mode 100644 aurora/utilities/encoder.py diff --git a/aurora/aurora.py b/aurora/aurora.py index 15a9b2f..463de76 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -19,8 +19,7 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import (box, error, humanize_list, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -29,19 +28,10 @@ from aurora.menus.guild import Guild from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides from aurora.utilities.config import config, register_config -from aurora.utilities.database import (connect, create_guild_table, fetch_case, - mysql_log) -from aurora.utilities.factory import (addrole_embed, case_factory, - changes_factory, evidenceformat_factory, - guild_embed, immune_embed, - message_factory, overrides_embed) +from aurora.utilities.database import connect, create_guild_table, fetch_case, mysql_log +from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed from aurora.utilities.logger import logger -from aurora.utilities.utils import (check_moddable, check_permissions, - convert_timedelta_to_str, - fetch_channel_dict, fetch_user_dict, - generate_dict, get_footer_image, log, - send_evidenceformat, - timedelta_from_relativedelta) +from aurora.utilities.utils import check_moddable, check_permissions, convert_timedelta_to_str, dump, dumps, fetch_channel_dict, fetch_user_dict, generate_dict, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta class Aurora(commands.Cog): @@ -1078,7 +1068,7 @@ class Aurora(commands.Cog): ) with open(filename, "w", encoding="utf-8") as f: - json.dump(cases, f, indent=2) + dump(cases, f, indent=2) await interaction.followup.send( file=discord.File( @@ -1337,7 +1327,7 @@ class Aurora(commands.Cog): cursor.execute( resolve_query, ( - json.dumps(changes), + dumps(changes), interaction.user.id, reason, case_dict["moderation_id"], @@ -1416,7 +1406,7 @@ class Aurora(commands.Cog): ) with open(filename, "w", encoding="utf-8") as f: - json.dump(case_dict, f, indent=2) + dump(case_dict, f, indent=2) if export.value == "codeblock": content = f"Case #{case:,} exported.\n" + warning( @@ -1437,7 +1427,7 @@ class Aurora(commands.Cog): os.remove(filename) return await interaction.response.send_message( - content=box(json.dumps(case_dict, indent=2), 'json'), + content=box(dumps(case_dict, indent=2), 'json'), ephemeral=ephemeral, ) return @@ -1586,7 +1576,7 @@ class Aurora(commands.Cog): cursor.execute( update_query, ( - json.dumps(changes), + dumps(changes), reason, convert_timedelta_to_str(parsed_time), end_timestamp, @@ -1595,7 +1585,7 @@ class Aurora(commands.Cog): ) else: update_query = f"UPDATE `moderation_{interaction.guild.id}` SET changes = ?, reason = ? WHERE moderation_id = ?" - cursor.execute(update_query, (json.dumps(changes), reason, case)) + cursor.execute(update_query, (dumps(changes), reason, case)) database.commit() new_case = await fetch_case(case, interaction.guild.id) diff --git a/aurora/models.py b/aurora/models.py index 391312d..6244ebf 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -68,15 +68,3 @@ class Moderation(BaseModel): return cls(**case) return None - -class JSONEncoder(json.JSONEncoder): - def default(self, o): - if isinstance(o, datetime): - return int(o.timestamp()) - if isinstance(o, timedelta): - return str(o) - if isinstance(o, Moderation): - return o.model_dump() - if isinstance(o, None): - return "NULL" - return super().default(o) diff --git a/aurora/utilities/encoder.py b/aurora/utilities/encoder.py new file mode 100644 index 0000000..ba69ef2 --- /dev/null +++ b/aurora/utilities/encoder.py @@ -0,0 +1,15 @@ +import json +from datetime import datetime, timedelta + +from aurora.models import Moderation + + +class JSONEncoder(json.JSONEncoder): + def default(self, o): + if isinstance(o, datetime): + return int(o.timestamp()) + if isinstance(o, timedelta): + return str(o) + if isinstance(o, Moderation): + return o.model_dump() + return super().default(o) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 1ff21fa..8a31063 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -296,3 +296,13 @@ def get_footer_image(coginstance: commands.Cog) -> File: """Returns the footer image for the embeds.""" image_path = data_manager.bundled_data_path(coginstance) / "arrow.png" return File(image_path, filename="arrow.png", description="arrow") + +def dumps(obj: any, indent: int = None) -> str: + """Returns a JSON string from an object.""" + from aurora.utilities.encoder import JSONEncoder + return json.dumps(obj, indent=indent, cls=JSONEncoder) + +def dump(obj: any, indent: int = None) -> str: + """Returns a JSON string from an object.""" + from aurora.utilities.encoder import JSONEncoder + return json.dump(obj, indent=indent, cls=JSONEncoder) -- 2.45.2 From 58303b8e9cf73f6d691229e2df25125c89f92b15 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 15:08:08 -0400 Subject: [PATCH 015/146] feat(aurora): added a to_json method to the moderation model --- aurora/models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aurora/models.py b/aurora/models.py index 6244ebf..4c0fff6 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -68,3 +68,7 @@ class Moderation(BaseModel): return cls(**case) return None + + def to_json(self, indent: int = None, file: bool = False): + from aurora.utilities.utils import dump, dumps + return dump(self.model_dump(), indent=indent) if file else dumps(self.model_dump(), indent=indent) -- 2.45.2 From df8761205518edc0c7abae6a60cf5559c5135b68 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 15:13:31 -0400 Subject: [PATCH 016/146] feat(aurora): added imported_timestamp to metadata on new imports --- aurora/importers/aurora.py | 2 ++ aurora/importers/galacticbot.py | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/aurora/importers/aurora.py b/aurora/importers/aurora.py index 29d153e..d1905e7 100644 --- a/aurora/importers/aurora.py +++ b/aurora/importers/aurora.py @@ -1,6 +1,7 @@ # pylint: disable=duplicate-code import json from datetime import timedelta +from time import time from typing import Dict from discord import ButtonStyle, Interaction, Message, ui @@ -78,6 +79,7 @@ class ImportAuroraView(ui.View): metadata: Dict[str, any] = json.loads(case["metadata"]) if not metadata.get("imported_from"): metadata.update({"imported_from": "Aurora"}) + metadata.update({"imported_timestamp": int(time())}) if case["duration"] != "NULL" and case["duration"] is not None: hours, minutes, seconds = map(int, case["duration"].split(":")) diff --git a/aurora/importers/galacticbot.py b/aurora/importers/galacticbot.py index c6e0b99..3d13b8e 100644 --- a/aurora/importers/galacticbot.py +++ b/aurora/importers/galacticbot.py @@ -1,6 +1,7 @@ # pylint: disable=duplicate-code import json from datetime import timedelta +from time import time from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands @@ -67,12 +68,12 @@ class ImportGalacticBotView(ui.View): if case["duration"] is not None and float(case["duration"]) != 0: duration = timedelta(seconds=round(float(case["duration"]) / 1000)) else: - duration = "NULL" + duration = None except OverflowError: failed_cases.append(case["case"]) continue - metadata = {"imported_from": "GalacticBot"} + metadata = {"imported_from": "GalacticBot", "imported_timestamp": int(time())} if case["type"] == "SLOWMODE": metadata["seconds"] = case["data"]["seconds"] @@ -113,14 +114,14 @@ class ImportGalacticBotView(ui.View): ] else: resolved = 0 - resolved_by = "NULL" - resolved_reason = "NULL" + resolved_by = None + resolved_reason = None changes = [] if case["reason"] and case["reason"] != "N/A": reason = case["reason"] else: - reason = "NULL" + reason = None await mysql_log( self.ctx.guild.id, -- 2.45.2 From 7dfd9c607a30324663a23be36571cb2cf62cbb96 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 15:25:05 -0400 Subject: [PATCH 017/146] misc(aurora): moved some stuff around --- aurora/aurora.py | 21 ++++-- aurora/models.py | 2 +- aurora/utilities/encoder.py | 15 ----- aurora/utilities/json.py | 124 ++++++++++++++++++++++++++++++++++++ aurora/utilities/utils.py | 10 --- 5 files changed, 141 insertions(+), 31 deletions(-) delete mode 100644 aurora/utilities/encoder.py create mode 100644 aurora/utilities/json.py diff --git a/aurora/aurora.py b/aurora/aurora.py index 463de76..300d127 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -19,7 +19,8 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (box, error, humanize_list, + humanize_timedelta, warning) from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -28,10 +29,20 @@ from aurora.menus.guild import Guild from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides from aurora.utilities.config import config, register_config -from aurora.utilities.database import connect, create_guild_table, fetch_case, mysql_log -from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed +from aurora.utilities.database import (connect, create_guild_table, fetch_case, + mysql_log) +from aurora.utilities.factory import (addrole_embed, case_factory, + changes_factory, evidenceformat_factory, + guild_embed, immune_embed, + message_factory, overrides_embed) +from aurora.utilities.json import dump, dumps from aurora.utilities.logger import logger -from aurora.utilities.utils import check_moddable, check_permissions, convert_timedelta_to_str, dump, dumps, fetch_channel_dict, fetch_user_dict, generate_dict, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta +from aurora.utilities.utils import (check_moddable, check_permissions, + convert_timedelta_to_str, + fetch_channel_dict, fetch_user_dict, + generate_dict, get_footer_image, log, + send_evidenceformat, + timedelta_from_relativedelta) class Aurora(commands.Cog): @@ -40,7 +51,7 @@ class Aurora(commands.Cog): This cog stores all of its data in an SQLite database.""" __author__ = ["SeaswimmerTheFsh"] - __version__ = "2.1.2" + __version__ = "2.2.0" __documentation__ = "https://seacogs.coastalcommits.com/aurora/" async def red_delete_data_for_user(self, *, requester, user_id: int): diff --git a/aurora/models.py b/aurora/models.py index 4c0fff6..5ee33c5 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -70,5 +70,5 @@ class Moderation(BaseModel): return None def to_json(self, indent: int = None, file: bool = False): - from aurora.utilities.utils import dump, dumps + from aurora.utilities.json import dump, dumps return dump(self.model_dump(), indent=indent) if file else dumps(self.model_dump(), indent=indent) diff --git a/aurora/utilities/encoder.py b/aurora/utilities/encoder.py deleted file mode 100644 index ba69ef2..0000000 --- a/aurora/utilities/encoder.py +++ /dev/null @@ -1,15 +0,0 @@ -import json -from datetime import datetime, timedelta - -from aurora.models import Moderation - - -class JSONEncoder(json.JSONEncoder): - def default(self, o): - if isinstance(o, datetime): - return int(o.timestamp()) - if isinstance(o, timedelta): - return str(o) - if isinstance(o, Moderation): - return o.model_dump() - return super().default(o) diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py new file mode 100644 index 0000000..90b335f --- /dev/null +++ b/aurora/utilities/json.py @@ -0,0 +1,124 @@ +import json +from datetime import datetime, timedelta + +from aurora.models import Moderation + + +class JSONEncoder(json.JSONEncoder): + def default(self, o): + if isinstance(o, datetime): + return int(o.timestamp()) + if isinstance(o, timedelta): + return str(o) + if isinstance(o, Moderation): + return o.model_dump() + return super().default(o) + + +# This is a wrapper around the json module's dumps function that uses our custom JSONEncoder class +def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, + allow_nan=True, indent=None, separators=None, + default=None, sort_keys=False, **kw) -> str: + """Serialize ``obj`` to a JSON formatted ``str``. + + If ``skipkeys`` is true then ``dict`` keys that are not basic types + (``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped + instead of raising a ``TypeError``. + + If ``ensure_ascii`` is false, then the return value can contain non-ASCII + characters if they appear in strings contained in ``obj``. Otherwise, all + such characters are escaped in JSON strings. + + If ``check_circular`` is false, then the circular reference check + for container types will be skipped and a circular reference will + result in an ``RecursionError`` (or worse). + + If ``allow_nan`` is false, then it will be a ``ValueError`` to + serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in + strict compliance of the JSON specification, instead of using the + JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). + + If ``indent`` is a non-negative integer, then JSON array elements and + object members will be pretty-printed with that indent level. An indent + level of 0 will only insert newlines. ``None`` is the most compact + representation. + + If specified, ``separators`` should be an ``(item_separator, key_separator)`` + tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and + ``(',', ': ')`` otherwise. To get the most compact JSON representation, + you should specify ``(',', ':')`` to eliminate whitespace. + + ``default(obj)`` is a function that should return a serializable version + of obj or raise TypeError. The default simply raises TypeError. + + If *sort_keys* is true (default: ``False``), then the output of + dictionaries will be sorted by key. + """ + return json.dumps( + obj, + cls=JSONEncoder, + shipkeys=skipkeys, + ensure_ascii=ensure_ascii, + check_circular=check_circular, + allow_nan=allow_nan, + indent=indent, + separators=separators, + default=default, + sort_keys=sort_keys, + **kw + ) + +# This is a wrapper around the json module's dump function that uses our custom JSONEncoder class +def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, + allow_nan=True, indent=None, separators=None, + default=None, sort_keys=False, **kw) -> str: + """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a + ``.write()``-supporting file-like object). + + If ``skipkeys`` is true then ``dict`` keys that are not basic types + (``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped + instead of raising a ``TypeError``. + + If ``ensure_ascii`` is false, then the strings written to ``fp`` can + contain non-ASCII characters if they appear in strings contained in + ``obj``. Otherwise, all such characters are escaped in JSON strings. + + If ``check_circular`` is false, then the circular reference check + for container types will be skipped and a circular reference will + result in an ``RecursionError`` (or worse). + + If ``allow_nan`` is false, then it will be a ``ValueError`` to + serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) + in strict compliance of the JSON specification, instead of using the + JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). + + If ``indent`` is a non-negative integer, then JSON array elements and + object members will be pretty-printed with that indent level. An indent + level of 0 will only insert newlines. ``None`` is the most compact + representation. + + If specified, ``separators`` should be an ``(item_separator, key_separator)`` + tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and + ``(',', ': ')`` otherwise. To get the most compact JSON representation, + you should specify ``(',', ':')`` to eliminate whitespace. + + ``default(obj)`` is a function that should return a serializable version + of obj or raise TypeError. The default simply raises TypeError. + + If *sort_keys* is true (default: ``False``), then the output of + dictionaries will be sorted by key. + """ + return json.dump( + obj, + fp, + cls=JSONEncoder, + skipkeys=skipkeys, + ensure_ascii=ensure_ascii, + check_circular=check_circular, + allow_nan=allow_nan, + indent=indent, + separators=separators, + default=default, + sort_keys=sort_keys, + **kw + ) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 8a31063..1ff21fa 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -296,13 +296,3 @@ def get_footer_image(coginstance: commands.Cog) -> File: """Returns the footer image for the embeds.""" image_path = data_manager.bundled_data_path(coginstance) / "arrow.png" return File(image_path, filename="arrow.png", description="arrow") - -def dumps(obj: any, indent: int = None) -> str: - """Returns a JSON string from an object.""" - from aurora.utilities.encoder import JSONEncoder - return json.dumps(obj, indent=indent, cls=JSONEncoder) - -def dump(obj: any, indent: int = None) -> str: - """Returns a JSON string from an object.""" - from aurora.utilities.encoder import JSONEncoder - return json.dump(obj, indent=indent, cls=JSONEncoder) -- 2.45.2 From 98f3e5943bd0218122c68b40057b369f04ec52db Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 15:29:01 -0400 Subject: [PATCH 018/146] fix(aurora): fixed incorrect kwarg name --- aurora/utilities/json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index 90b335f..bac1668 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -57,7 +57,7 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, return json.dumps( obj, cls=JSONEncoder, - shipkeys=skipkeys, + skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, -- 2.45.2 From ca7c0d8d7cc1e4b300b55fcc6a0aad76e061fd97 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 15:33:49 -0400 Subject: [PATCH 019/146] fix(aurora): use pydantic basemodels instead of the Moderation model --- aurora/utilities/json.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index bac1668..c3e79a3 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -1,7 +1,7 @@ import json from datetime import datetime, timedelta -from aurora.models import Moderation +from pydantic import BaseModel class JSONEncoder(json.JSONEncoder): @@ -10,7 +10,7 @@ class JSONEncoder(json.JSONEncoder): return int(o.timestamp()) if isinstance(o, timedelta): return str(o) - if isinstance(o, Moderation): + if isinstance(o, BaseModel): return o.model_dump() return super().default(o) -- 2.45.2 From 2dfc9d98240e45c2f6ccc9710d34f7ba69bbe661 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 15:37:07 -0400 Subject: [PATCH 020/146] fix(aurora): use AuroraBaseModels for the JSONEncoder class instead of just pydantic ones to prevent issues with other data types --- aurora/models.py | 5 ++++- aurora/utilities/json.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 5ee33c5..62b5ea7 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -5,7 +5,10 @@ from typing import Dict, List, Optional from pydantic import BaseModel -class Moderation(BaseModel): + +class AuroraBaseModel(BaseModel): + """Base class for all models in Aurora.""" +class Moderation(AuroraBaseModel): moderation_id: int guild_id: int timestamp: datetime diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index c3e79a3..e8678af 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -1,7 +1,7 @@ import json from datetime import datetime, timedelta -from pydantic import BaseModel +from aurora.models import AuroraBaseModel class JSONEncoder(json.JSONEncoder): @@ -10,7 +10,7 @@ class JSONEncoder(json.JSONEncoder): return int(o.timestamp()) if isinstance(o, timedelta): return str(o) - if isinstance(o, BaseModel): + if isinstance(o, AuroraBaseModel): return o.model_dump() return super().default(o) -- 2.45.2 From 6147c8c6d5c7ca16d7e84301ce4c798321ef27ef Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 16:54:12 -0400 Subject: [PATCH 021/146] feat(aurora): whole bunch of changes to the models and various other things --- aurora/aurora.py | 36 ++++------- aurora/models.py | 120 ++++++++++++++++++++++++++---------- aurora/utilities/factory.py | 114 +++++++++------------------------- aurora/utilities/utils.py | 45 ++++++++------ poetry.lock | 13 +++- pyproject.toml | 1 + 6 files changed, 167 insertions(+), 162 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 300d127..0996fd8 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -19,8 +19,7 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import (box, error, humanize_list, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -28,21 +27,13 @@ from aurora.menus.addrole import Addrole from aurora.menus.guild import Guild from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides +from aurora.models import Moderation from aurora.utilities.config import config, register_config -from aurora.utilities.database import (connect, create_guild_table, fetch_case, - mysql_log) -from aurora.utilities.factory import (addrole_embed, case_factory, - changes_factory, evidenceformat_factory, - guild_embed, immune_embed, - message_factory, overrides_embed) +from aurora.utilities.database import connect, create_guild_table, fetch_case, mysql_log +from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed from aurora.utilities.json import dump, dumps from aurora.utilities.logger import logger -from aurora.utilities.utils import (check_moddable, check_permissions, - convert_timedelta_to_str, - fetch_channel_dict, fetch_user_dict, - generate_dict, get_footer_image, log, - send_evidenceformat, - timedelta_from_relativedelta) +from aurora.utilities.utils import check_moddable, check_permissions, convert_timedelta_to_str, fetch_channel_dict, fetch_user_dict, generate_dict, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta class Aurora(commands.Cog): @@ -1406,10 +1397,10 @@ class Aurora(commands.Cog): ) if case != 0: - case_dict = await fetch_case(case, interaction.guild.id) - if case_dict: + mod = Moderation.from_sql(interaction.client, case, interaction.guild.id) + if mod: if export: - if export.value == "file" or len(str(case_dict)) > 1800: + if export.value == "file" or len(mod.to_json(2)) > 1800: filename = ( str(data_manager.cog_data_path(cog_instance=self)) + str(os.sep) @@ -1417,8 +1408,7 @@ class Aurora(commands.Cog): ) with open(filename, "w", encoding="utf-8") as f: - dump(case_dict, f, indent=2) - + mod.to_json(2, f) if export.value == "codeblock": content = f"Case #{case:,} exported.\n" + warning( "Case was too large to export as codeblock, so it has been uploaded as a `.json` file." @@ -1438,27 +1428,27 @@ class Aurora(commands.Cog): os.remove(filename) return await interaction.response.send_message( - content=box(dumps(case_dict, indent=2), 'json'), + content=box(mod.to_json(2), 'json'), ephemeral=ephemeral, ) return if changes: embed = await changes_factory( - interaction=interaction, case_dict=case_dict + interaction=interaction, moderation=mod ) await interaction.response.send_message( embed=embed, ephemeral=ephemeral ) elif evidenceformat: content = await evidenceformat_factory( - interaction=interaction, case_dict=case_dict + interaction=interaction, moderation=mod ) await interaction.response.send_message( content=content, ephemeral=ephemeral ) else: embed = await case_factory( - interaction=interaction, case_dict=case_dict + interaction=interaction, moderation=mod ) await interaction.response.send_message( embed=embed, ephemeral=ephemeral diff --git a/aurora/models.py b/aurora/models.py index 62b5ea7..9fbb0ba 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,14 +1,18 @@ -import json from datetime import datetime, timedelta -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional, Union +from async_property import async_cached_property +from discord import Forbidden, HTTPException, InvalidData, NotFound from pydantic import BaseModel +from redbot.core.bot import Red +from aurora.utilities.utils import generate_dict class AuroraBaseModel(BaseModel): """Base class for all models in Aurora.""" class Moderation(AuroraBaseModel): + bot: Red moderation_id: int guild_id: int timestamp: datetime @@ -27,11 +31,36 @@ class Moderation(AuroraBaseModel): changes: List[Dict] metadata: Dict + @property + def id(self) -> int: + return self.moderation_id + + @property + def type(self) -> str: + return self.moderation_type + + @async_cached_property + async def moderator(self) -> "PartialUser": + return await PartialUser.from_id(self.bot, self.moderator_id) + + @async_cached_property + async def target(self) -> Union["PartialUser", "PartialChannel"]: + if self.target_type == "user": + return await PartialUser.from_id(self.bot, self.target_id) + else: + return await PartialChannel.from_id(self.bot, self.target_id) + + @async_cached_property + async def resolved_by_user(self) -> Optional["PartialUser"]: + if self.resolved_by: + return await PartialUser.from_id(self.bot, self.resolved_by) + return None + def __str__(self): return f"{self.moderation_type} {self.target_type} {self.target_id} {self.reason}" @classmethod - def from_sql(cls, moderation_id: int, guild_id: int): + def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> Optional["Moderation"]: from aurora.utilities.database import connect query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;" @@ -41,37 +70,64 @@ class Moderation(AuroraBaseModel): result = cursor.fetchone() if result: - if result[7] is not None: - hours, minutes, seconds = map(int, result[7].split(':')) - duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) - else: - duration = None - case = { - "moderation_id": int(result[0]), - "guild_id": int(guild_id), - "timestamp": datetime.fromtimestamp(result[1]), - "moderation_type": str(result[2]), - "target_type": str(result[3]), - "target_id": int(result[4]), - "moderator_id": int(result[5]), - "role_id": int(result[6]) if result[6] is not None else None, - "duration": duration, - "end_timestamp": datetime.fromtimestamp(result[8]) if result[8] is not None else None, - "reason": result[9], - "resolved": bool(result[10]), - "resolved_by": result[11], - "resolve_reason": result[12], - "expired": bool(result[13]), - "changes": json.loads(result[14].strip('"').replace('\\"', '"')) if result[14] else [], - "metadata": json.loads(result[15].strip('"').replace('\\"', '"')) if result[15] else {}, - } - + case = generate_dict(result) cursor.close() - - return cls(**case) + return cls.from_dict(bot, case) return None - def to_json(self, indent: int = None, file: bool = False): + @classmethod + def from_dict(cls, bot: Red, data: dict) -> "Moderation": + return cls(bot=bot, **data) + + def to_json(self, indent: int = None, file: Any = None): from aurora.utilities.json import dump, dumps - return dump(self.model_dump(), indent=indent) if file else dumps(self.model_dump(), indent=indent) + return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent) + + +class PartialUser(AuroraBaseModel): + id: int + username: str + discriminator: int + + @property + def name(self): + return f"{self.username}#{self.discriminator}" if self.discriminator == 0 else self.username + + def __str__(self): + return self.name + + @classmethod + async def from_id(cls, bot: Red, user_id: int) -> "PartialUser": + user = bot.get_user(user_id) + if not user: + try: + user = await bot.fetch_user(user_id) + return cls(id=user.id, username=user.name, discriminator=user.discriminator) + except NotFound: + return cls(id=user_id, username="Deleted User", discriminator=0) + +class PartialChannel(AuroraBaseModel): + id: int + name: str + + @property + def mention(self): + if self.name == "Deleted Channel" or self.name == "Forbidden Channel": + return self.name + return f"<#{self.id}>" + + def __str__(self): + return self.mention + + @classmethod + async def from_id(cls, bot: Red, channel_id: int) -> "PartialChannel": + user = bot.get_channel(channel_id) + if not user: + try: + user = await bot.fetch_channel(channel_id) + return cls(id=user.id, username=user.name, discriminator=user.discriminator) + except (NotFound, InvalidData, HTTPException, Forbidden) as e: + if e == Forbidden: + return cls(id=channel_id, name="Forbidden Channel") + return cls(id=channel_id, name="Deleted Channel") diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index d7f4eb7..4f9d8c6 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -1,13 +1,18 @@ # pylint: disable=cyclic-import from datetime import datetime, timedelta -from typing import Union +from typing import Optional, Union -from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User +from discord import (Color, Embed, Guild, Interaction, InteractionMessage, + Member, Role, User) from redbot.core import commands -from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (bold, box, error, + humanize_timedelta, warning) +from aurora.models import Moderation, PartialChannel, PartialUser from aurora.utilities.config import config -from aurora.utilities.utils import fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, get_pagesize_str +from aurora.utilities.utils import (fetch_channel_dict, fetch_user_dict, + get_bool_emoji, get_next_case_number, + get_pagesize_str) async def message_factory( @@ -94,7 +99,7 @@ async def message_factory( async def log_factory( - interaction: Interaction, case_dict: dict, resolved: bool = False + interaction: Interaction, moderation: Moderation, resolved: bool = False ) -> Embed: """This function creates a log embed from set parameters, meant for moderation logging. @@ -103,113 +108,50 @@ async def log_factory( case_dict (dict): The case dictionary. resolved (bool, optional): Whether the case is resolved or not. Defaults to False. """ + target: Union[PartialUser, PartialChannel] = await moderation.target + moderator: PartialUser = await moderation.moderator if resolved: - if case_dict["target_type"] == "USER": - target_user = await fetch_user_dict(interaction.client, case_dict["target_id"]) - target_name = ( - f"`{target_user['name']}`" - if target_user["discriminator"] == "0" - else f"`{target_user['name']}#{target_user['discriminator']}`" - ) - elif case_dict["target_type"] == "CHANNEL": - target_user = await fetch_channel_dict(interaction.guild, case_dict["target_id"]) - if target_user["mention"]: - target_name = f"{target_user['mention']}" - else: - target_name = f"`{target_user['name']}`" - - moderator_user = await fetch_user_dict(interaction.client, case_dict["moderator_id"]) - moderator_name = ( - f"`{moderator_user['name']}`" - if moderator_user["discriminator"] == "0" - else f"`{moderator_user['name']}#{moderator_user['discriminator']}`" - ) - embed = Embed( - title=f"📕 Case #{case_dict['moderation_id']:,} Resolved", + title=f"📕 Case #{moderation.id:,} Resolved", color=await interaction.client.get_embed_color(interaction.channel), ) - embed.description = f"**Type:** {str.title(case_dict['moderation_type'])}\n**Target:** {target_name} ({target_user['id']})\n**Moderator:** {moderator_name} ({moderator_user['id']})\n**Timestamp:** | " + resolved_by: Optional[PartialUser] = await moderation.resolved_by_user + embed.description = f"**Type:** {str.title(moderation.moderation_type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Timestamp:** | " - if case_dict["duration"] != "NULL": - td = timedelta( - **{ - unit: int(val) - for unit, val in zip( - ["hours", "minutes", "seconds"], - case_dict["duration"].split(":"), - ) - } - ) + if moderation.duration is not None: duration_embed = ( - f"{humanize_timedelta(timedelta=td)} | " - if case_dict["expired"] == "0" - else str(humanize_timedelta(timedelta=td)) + f"{humanize_timedelta(timedelta=moderation.duration)} | " + if not moderation.expired + else str(humanize_timedelta(timedelta=moderation.duration)) ) embed.description = ( embed.description - + f"\n**Duration:** {duration_embed}\n**Expired:** {bool(case_dict['expired'])}" + + f"\n**Duration:** {duration_embed}\n**Expired:** {moderation.expired}" ) - embed.add_field(name="Reason", value=box(case_dict["reason"]), inline=False) + embed.add_field(name="Reason", value=box(moderation.reason), inline=False) - resolved_user = await fetch_user_dict(interaction.client, case_dict["resolved_by"]) - resolved_name = ( - resolved_user["name"] - if resolved_user["discriminator"] == "0" - else f"{resolved_user['name']}#{resolved_user['discriminator']}" - ) embed.add_field( name="Resolve Reason", - value=f"Resolved by `{resolved_name}` ({resolved_user['id']}) for:\n" - + box(case_dict["resolve_reason"]), + value=f"Resolved by `{resolved_by.name}` ({resolved_by.id}) for:\n" + + box(moderation.resolve_reason), inline=False, ) else: - if case_dict["target_type"] == "USER": - target_user = await fetch_user_dict(interaction.client, case_dict["target_id"]) - target_name = ( - f"`{target_user['name']}`" - if target_user["discriminator"] == "0" - else f"`{target_user['name']}#{target_user['discriminator']}`" - ) - elif case_dict["target_type"] == "CHANNEL": - target_user = await fetch_channel_dict(interaction.guild, case_dict["target_id"]) - if target_user["mention"]: - target_name = target_user["mention"] - else: - target_name = f"`{target_user['name']}`" - - moderator_user = await fetch_user_dict(interaction.client, case_dict["moderator_id"]) - moderator_name = ( - f"`{moderator_user['name']}`" - if moderator_user["discriminator"] == "0" - else f"`{moderator_user['name']}#{moderator_user['discriminator']}`" - ) - embed = Embed( - title=f"📕 Case #{case_dict['moderation_id']:,}", + title=f"📕 Case #{moderation.id:,}", color=await interaction.client.get_embed_color(interaction.channel), ) - embed.description = f"**Type:** {str.title(case_dict['moderation_type'])}\n**Target:** {target_name} ({target_user['id']})\n**Moderator:** {moderator_name} ({moderator_user['id']})\n**Timestamp:** | " + embed.description = f"**Type:** {str.title(moderation.type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Timestamp:** | " - if case_dict["duration"] != "NULL": - td = timedelta( - **{ - unit: int(val) - for unit, val in zip( - ["hours", "minutes", "seconds"], - case_dict["duration"].split(":"), - ) - } - ) + if moderation.duration: embed.description = ( embed.description - + f"\n**Duration:** {humanize_timedelta(timedelta=td)} | " + + f"\n**Duration:** {humanize_timedelta(timedelta=moderation.duration)} | " ) - embed.add_field(name="Reason", value=box(case_dict["reason"]), inline=False) + embed.add_field(name="Reason", value=box(moderation.reason), inline=False) return embed diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 1ff21fa..3761543 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -1,7 +1,6 @@ # pylint: disable=cyclic-import import json -from datetime import datetime -from datetime import timedelta as td +from datetime import datetime, timedelta from typing import Optional, Union from dateutil.relativedelta import relativedelta as rd @@ -10,7 +9,7 @@ from discord.errors import Forbidden, NotFound from redbot.core import commands, data_manager from redbot.core.utils.chat_formatting import error -from .config import config +from aurora.utilities.config import config def check_permissions( @@ -125,24 +124,30 @@ async def get_next_case_number(guild_id: str, cursor=None) -> int: return (result[0] + 1) if result else 1 -def generate_dict(result) -> dict: +def generate_dict(result: dict, guild_id: int) -> dict: + if result[7] is not None: + hours, minutes, seconds = map(int, result[7].split(':')) + duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) + else: + duration = None case = { - "moderation_id": result[0], - "timestamp": result[1], - "moderation_type": result[2], - "target_type": result[3], - "target_id": result[4], - "moderator_id": result[5], - "role_id": result[6], - "duration": result[7], - "end_timestamp": result[8], + "moderation_id": int(result[0]), + "guild_id": int(guild_id), + "timestamp": datetime.fromtimestamp(result[1]), + "moderation_type": str(result[2]), + "target_type": str(result[3]), + "target_id": int(result[4]), + "moderator_id": int(result[5]), + "role_id": int(result[6]) if result[6] is not None else None, + "duration": duration, + "end_timestamp": datetime.fromtimestamp(result[8]) if result[8] is not None else None, "reason": result[9], - "resolved": result[10], + "resolved": bool(result[10]), "resolved_by": result[11], "resolve_reason": result[12], - "expired": result[13], - "changes": json.loads(result[14]), - "metadata": json.loads(result[15]), + "expired": bool(result[13]), + "changes": json.loads(result[14].strip('"').replace('\\"', '"')) if result[14] else [], + "metadata": json.loads(result[15].strip('"').replace('\\"', '"')) if result[15] else {}, } return case @@ -241,9 +246,9 @@ async def send_evidenceformat(interaction: Interaction, case_dict: dict) -> None await interaction.followup.send(content=content, ephemeral=True) -def convert_timedelta_to_str(timedelta: td) -> str: +def convert_timedelta_to_str(td: timedelta) -> str: """This function converts a timedelta object to a string.""" - total_seconds = int(timedelta.total_seconds()) + total_seconds = int(td.total_seconds()) hours = total_seconds // 3600 minutes = (total_seconds % 3600) // 60 seconds = total_seconds % 60 @@ -286,7 +291,7 @@ def create_pagesize_options() -> list[SelectOption]: ) return options -def timedelta_from_relativedelta(relativedelta: rd) -> td: +def timedelta_from_relativedelta(relativedelta: rd) -> timedelta: """Converts a relativedelta object to a timedelta object.""" now = datetime.now() then = now - relativedelta diff --git a/poetry.lock b/poetry.lock index 0a5918b..9089f57 100644 --- a/poetry.lock +++ b/poetry.lock @@ -206,6 +206,17 @@ files = [ {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, ] +[[package]] +name = "async-property" +version = "0.2.2" +description = "Python decorator for async properties." +optional = false +python-versions = "*" +files = [ + {file = "async_property-0.2.2-py2.py3-none-any.whl", hash = "sha256:8924d792b5843994537f8ed411165700b27b2bd966cefc4daeefc1253442a9d7"}, + {file = "async_property-0.2.2.tar.gz", hash = "sha256:17d9bd6ca67e27915a75d92549df64b5c7174e9dc806b30a3934dc4ff0506380"}, +] + [[package]] name = "attrs" version = "23.2.0" @@ -2545,4 +2556,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "67eb5e616951979332b6f32bcb39d85171cbf8377f566ea1862c51b5068b52f3" +content-hash = "05c89da1577b4a3507856338502218e0da92dd9785a5fc4a78d6cb59058d887f" diff --git a/pyproject.toml b/pyproject.toml index 91c350e..7e50d58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ websockets = "^12.0" pillow = "^10.3.0" numpy = "^1.26.4" pydantic = "^2.7.1" +async-property = "^0.2.2" [tool.poetry.group.dev] optional = true -- 2.45.2 From d13ad88f1699b0439cf3e6798da8b0cc82eb6ddb Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 17:09:22 -0400 Subject: [PATCH 022/146] fix(aurora): fixed a whole bunch of pydantic errors --- aurora/models.py | 10 +++------- aurora/utilities/factory.py | 14 +++++++------- poetry.lock | 13 +------------ pyproject.toml | 1 - 4 files changed, 11 insertions(+), 27 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 9fbb0ba..6de0340 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,7 +1,6 @@ from datetime import datetime, timedelta from typing import Any, Dict, List, Optional, Union -from async_property import async_cached_property from discord import Forbidden, HTTPException, InvalidData, NotFound from pydantic import BaseModel from redbot.core.bot import Red @@ -39,19 +38,16 @@ class Moderation(AuroraBaseModel): def type(self) -> str: return self.moderation_type - @async_cached_property - async def moderator(self) -> "PartialUser": + async def get_moderator(self) -> "PartialUser": return await PartialUser.from_id(self.bot, self.moderator_id) - @async_cached_property - async def target(self) -> Union["PartialUser", "PartialChannel"]: + async def get_target(self) -> Union["PartialUser", "PartialChannel"]: if self.target_type == "user": return await PartialUser.from_id(self.bot, self.target_id) else: return await PartialChannel.from_id(self.bot, self.target_id) - @async_cached_property - async def resolved_by_user(self) -> Optional["PartialUser"]: + async def get_resolved_by(self) -> Optional["PartialUser"]: if self.resolved_by: return await PartialUser.from_id(self.bot, self.resolved_by) return None diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 4f9d8c6..c6506ec 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -1,6 +1,6 @@ # pylint: disable=cyclic-import from datetime import datetime, timedelta -from typing import Optional, Union +from typing import Union from discord import (Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User) @@ -8,7 +8,7 @@ from redbot.core import commands from redbot.core.utils.chat_formatting import (bold, box, error, humanize_timedelta, warning) -from aurora.models import Moderation, PartialChannel, PartialUser +from aurora.models import Moderation from aurora.utilities.config import config from aurora.utilities.utils import (fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, @@ -104,19 +104,19 @@ async def log_factory( """This function creates a log embed from set parameters, meant for moderation logging. Args: - interaction (Interaction): The interaction object. - case_dict (dict): The case dictionary. + interaction (discord.Interaction): The interaction object. + moderation (aurora.models.Moderation): The moderation object. resolved (bool, optional): Whether the case is resolved or not. Defaults to False. """ - target: Union[PartialUser, PartialChannel] = await moderation.target - moderator: PartialUser = await moderation.moderator + target = await moderation.get_target() + moderator = await moderation.get_moderator() if resolved: embed = Embed( title=f"📕 Case #{moderation.id:,} Resolved", color=await interaction.client.get_embed_color(interaction.channel), ) - resolved_by: Optional[PartialUser] = await moderation.resolved_by_user + resolved_by = await moderation.get_resolved_by() embed.description = f"**Type:** {str.title(moderation.moderation_type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Timestamp:** | " if moderation.duration is not None: diff --git a/poetry.lock b/poetry.lock index 9089f57..0a5918b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -206,17 +206,6 @@ files = [ {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, ] -[[package]] -name = "async-property" -version = "0.2.2" -description = "Python decorator for async properties." -optional = false -python-versions = "*" -files = [ - {file = "async_property-0.2.2-py2.py3-none-any.whl", hash = "sha256:8924d792b5843994537f8ed411165700b27b2bd966cefc4daeefc1253442a9d7"}, - {file = "async_property-0.2.2.tar.gz", hash = "sha256:17d9bd6ca67e27915a75d92549df64b5c7174e9dc806b30a3934dc4ff0506380"}, -] - [[package]] name = "attrs" version = "23.2.0" @@ -2556,4 +2545,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "05c89da1577b4a3507856338502218e0da92dd9785a5fc4a78d6cb59058d887f" +content-hash = "67eb5e616951979332b6f32bcb39d85171cbf8377f566ea1862c51b5068b52f3" diff --git a/pyproject.toml b/pyproject.toml index 7e50d58..91c350e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ websockets = "^12.0" pillow = "^10.3.0" numpy = "^1.26.4" pydantic = "^2.7.1" -async-property = "^0.2.2" [tool.poetry.group.dev] optional = true -- 2.45.2 From 26bf5920c6f1d1f8a7b2bc6b4d03c1de5344835d Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 17:10:26 -0400 Subject: [PATCH 023/146] fix(aurora): mark the bot as a classvar in the moderation model --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 6de0340..8409ee2 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,5 +1,5 @@ from datetime import datetime, timedelta -from typing import Any, Dict, List, Optional, Union +from typing import Any, ClassVar, Dict, List, Optional, Union from discord import Forbidden, HTTPException, InvalidData, NotFound from pydantic import BaseModel @@ -11,7 +11,7 @@ from aurora.utilities.utils import generate_dict class AuroraBaseModel(BaseModel): """Base class for all models in Aurora.""" class Moderation(AuroraBaseModel): - bot: Red + bot: ClassVar[Red] moderation_id: int guild_id: int timestamp: datetime -- 2.45.2 From a3a208b38e10ae8f9153e91b1017829f757f82be Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 17:12:35 -0400 Subject: [PATCH 024/146] fix(aurora): fixed an incorrect function call in Moderation.from_sql() --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 8409ee2..e9f0ca0 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -66,7 +66,7 @@ class Moderation(AuroraBaseModel): result = cursor.fetchone() if result: - case = generate_dict(result) + case = generate_dict(result, guild_id) cursor.close() return cls.from_dict(bot, case) -- 2.45.2 From 2c336ff70c143abea2a605ded05b934ad185e9cb Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 17:15:21 -0400 Subject: [PATCH 025/146] fix(aurora): fixed a faulty expiration check --- aurora/aurora.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 0996fd8..0cc9546 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -1621,7 +1621,7 @@ class Aurora(commands.Cog): if not await self.bot.cog_disabled_in_guild(self, guild): time_per_guild = time.time() - tempban_query = f"SELECT target_id, moderation_id FROM moderation_{guild.id} WHERE end_timestamp != 0 AND end_timestamp <= ? AND moderation_type = 'TEMPBAN' AND expired = 0" + tempban_query = f"SELECT target_id, moderation_id FROM moderation_{guild.id} WHERE end_timestamp IS NOT NULL 0 AND end_timestamp <= ? AND moderation_type = 'TEMPBAN' AND expired = 0" try: cursor.execute(tempban_query, (time.time(),)) @@ -1680,7 +1680,7 @@ class Aurora(commands.Cog): ) removerole_num = 0 - addrole_query = f"SELECT target_id, moderation_id, role_id FROM moderation_{guild.id} WHERE end_timestamp != 0 AND end_timestamp <= ? AND moderation_type = 'ADDROLE' AND expired = 0" + addrole_query = f"SELECT target_id, moderation_id, role_id FROM moderation_{guild.id} WHERE end_timestamp IS NOT NULL AND end_timestamp <= ? AND moderation_type = 'ADDROLE' AND expired = 0" try: cursor.execute(addrole_query, (time.time(),)) result = cursor.fetchall() @@ -1715,7 +1715,7 @@ class Aurora(commands.Cog): continue addrole_num = 0 - removerole_query = f"SELECT target_id, moderation_id, role_id FROM moderation_{guild.id} WHERE end_timestamp != 0 AND end_timestamp <= ? AND moderation_type = 'REMOVEROLE' AND expired = 0" + removerole_query = f"SELECT target_id, moderation_id, role_id FROM moderation_{guild.id} WHERE end_timestamp IS NOT NULL 0 AND end_timestamp <= ? AND moderation_type = 'REMOVEROLE' AND expired = 0" try: cursor.execute(removerole_query, (time.time(),)) result = cursor.fetchall() @@ -1744,7 +1744,7 @@ class Aurora(commands.Cog): logger.error("Adding the role %s to user %s failed due to: \n%s", role_id, target_id, e) continue - expiry_query = f"UPDATE `moderation_{guild.id}` SET expired = 1 WHERE (end_timestamp != 0 AND end_timestamp <= ? AND expired = 0) OR (expired = 0 AND resolved = 1);" + expiry_query = f"UPDATE `moderation_{guild.id}` SET expired = 1 WHERE (end_timestamp IS NOT NULL AND end_timestamp <= ? AND expired = 0) OR (expired = 0 AND resolved = 1);" cursor.execute(expiry_query, (time.time(),)) per_guild_completion_time = (time.time() - time_per_guild) * 1000 -- 2.45.2 From eea66607d9479315a742f7111fc825b7015f4836 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 17:31:16 -0400 Subject: [PATCH 026/146] feat(aurora): finished migrating case_factory to the new model --- aurora/models.py | 32 ++++++++++++++- aurora/utilities/factory.py | 82 +++++++++++++------------------------ 2 files changed, 58 insertions(+), 56 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index e9f0ca0..2561267 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -52,6 +52,11 @@ class Moderation(AuroraBaseModel): return await PartialUser.from_id(self.bot, self.resolved_by) return None + async def get_role(self) -> Optional["PartialRole"]: + if self.role_id: + return await PartialRole.from_id(self.bot, self.guild_id, self.role_id) + return None + def __str__(self): return f"{self.moderation_type} {self.target_type} {self.target_id} {self.reason}" @@ -76,9 +81,9 @@ class Moderation(AuroraBaseModel): def from_dict(cls, bot: Red, data: dict) -> "Moderation": return cls(bot=bot, **data) - def to_json(self, indent: int = None, file: Any = None): + def to_json(self, indent: int = None, file: Any = None, **kwargs): from aurora.utilities.json import dump, dumps - return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent) + return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) class PartialUser(AuroraBaseModel): @@ -127,3 +132,26 @@ class PartialChannel(AuroraBaseModel): if e == Forbidden: return cls(id=channel_id, name="Forbidden Channel") return cls(id=channel_id, name="Deleted Channel") + +class PartialRole(AuroraBaseModel): + id: int + guild_id: int + name: str + + @property + def mention(self): + return f"<@&{self.id}>" + + def __str__(self): + return self.mention + + @classmethod + async def from_id(cls, bot: Red, guild_id: int, role_id: int) -> "PartialRole": + try: + guild = await bot.fetch_guild(guild_id, with_counts=False) + except (Forbidden, HTTPException): + return cls(id=role_id, guild_id=guild_id, name="Forbidden Role") + role = guild.get_role(role_id) + if not role: + return cls(id=role_id, guild_id=guild_id, name="Deleted Role") + return cls(id=role.id, guild_id=guild_id, name=role.name) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index c6506ec..09d9dd3 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -155,83 +155,57 @@ async def log_factory( return embed -async def case_factory(interaction: Interaction, case_dict: dict) -> Embed: +async def case_factory(interaction: Interaction, moderation: Moderation) -> Embed: """This function creates a case embed from set parameters. Args: - interaction (Interaction): The interaction object. - case_dict (dict): The case dictionary. + interaction (discord.Interaction): The interaction object. + moderation (aurora.models.Moderation): The moderation object. """ - if case_dict["target_type"] == "USER": - target_user = await fetch_user_dict(interaction.client, case_dict["target_id"]) - target_name = ( - f"`{target_user['name']}`" - if target_user["discriminator"] == "0" - else f"`{target_user['name']}#{target_user['discriminator']}`" - ) - elif case_dict["target_type"] == "CHANNEL": - target_user = await fetch_channel_dict(interaction.guild, case_dict["target_id"]) - if target_user["mention"]: - target_name = f"{target_user['mention']}" - else: - target_name = f"`{target_user['name']}`" - - moderator_user = await fetch_user_dict(interaction.client, case_dict["moderator_id"]) - moderator_name = ( - f"`{moderator_user['name']}`" - if moderator_user["discriminator"] == "0" - else f"`{moderator_user['name']}#{moderator_user['discriminator']}`" - ) + target = await moderation.get_target() + moderator = await moderation.get_moderator() embed = Embed( - title=f"📕 Case #{case_dict['moderation_id']:,}", + title=f"📕 Case #{moderation.id:,}", color=await interaction.client.get_embed_color(interaction.channel), ) - embed.description = f"**Type:** {str.title(case_dict['moderation_type'])}\n**Target:** {target_name} ({target_user['id']})\n**Moderator:** {moderator_name} ({moderator_user['id']})\n**Resolved:** {bool(case_dict['resolved'])}\n**Timestamp:** | " + embed.description = f"**Type:** {str.title(moderation.type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Resolved:** {moderation.resolved}\n**Timestamp:** | " - if case_dict["duration"] != "NULL": - td = timedelta( - **{ - unit: int(val) - for unit, val in zip( - ["hours", "minutes", "seconds"], case_dict["duration"].split(":") - ) - } - ) + if moderation.duration: duration_embed = ( - f"{humanize_timedelta(timedelta=td)} | " - if bool(case_dict["expired"]) is False - else str(humanize_timedelta(timedelta=td)) + f"{humanize_timedelta(timedelta=moderation.duration)} | " + if moderation.expired is False + else str(humanize_timedelta(timedelta=moderation.duration)) ) - embed.description += f"\n**Duration:** {duration_embed}\n**Expired:** {bool(case_dict['expired'])}" + embed.description += f"\n**Duration:** {duration_embed}\n**Expired:** {moderation.expired}" embed.description += ( - f"\n**Changes:** {len(case_dict['changes']) - 1}" - if case_dict["changes"] + f"\n**Changes:** {len(moderation.changes) - 1}" + if moderation.changes else "\n**Changes:** 0" ) - if case_dict["role_id"]: - embed.description += f"\n**Role:** <@&{case_dict['role_id']}>" + if moderation.role_id: + role = await moderation.get_role() + embed.description += f"\n**Role:** {role.name}" - if case_dict["metadata"]: - if case_dict["metadata"]["imported_from"]: + if moderation.metadata: + if moderation.metadata["imported_from"]: embed.description += ( - f"\n**Imported From:** {case_dict['metadata']['imported_from']}" + f"\n**Imported From:** {moderation.metadata['imported_from']}" + ) + if moderation.metadata["imported_timestamp"]: + embed.description += ( + f"\n**Imported Timestamp:** | " ) - embed.add_field(name="Reason", value=box(case_dict["reason"]), inline=False) + embed.add_field(name="Reason", value=box(moderation.reason), inline=False) - if case_dict["resolved"] == 1: - resolved_user = await fetch_user_dict(interaction.client, case_dict["resolved_by"]) - resolved_name = ( - f"`{resolved_user['name']}`" - if resolved_user["discriminator"] == "0" - else f"`{resolved_user['name']}#{resolved_user['discriminator']}`" - ) + if moderation.resolved: + resolved_user = await moderation.get_resolved_by() embed.add_field( name="Resolve Reason", - value=f"Resolved by {resolved_name} ({resolved_user['id']}) for:\n{box(case_dict['resolve_reason'])}", + value=f"Resolved by {resolved_user.name} ({resolved_user.id}) for:\n{box(moderation.resolve_reason)}", inline=False, ) -- 2.45.2 From e7e8d60f3b56d33baead9915aa70a52b4c827453 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 17:48:08 -0400 Subject: [PATCH 027/146] feat(aurora): added a new object for Changes --- aurora/models.py | 23 ++++++++++++++++++++++- aurora/utilities/factory.py | 22 +++++++++------------- aurora/utilities/utils.py | 11 ++++++++++- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 2561267..6fd3862 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,5 +1,5 @@ from datetime import datetime, timedelta -from typing import Any, ClassVar, Dict, List, Optional, Union +from typing import Any, ClassVar, Dict, List, Literal, Optional, Union from discord import Forbidden, HTTPException, InvalidData, NotFound from pydantic import BaseModel @@ -10,6 +10,11 @@ from aurora.utilities.utils import generate_dict class AuroraBaseModel(BaseModel): """Base class for all models in Aurora.""" + + def to_json(self, indent: int = None, file: Any = None, **kwargs): + from aurora.utilities.json import dump, dumps + return dump(self.model_dump(), file, indent=indent, **kwargs) if file else dumps(self.model_dump(), indent=indent, **kwargs) + class Moderation(AuroraBaseModel): bot: ClassVar[Red] moderation_id: int @@ -86,6 +91,21 @@ class Moderation(AuroraBaseModel): return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) +class Change(AuroraBaseModel): + type: Literal["ORIGINAL", "RESOLVE", "EDIT"] + timestamp: datetime + reason: str + user_id: int + duration: Optional[timedelta] + end_timestamp: Optional[datetime] + + def __str__(self): + return f"{self.type} {self.user_id} {self.reason}" + + @classmethod + def from_dict(cls, data: dict) -> "Change": + return cls(**data) + class PartialUser(AuroraBaseModel): id: int username: str @@ -108,6 +128,7 @@ class PartialUser(AuroraBaseModel): except NotFound: return cls(id=user_id, username="Deleted User", discriminator=0) + class PartialChannel(AuroraBaseModel): id: int name: str diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 09d9dd3..823f05c 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -2,17 +2,13 @@ from datetime import datetime, timedelta from typing import Union -from discord import (Color, Embed, Guild, Interaction, InteractionMessage, - Member, Role, User) +from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User from redbot.core import commands -from redbot.core.utils.chat_formatting import (bold, box, error, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning from aurora.models import Moderation from aurora.utilities.config import config -from aurora.utilities.utils import (fetch_channel_dict, fetch_user_dict, - get_bool_emoji, get_next_case_number, - get_pagesize_str) +from aurora.utilities.utils import fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, get_pagesize_str async def message_factory( @@ -212,22 +208,22 @@ async def case_factory(interaction: Interaction, moderation: Moderation) -> Embe return embed -async def changes_factory(interaction: Interaction, case_dict: dict) -> Embed: +async def changes_factory(interaction: Interaction, moderation: Moderation) -> Embed: """This function creates a changes embed from set parameters. Args: - interaction (Interaction): The interaction object. - case_dict (dict): The case dictionary. + interaction (discord.Interaction): The interaction object. + moderation (aurora.models.Moderation): The moderation object. """ embed = Embed( - title=f"📕 Case #{case_dict['moderation_id']:,} Changes", + title=f"📕 Case #{moderation.id:,} Changes", color=await interaction.client.get_embed_color(interaction.channel), ) memory_dict = {} - if case_dict["changes"]: - for change in case_dict["changes"]: + if moderation.changes: + for change in moderation.changes: if change["user_id"] not in memory_dict: memory_dict[str(change["user_id"])] = await fetch_user_dict( interaction.client, change["user_id"] diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 3761543..5f7de72 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -9,9 +9,11 @@ from discord.errors import Forbidden, NotFound from redbot.core import commands, data_manager from redbot.core.utils.chat_formatting import error +from aurora.models import Change from aurora.utilities.config import config + def check_permissions( user: User, permissions: list, @@ -130,6 +132,13 @@ def generate_dict(result: dict, guild_id: int) -> dict: duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) else: duration = None + + if result[14] is not None: + changes = json.loads(result[14].strip('"').replace('\\"', '"')) + change_obj_list = [] + for change in changes: + change_obj_list.append(Change(**change)) + case = { "moderation_id": int(result[0]), "guild_id": int(guild_id), @@ -146,7 +155,7 @@ def generate_dict(result: dict, guild_id: int) -> dict: "resolved_by": result[11], "resolve_reason": result[12], "expired": bool(result[13]), - "changes": json.loads(result[14].strip('"').replace('\\"', '"')) if result[14] else [], + "changes": change_obj_list if result[14] else [], "metadata": json.loads(result[15].strip('"').replace('\\"', '"')) if result[15] else {}, } return case -- 2.45.2 From e46612dc08a75db8fe789243b3c77a58793a9688 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 17:49:01 -0400 Subject: [PATCH 028/146] fix(aurora): fixed a circular import --- aurora/utilities/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 5f7de72..874ade3 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -9,11 +9,9 @@ from discord.errors import Forbidden, NotFound from redbot.core import commands, data_manager from redbot.core.utils.chat_formatting import error -from aurora.models import Change from aurora.utilities.config import config - def check_permissions( user: User, permissions: list, @@ -127,6 +125,7 @@ async def get_next_case_number(guild_id: str, cursor=None) -> int: def generate_dict(result: dict, guild_id: int) -> dict: + from aurora.models import Change if result[7] is not None: hours, minutes, seconds = map(int, result[7].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) -- 2.45.2 From 39479eb216bac3e9c0f9201ccd5ab6cffa95dbe2 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:05:20 -0400 Subject: [PATCH 029/146] fix(aurora): fixing up some model stuff --- aurora/models.py | 61 +++++++++++++++++++-------------------- aurora/utilities/utils.py | 5 ++-- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 6fd3862..c21ff4c 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -10,29 +10,29 @@ from aurora.utilities.utils import generate_dict class AuroraBaseModel(BaseModel): """Base class for all models in Aurora.""" + bot: ClassVar[Red] + guild_id: int def to_json(self, indent: int = None, file: Any = None, **kwargs): from aurora.utilities.json import dump, dumps - return dump(self.model_dump(), file, indent=indent, **kwargs) if file else dumps(self.model_dump(), indent=indent, **kwargs) + return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) class Moderation(AuroraBaseModel): - bot: ClassVar[Red] moderation_id: int - guild_id: int timestamp: datetime moderation_type: str target_type: str target_id: int moderator_id: int - role_id: Optional[int] - duration: Optional[timedelta] - end_timestamp: Optional[datetime] - reason: Optional[str] + role_id: Optional[int] = None + duration: Optional[timedelta] = None + end_timestamp: Optional[datetime] = None + reason: Optional[str] = None resolved: bool - resolved_by: Optional[int] - resolve_reason: Optional[str] + resolved_by: Optional[int] = None + resolve_reason: Optional[str] = None expired: bool - changes: List[Dict] + changes: List["Change"] metadata: Dict @property @@ -76,7 +76,7 @@ class Moderation(AuroraBaseModel): result = cursor.fetchone() if result: - case = generate_dict(result, guild_id) + case = generate_dict(bot, result, guild_id) cursor.close() return cls.from_dict(bot, case) @@ -86,25 +86,23 @@ class Moderation(AuroraBaseModel): def from_dict(cls, bot: Red, data: dict) -> "Moderation": return cls(bot=bot, **data) - def to_json(self, indent: int = None, file: Any = None, **kwargs): - from aurora.utilities.json import dump, dumps - return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) - - class Change(AuroraBaseModel): type: Literal["ORIGINAL", "RESOLVE", "EDIT"] timestamp: datetime reason: str user_id: int - duration: Optional[timedelta] - end_timestamp: Optional[datetime] + duration: Optional[timedelta] = None + end_timestamp: Optional[datetime] = None def __str__(self): return f"{self.type} {self.user_id} {self.reason}" + async def get_user(self) -> "PartialUser": + return await PartialUser.from_id(self.bot, self.user_id) + @classmethod - def from_dict(cls, data: dict) -> "Change": - return cls(**data) + def from_dict(cls, bot: Red, data: dict) -> "Change": + return cls(bot=bot, **data) class PartialUser(AuroraBaseModel): id: int @@ -124,9 +122,9 @@ class PartialUser(AuroraBaseModel): if not user: try: user = await bot.fetch_user(user_id) - return cls(id=user.id, username=user.name, discriminator=user.discriminator) + return cls(bot=bot, guild_id=0, id=user.id, username=user.name, discriminator=user.discriminator) except NotFound: - return cls(id=user_id, username="Deleted User", discriminator=0) + return cls(bot=bot, guild_id=0, id=user_id, username="Deleted User", discriminator=0) class PartialChannel(AuroraBaseModel): @@ -144,19 +142,18 @@ class PartialChannel(AuroraBaseModel): @classmethod async def from_id(cls, bot: Red, channel_id: int) -> "PartialChannel": - user = bot.get_channel(channel_id) - if not user: + channel = bot.get_channel(channel_id) + if not channel: try: - user = await bot.fetch_channel(channel_id) - return cls(id=user.id, username=user.name, discriminator=user.discriminator) + channel = await bot.fetch_channel(channel_id) + return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) except (NotFound, InvalidData, HTTPException, Forbidden) as e: if e == Forbidden: - return cls(id=channel_id, name="Forbidden Channel") - return cls(id=channel_id, name="Deleted Channel") + return cls(bot=bot, guild_id=0, id=channel_id, name="Forbidden Channel") + return cls(bot=bot, guild_id=0, id=channel_id, name="Deleted Channel") class PartialRole(AuroraBaseModel): id: int - guild_id: int name: str @property @@ -171,8 +168,8 @@ class PartialRole(AuroraBaseModel): try: guild = await bot.fetch_guild(guild_id, with_counts=False) except (Forbidden, HTTPException): - return cls(id=role_id, guild_id=guild_id, name="Forbidden Role") + return cls(bot=bot, guild_id=guild_id, id=role_id, name="Forbidden Role") role = guild.get_role(role_id) if not role: - return cls(id=role_id, guild_id=guild_id, name="Deleted Role") - return cls(id=role.id, guild_id=guild_id, name=role.name) + return cls(bot=bot, guild_id=guild_id, id=role_id, name="Deleted Role") + return cls(bot=bot, guild_id=guild_id, id=role.id, name=role.name) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 874ade3..f6e69bf 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -7,6 +7,7 @@ from dateutil.relativedelta import relativedelta as rd from discord import File, Guild, Interaction, Member, SelectOption, User from discord.errors import Forbidden, NotFound from redbot.core import commands, data_manager +from redbot.core.bot import Red from redbot.core.utils.chat_formatting import error from aurora.utilities.config import config @@ -124,7 +125,7 @@ async def get_next_case_number(guild_id: str, cursor=None) -> int: return (result[0] + 1) if result else 1 -def generate_dict(result: dict, guild_id: int) -> dict: +def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: from aurora.models import Change if result[7] is not None: hours, minutes, seconds = map(int, result[7].split(':')) @@ -136,7 +137,7 @@ def generate_dict(result: dict, guild_id: int) -> dict: changes = json.loads(result[14].strip('"').replace('\\"', '"')) change_obj_list = [] for change in changes: - change_obj_list.append(Change(**change)) + change_obj_list.append(Change.from_dict(bot=bot, **change)) case = { "moderation_id": int(result[0]), -- 2.45.2 From b752181781aa497360e2c0887e9499a0abce1835 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:06:23 -0400 Subject: [PATCH 030/146] fix(aurora): oops lol --- aurora/utilities/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index f6e69bf..b15a865 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -137,7 +137,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: changes = json.loads(result[14].strip('"').replace('\\"', '"')) change_obj_list = [] for change in changes: - change_obj_list.append(Change.from_dict(bot=bot, **change)) + change_obj_list.append(Change.from_dict(bot=bot, data=change)) case = { "moderation_id": int(result[0]), -- 2.45.2 From 1313834ea51ff21232a3d83f387fcf4db7b68311 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:07:29 -0400 Subject: [PATCH 031/146] fix(aurora): fixed a pydantic error --- aurora/models.py | 2 +- aurora/utilities/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index c21ff4c..c3ef428 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -101,7 +101,7 @@ class Change(AuroraBaseModel): return await PartialUser.from_id(self.bot, self.user_id) @classmethod - def from_dict(cls, bot: Red, data: dict) -> "Change": + def from_dict(cls, bot: Red, guild_id: int, data: dict) -> "Change": return cls(bot=bot, **data) class PartialUser(AuroraBaseModel): diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index b15a865..71e57ff 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -137,7 +137,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: changes = json.loads(result[14].strip('"').replace('\\"', '"')) change_obj_list = [] for change in changes: - change_obj_list.append(Change.from_dict(bot=bot, data=change)) + change_obj_list.append(Change.from_dict(bot=bot, guild_id=guild_id, data=change)) case = { "moderation_id": int(result[0]), -- 2.45.2 From 74a098341932e83cca96000e2c4a07945b8f4b4a Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:08:27 -0400 Subject: [PATCH 032/146] fix(aurora): fixed a missing kwarg in a model initialization function --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index c3ef428..3fbd6dd 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -102,7 +102,7 @@ class Change(AuroraBaseModel): @classmethod def from_dict(cls, bot: Red, guild_id: int, data: dict) -> "Change": - return cls(bot=bot, **data) + return cls(bot=bot, guild_id=guild_id, **data) class PartialUser(AuroraBaseModel): id: int -- 2.45.2 From 356d58f9d7247cd07d51d42c8eaf5a2fdc910aae Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:09:06 -0400 Subject: [PATCH 033/146] fix(aurora): changed the jsonencoder --- aurora/utilities/json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index e8678af..aad7be7 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -11,7 +11,7 @@ class JSONEncoder(json.JSONEncoder): if isinstance(o, timedelta): return str(o) if isinstance(o, AuroraBaseModel): - return o.model_dump() + return o.to_json() return super().default(o) -- 2.45.2 From d70f2bf5f1eac1300e0278f891512de9aa2209cb Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:17:21 -0400 Subject: [PATCH 034/146] fix(aurora): fixed guild ids appearing in changes --- aurora/models.py | 25 ++++++++++++++++--------- aurora/utilities/utils.py | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 3fbd6dd..012eaaf 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -11,13 +11,20 @@ from aurora.utilities.utils import generate_dict class AuroraBaseModel(BaseModel): """Base class for all models in Aurora.""" bot: ClassVar[Red] + + def to_json(self, indent: int = None, file: Any = None, **kwargs): + from aurora.utilities.json import dump, dumps + return dump(self.model_dump(exclude={"bot"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) + +class AuroraGuildModel(AuroraBaseModel): + """Subclass of AuroraBaseModel that includes a guild_id attribute, and a modified to_json() method to match.""" guild_id: int def to_json(self, indent: int = None, file: Any = None, **kwargs): from aurora.utilities.json import dump, dumps return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) -class Moderation(AuroraBaseModel): +class Moderation(AuroraGuildModel): moderation_id: int timestamp: datetime moderation_type: str @@ -101,8 +108,8 @@ class Change(AuroraBaseModel): return await PartialUser.from_id(self.bot, self.user_id) @classmethod - def from_dict(cls, bot: Red, guild_id: int, data: dict) -> "Change": - return cls(bot=bot, guild_id=guild_id, **data) + def from_dict(cls, bot: Red, data: dict) -> "Change": + return cls(bot=bot **data) class PartialUser(AuroraBaseModel): id: int @@ -122,12 +129,12 @@ class PartialUser(AuroraBaseModel): if not user: try: user = await bot.fetch_user(user_id) - return cls(bot=bot, guild_id=0, id=user.id, username=user.name, discriminator=user.discriminator) + return cls(bot=bot, id=user.id, username=user.name, discriminator=user.discriminator) except NotFound: - return cls(bot=bot, guild_id=0, id=user_id, username="Deleted User", discriminator=0) + return cls(bot=bot, id=user_id, username="Deleted User", discriminator=0) -class PartialChannel(AuroraBaseModel): +class PartialChannel(AuroraGuildModel): id: int name: str @@ -149,10 +156,10 @@ class PartialChannel(AuroraBaseModel): return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) except (NotFound, InvalidData, HTTPException, Forbidden) as e: if e == Forbidden: - return cls(bot=bot, guild_id=0, id=channel_id, name="Forbidden Channel") - return cls(bot=bot, guild_id=0, id=channel_id, name="Deleted Channel") + return cls(bot=bot, guild_id=None, id=channel_id, name="Forbidden Channel") + return cls(bot=bot, guild_id=None, id=channel_id, name="Deleted Channel") -class PartialRole(AuroraBaseModel): +class PartialRole(AuroraGuildModel): id: int name: str diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 71e57ff..b15a865 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -137,7 +137,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: changes = json.loads(result[14].strip('"').replace('\\"', '"')) change_obj_list = [] for change in changes: - change_obj_list.append(Change.from_dict(bot=bot, guild_id=guild_id, data=change)) + change_obj_list.append(Change.from_dict(bot=bot, data=change)) case = { "moderation_id": int(result[0]), -- 2.45.2 From ea280c2d62b280abefb995d6a78309bd199bea94 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:18:57 -0400 Subject: [PATCH 035/146] fix(aurora): fixed a syntax error --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 012eaaf..f0b8590 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -109,7 +109,7 @@ class Change(AuroraBaseModel): @classmethod def from_dict(cls, bot: Red, data: dict) -> "Change": - return cls(bot=bot **data) + return cls(bot=bot, **data) class PartialUser(AuroraBaseModel): id: int -- 2.45.2 From 6520f4f2b91ffb12530b9cd493d7346825b42970 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:22:42 -0400 Subject: [PATCH 036/146] fix(aurora): don't inherit from AuroraBaseModel --- aurora/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index f0b8590..c90d674 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -16,8 +16,9 @@ class AuroraBaseModel(BaseModel): from aurora.utilities.json import dump, dumps return dump(self.model_dump(exclude={"bot"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) -class AuroraGuildModel(AuroraBaseModel): - """Subclass of AuroraBaseModel that includes a guild_id attribute, and a modified to_json() method to match.""" +class AuroraGuildModel(BaseModel): + """Base class that includes a guild_id attribute, and a modified to_json() method to match.""" + bot: ClassVar[Red] guild_id: int def to_json(self, indent: int = None, file: Any = None, **kwargs): -- 2.45.2 From a7d8f452d11ca8a4fa52021b073c98891ad2fd32 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:23:24 -0400 Subject: [PATCH 037/146] fix(aurora): updated utilities.json.JSONEncoder to match the model change I just made --- aurora/utilities/json.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index aad7be7..7052f90 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -1,7 +1,7 @@ import json from datetime import datetime, timedelta -from aurora.models import AuroraBaseModel +from aurora.models import AuroraBaseModel, AuroraGuildModel class JSONEncoder(json.JSONEncoder): @@ -12,6 +12,8 @@ class JSONEncoder(json.JSONEncoder): return str(o) if isinstance(o, AuroraBaseModel): return o.to_json() + if isinstance(o, AuroraGuildModel): + return o.to_json() return super().default(o) -- 2.45.2 From 6a7758e8f933c29b56943f2f91c994a2729e4ee4 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:27:12 -0400 Subject: [PATCH 038/146] fix(aurora): reverted previous change, instead using pydantic's ConfigDicts to resolve the error I was encountering previously --- aurora/models.py | 12 ++++++------ aurora/utilities/factory.py | 10 +++++++--- aurora/utilities/json.py | 4 +--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index c90d674..a50aba8 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,8 +1,8 @@ from datetime import datetime, timedelta -from typing import Any, ClassVar, Dict, List, Literal, Optional, Union +from typing import Any, Dict, List, Literal, Optional, Union from discord import Forbidden, HTTPException, InvalidData, NotFound -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from redbot.core.bot import Red from aurora.utilities.utils import generate_dict @@ -10,15 +10,15 @@ from aurora.utilities.utils import generate_dict class AuroraBaseModel(BaseModel): """Base class for all models in Aurora.""" - bot: ClassVar[Red] + model_config = ConfigDict(ignored_types=(Red,)) + bot: Red def to_json(self, indent: int = None, file: Any = None, **kwargs): from aurora.utilities.json import dump, dumps return dump(self.model_dump(exclude={"bot"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) -class AuroraGuildModel(BaseModel): - """Base class that includes a guild_id attribute, and a modified to_json() method to match.""" - bot: ClassVar[Red] +class AuroraGuildModel(AuroraBaseModel): + """Subclass of AuroraBaseModel that includes a guild_id attribute, and a modified to_json() method to match.""" guild_id: int def to_json(self, indent: int = None, file: Any = None, **kwargs): diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 823f05c..fdc4126 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -2,13 +2,17 @@ from datetime import datetime, timedelta from typing import Union -from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User +from discord import (Color, Embed, Guild, Interaction, InteractionMessage, + Member, Role, User) from redbot.core import commands -from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (bold, box, error, + humanize_timedelta, warning) from aurora.models import Moderation from aurora.utilities.config import config -from aurora.utilities.utils import fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, get_pagesize_str +from aurora.utilities.utils import (fetch_channel_dict, fetch_user_dict, + get_bool_emoji, get_next_case_number, + get_pagesize_str) async def message_factory( diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index 7052f90..aad7be7 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -1,7 +1,7 @@ import json from datetime import datetime, timedelta -from aurora.models import AuroraBaseModel, AuroraGuildModel +from aurora.models import AuroraBaseModel class JSONEncoder(json.JSONEncoder): @@ -12,8 +12,6 @@ class JSONEncoder(json.JSONEncoder): return str(o) if isinstance(o, AuroraBaseModel): return o.to_json() - if isinstance(o, AuroraGuildModel): - return o.to_json() return super().default(o) -- 2.45.2 From a22de1d2c2b344577400eb12e486b68bece42f30 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 18:28:03 -0400 Subject: [PATCH 039/146] fix(aurora): fixed another pydantic error --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index a50aba8..2c74015 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -10,7 +10,7 @@ from aurora.utilities.utils import generate_dict class AuroraBaseModel(BaseModel): """Base class for all models in Aurora.""" - model_config = ConfigDict(ignored_types=(Red,)) + model_config = ConfigDict(ignored_types=(Red,), arbitrary_types_allowed=True) bot: Red def to_json(self, indent: int = None, file: Any = None, **kwargs): -- 2.45.2 From 3f6aec0a82d05c648750c8936b0ad6af3fd0caa8 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 20:55:11 -0400 Subject: [PATCH 040/146] fix(aurora): don't JSON serialize the Red class --- aurora/utilities/json.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index aad7be7..cad264e 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -1,6 +1,9 @@ import json from datetime import datetime, timedelta +from redbot.core.bot import Red + + from aurora.models import AuroraBaseModel @@ -12,6 +15,8 @@ class JSONEncoder(json.JSONEncoder): return str(o) if isinstance(o, AuroraBaseModel): return o.to_json() + if isinstance(o, Red): + return None return super().default(o) -- 2.45.2 From 0b31e70089b7698668d355805f5e77ba5d98bc3c Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 20:59:23 -0400 Subject: [PATCH 041/146] fix(aurora): fixed a conditional statement and a pydantic issue --- aurora/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 2c74015..be0c0e2 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -55,7 +55,7 @@ class Moderation(AuroraGuildModel): return await PartialUser.from_id(self.bot, self.moderator_id) async def get_target(self) -> Union["PartialUser", "PartialChannel"]: - if self.target_type == "user": + if self.target_type == "USER": return await PartialUser.from_id(self.bot, self.target_id) else: return await PartialChannel.from_id(self.bot, self.target_id) @@ -157,8 +157,8 @@ class PartialChannel(AuroraGuildModel): return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) except (NotFound, InvalidData, HTTPException, Forbidden) as e: if e == Forbidden: - return cls(bot=bot, guild_id=None, id=channel_id, name="Forbidden Channel") - return cls(bot=bot, guild_id=None, id=channel_id, name="Deleted Channel") + return cls(bot=bot, guild_id=0, id=channel_id, name="Forbidden Channel") + return cls(bot=bot, guild_id=0, id=channel_id, name="Deleted Channel") class PartialRole(AuroraGuildModel): id: int -- 2.45.2 From 23eba46948b1ee96f6dc0768c4af38e7991a9b84 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 21:02:18 -0400 Subject: [PATCH 042/146] fix(aurora): fix a few classmethods returning None instead of their constructed classes --- aurora/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aurora/models.py b/aurora/models.py index be0c0e2..b336d3a 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -133,6 +133,7 @@ class PartialUser(AuroraBaseModel): return cls(bot=bot, id=user.id, username=user.name, discriminator=user.discriminator) except NotFound: return cls(bot=bot, id=user_id, username="Deleted User", discriminator=0) + return cls(bot=bot, id=user.id, username=user.name, discriminator=user.discriminator) class PartialChannel(AuroraGuildModel): @@ -159,6 +160,7 @@ class PartialChannel(AuroraGuildModel): if e == Forbidden: return cls(bot=bot, guild_id=0, id=channel_id, name="Forbidden Channel") return cls(bot=bot, guild_id=0, id=channel_id, name="Deleted Channel") + return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) class PartialRole(AuroraGuildModel): id: int -- 2.45.2 From 50c2db80d9f3df344399d9ea0477ca04fcb5d759 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 21:06:29 -0400 Subject: [PATCH 043/146] fix(aurora): convert datetimes to unix timestamps --- aurora/models.py | 4 ++++ aurora/utilities/factory.py | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index b336d3a..ab8bd3d 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -51,6 +51,10 @@ class Moderation(AuroraGuildModel): def type(self) -> str: return self.moderation_type + @property + def unix_timestamp(self) -> int: + return int(self.timestamp.timestamp()) + async def get_moderator(self) -> "PartialUser": return await PartialUser.from_id(self.bot, self.moderator_id) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index fdc4126..63e8f03 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -117,7 +117,7 @@ async def log_factory( ) resolved_by = await moderation.get_resolved_by() - embed.description = f"**Type:** {str.title(moderation.moderation_type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Timestamp:** | " + embed.description = f"**Type:** {str.title(moderation.moderation_type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Timestamp:** | " if moderation.duration is not None: duration_embed = ( @@ -143,12 +143,12 @@ async def log_factory( title=f"📕 Case #{moderation.id:,}", color=await interaction.client.get_embed_color(interaction.channel), ) - embed.description = f"**Type:** {str.title(moderation.type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Timestamp:** | " + embed.description = f"**Type:** {str.title(moderation.type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Timestamp:** | " if moderation.duration: embed.description = ( embed.description - + f"\n**Duration:** {humanize_timedelta(timedelta=moderation.duration)} | " + + f"\n**Duration:** {humanize_timedelta(timedelta=moderation.duration)} | " ) embed.add_field(name="Reason", value=box(moderation.reason), inline=False) @@ -169,11 +169,11 @@ async def case_factory(interaction: Interaction, moderation: Moderation) -> Embe title=f"📕 Case #{moderation.id:,}", color=await interaction.client.get_embed_color(interaction.channel), ) - embed.description = f"**Type:** {str.title(moderation.type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Resolved:** {moderation.resolved}\n**Timestamp:** | " + embed.description = f"**Type:** {str.title(moderation.type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Resolved:** {moderation.resolved}\n**Timestamp:** | " if moderation.duration: duration_embed = ( - f"{humanize_timedelta(timedelta=moderation.duration)} | " + f"{humanize_timedelta(timedelta=moderation.duration)} | " if moderation.expired is False else str(humanize_timedelta(timedelta=moderation.duration)) ) -- 2.45.2 From ee331b7544c6f937687e31cceb01f39e28c4ca6f Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 21:07:37 -0400 Subject: [PATCH 044/146] fix(aurora): fixed an incorrect conditional statement resulting in unintended behavior --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index ab8bd3d..069bc40 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -123,7 +123,7 @@ class PartialUser(AuroraBaseModel): @property def name(self): - return f"{self.username}#{self.discriminator}" if self.discriminator == 0 else self.username + return f"{self.username}#{self.discriminator}" if self.discriminator != 0 else self.username def __str__(self): return self.name -- 2.45.2 From 94f6d6c3b588cc7de63b533d1f1773106d4717df Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 21:15:39 -0400 Subject: [PATCH 045/146] fix(aurora): fixed the changes_factory --- aurora/models.py | 14 ++++++++++++++ aurora/utilities/factory.py | 38 ++++++++++++++----------------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 069bc40..a7b81a7 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -106,6 +106,10 @@ class Change(AuroraBaseModel): duration: Optional[timedelta] = None end_timestamp: Optional[datetime] = None + @property + def unix_timestamp(self) -> int: + return int(self.timestamp.timestamp()) + def __str__(self): return f"{self.type} {self.user_id} {self.reason}" @@ -114,6 +118,16 @@ class Change(AuroraBaseModel): @classmethod def from_dict(cls, bot: Red, data: dict) -> "Change": + if data["duration"] is not None: + hours, minutes, seconds = map(int, data["duration"].split(':')) + duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) + else: + duration = None + data.update({ + "timestamp": datetime.fromtimestamp(data["timestamp"]), + "end_timestamp": datetime.fromtimestamp(data["end_timestamp"]) if data["end_timestamp"] else None, + "duration": duration + }) return cls(bot=bot, **data) class PartialUser(AuroraBaseModel): diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 63e8f03..4a0945e 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -2,17 +2,13 @@ from datetime import datetime, timedelta from typing import Union -from discord import (Color, Embed, Guild, Interaction, InteractionMessage, - Member, Role, User) +from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User from redbot.core import commands -from redbot.core.utils.chat_formatting import (bold, box, error, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning from aurora.models import Moderation from aurora.utilities.config import config -from aurora.utilities.utils import (fetch_channel_dict, fetch_user_dict, - get_bool_emoji, get_next_case_number, - get_pagesize_str) +from aurora.utilities.utils import fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, get_pagesize_str async def message_factory( @@ -228,38 +224,32 @@ async def changes_factory(interaction: Interaction, moderation: Moderation) -> E if moderation.changes: for change in moderation.changes: - if change["user_id"] not in memory_dict: - memory_dict[str(change["user_id"])] = await fetch_user_dict( - interaction.client, change["user_id"] - ) + if change.user_id not in memory_dict: + memory_dict[str(change.user_id)] = await change.get_user() - user = memory_dict[str(change["user_id"])] - name = ( - user["name"] - if user["discriminator"] == "0" - else f"{user['name']}#{user['discriminator']}" - ) + user = memory_dict[str(change.user_id)] + name = user["name"] - timestamp = f" | " + timestamp = f" | " - if change["type"] == "ORIGINAL": + if change.type == "ORIGINAL": embed.add_field( name="Original", - value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change['reason']}\n**Timestamp:** {timestamp}", + value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change.reason}\n**Timestamp:** {timestamp}", inline=False, ) - elif change["type"] == "EDIT": + elif change.type == "EDIT": embed.add_field( name="Edit", - value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change['reason']}\n**Timestamp:** {timestamp}", + value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change.reason}\n**Timestamp:** {timestamp}", inline=False, ) - elif change["type"] == "RESOLVE": + elif change.type == "RESOLVE": embed.add_field( name="Resolve", - value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change['reason']}\n**Timestamp:** {timestamp}", + value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change.reason}\n**Timestamp:** {timestamp}", inline=False, ) -- 2.45.2 From 6eeab9ed96ae1f1fd2c92e7d4b2745ea287aa060 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 21:17:03 -0400 Subject: [PATCH 046/146] fix(aurora): fixed a keyerror --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index a7b81a7..f00f0b5 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -118,14 +118,14 @@ class Change(AuroraBaseModel): @classmethod def from_dict(cls, bot: Red, data: dict) -> "Change": - if data["duration"] is not None: + if "duration" in data and data["duration"] is not None: hours, minutes, seconds = map(int, data["duration"].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) else: duration = None data.update({ "timestamp": datetime.fromtimestamp(data["timestamp"]), - "end_timestamp": datetime.fromtimestamp(data["end_timestamp"]) if data["end_timestamp"] else None, + "end_timestamp": datetime.fromtimestamp(data["end_timestamp"]) if "end_timestamp" in data and data["end_timestamp"] else None, "duration": duration }) return cls(bot=bot, **data) -- 2.45.2 From a8414a7918c5da3878d7ba2e7491c6144808a5f0 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 21:19:06 -0400 Subject: [PATCH 047/146] fix(aurora): fixed some subscripting errors in changes_factory --- aurora/utilities/factory.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 4a0945e..4d15092 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -2,13 +2,17 @@ from datetime import datetime, timedelta from typing import Union -from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User +from discord import (Color, Embed, Guild, Interaction, InteractionMessage, + Member, Role, User) from redbot.core import commands -from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (bold, box, error, + humanize_timedelta, warning) -from aurora.models import Moderation +from aurora.models import Moderation, PartialUser from aurora.utilities.config import config -from aurora.utilities.utils import fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, get_pagesize_str +from aurora.utilities.utils import (fetch_channel_dict, fetch_user_dict, + get_bool_emoji, get_next_case_number, + get_pagesize_str) async def message_factory( @@ -227,29 +231,28 @@ async def changes_factory(interaction: Interaction, moderation: Moderation) -> E if change.user_id not in memory_dict: memory_dict[str(change.user_id)] = await change.get_user() - user = memory_dict[str(change.user_id)] - name = user["name"] + user: PartialUser = memory_dict[str(change.user_id)] timestamp = f" | " if change.type == "ORIGINAL": embed.add_field( name="Original", - value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change.reason}\n**Timestamp:** {timestamp}", + value=f"**User:** `{user.name}` ({user.id})\n**Reason:** {change.reason}\n**Timestamp:** {timestamp}", inline=False, ) elif change.type == "EDIT": embed.add_field( name="Edit", - value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change.reason}\n**Timestamp:** {timestamp}", + value=f"**User:** `{user.name}` ({user.id})\n**Reason:** {change.reason}\n**Timestamp:** {timestamp}", inline=False, ) elif change.type == "RESOLVE": embed.add_field( name="Resolve", - value=f"**User:** `{name}` ({user['id']})\n**Reason:** {change.reason}\n**Timestamp:** {timestamp}", + value=f"**User:** `{user.name}` ({user.id})\n**Reason:** {change.reason}\n**Timestamp:** {timestamp}", inline=False, ) -- 2.45.2 From e89db3de5a7334327f32de6353e8f9bb87b2d975 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 21:20:05 -0400 Subject: [PATCH 048/146] misc(aurora): added some markdown formatting --- aurora/utilities/factory.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 4d15092..83bd7f1 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -2,17 +2,13 @@ from datetime import datetime, timedelta from typing import Union -from discord import (Color, Embed, Guild, Interaction, InteractionMessage, - Member, Role, User) +from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User from redbot.core import commands -from redbot.core.utils.chat_formatting import (bold, box, error, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning from aurora.models import Moderation, PartialUser from aurora.utilities.config import config -from aurora.utilities.utils import (fetch_channel_dict, fetch_user_dict, - get_bool_emoji, get_next_case_number, - get_pagesize_str) +from aurora.utilities.utils import fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, get_pagesize_str async def message_factory( @@ -169,7 +165,7 @@ async def case_factory(interaction: Interaction, moderation: Moderation) -> Embe title=f"📕 Case #{moderation.id:,}", color=await interaction.client.get_embed_color(interaction.channel), ) - embed.description = f"**Type:** {str.title(moderation.type)}\n**Target:** {target.name} ({target.id})\n**Moderator:** {moderator.name} ({moderator.id})\n**Resolved:** {moderation.resolved}\n**Timestamp:** | " + embed.description = f"**Type:** {str.title(moderation.type)}\n**Target:** `{target.name}` ({target.id})\n**Moderator:** `{moderator.name}` ({moderator.id})\n**Resolved:** {moderation.resolved}\n**Timestamp:** | " if moderation.duration: duration_embed = ( @@ -205,7 +201,7 @@ async def case_factory(interaction: Interaction, moderation: Moderation) -> Embe resolved_user = await moderation.get_resolved_by() embed.add_field( name="Resolve Reason", - value=f"Resolved by {resolved_user.name} ({resolved_user.id}) for:\n{box(moderation.resolve_reason)}", + value=f"Resolved by `{resolved_user.name}` ({resolved_user.id}) for:\n{box(moderation.resolve_reason)}", inline=False, ) -- 2.45.2 From fc15b434c7829efa30387bc3c1ae950744f12b6b Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 21:24:37 -0400 Subject: [PATCH 049/146] feat(aurora): finished the factory migrations --- aurora/utilities/factory.py | 45 +++++++++++++------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 83bd7f1..5977f26 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -2,13 +2,16 @@ from datetime import datetime, timedelta from typing import Union -from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User +from discord import (Color, Embed, Guild, Interaction, InteractionMessage, + Member, Role, User) from redbot.core import commands -from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (bold, box, error, + humanize_timedelta, warning) from aurora.models import Moderation, PartialUser from aurora.utilities.config import config -from aurora.utilities.utils import fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, get_pagesize_str +from aurora.utilities.utils import (get_bool_emoji, get_next_case_number, + get_pagesize_str) async def message_factory( @@ -258,40 +261,22 @@ async def changes_factory(interaction: Interaction, moderation: Moderation) -> E return embed -async def evidenceformat_factory(interaction: Interaction, case_dict: dict) -> str: +async def evidenceformat_factory(interaction: Interaction, moderation: Moderation) -> str: """This function creates a codeblock in evidence format from set parameters. Args: - interaction (Interaction): The interaction object. - case_dict (dict): The case dictionary. + interaction (discord.Interaction): The interaction object. + moderation (aurora.models.Moderation): The moderation object. """ - if case_dict["target_type"] == "USER": - target_user = await fetch_user_dict(interaction.client, case_dict["target_id"]) - target_name = ( - target_user["name"] - if target_user["discriminator"] == "0" - else f"{target_user['name']}#{target_user['discriminator']}" - ) + target = await moderation.get_target() + moderator = await moderation.get_moderator() - elif case_dict["target_type"] == "CHANNEL": - target_user = await fetch_channel_dict(interaction.guild, case_dict["target_id"]) - target_name = target_user["name"] + content = f"Case: {moderation.id:,} ({str.title(moderation.type)})\nTarget: {target.name} ({target.id})\nModerator: {moderator.name} ({moderator.id})" - moderator_user = await fetch_user_dict(interaction.client, case_dict["moderator_id"]) - moderator_name = ( - moderator_user["name"] - if moderator_user["discriminator"] == "0" - else f"{moderator_user['name']}#{moderator_user['discriminator']}" - ) + if moderation.duration is not None: + content += f"\nDuration: {humanize_timedelta(timedelta=moderation.duration)}" - content = f"Case: {case_dict['moderation_id']:,} ({str.title(case_dict['moderation_type'])})\nTarget: {target_name} ({target_user['id']})\nModerator: {moderator_name} ({moderator_user['id']})" - - if case_dict["duration"] != "NULL": - hours, minutes, seconds = map(int, case_dict["duration"].split(":")) - td = timedelta(hours=hours, minutes=minutes, seconds=seconds) - content += f"\nDuration: {humanize_timedelta(timedelta=td)}" - - content += f"\nReason: {case_dict['reason']}" + content += f"\nReason: {moderation.reason}" return box(content, "prolog") -- 2.45.2 From dc44e8c6de37377a320ffb4ff1a31ba00c59fbeb Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:01:32 -0400 Subject: [PATCH 050/146] feat(aurora): migrated `/edit` to the new model system --- aurora/aurora.py | 148 +++++++++++++++++++---------------------------- aurora/models.py | 31 +++++++++- 2 files changed, 86 insertions(+), 93 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 0cc9546..1572195 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -27,13 +27,13 @@ from aurora.menus.addrole import Addrole from aurora.menus.guild import Guild from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides -from aurora.models import Moderation +from aurora.models import Change, Moderation from aurora.utilities.config import config, register_config from aurora.utilities.database import connect, create_guild_table, fetch_case, mysql_log from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed from aurora.utilities.json import dump, dumps from aurora.utilities.logger import logger -from aurora.utilities.utils import check_moddable, check_permissions, convert_timedelta_to_str, fetch_channel_dict, fetch_user_dict, generate_dict, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta +from aurora.utilities.utils import check_moddable, check_permissions, fetch_channel_dict, fetch_user_dict, generate_dict, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta class Aurora(commands.Cog): @@ -1490,107 +1490,77 @@ class Aurora(commands.Cog): return if case != 0: - parsed_time = None - case_dict = await fetch_case(case, interaction.guild.id) - if case_dict: - if duration: - parsed_time = parse_timedelta(duration) - if parsed_time is None: - await interaction.response.send_message( - error("Please provide a valid duration!"), ephemeral=True - ) - return - - end_timestamp = case_dict["timestamp"] + parsed_time.total_seconds() - - if case_dict["moderation_type"] == "MUTE": - if ( - time.time() - case_dict["timestamp"] - ) + parsed_time.total_seconds() > 2419200: - await interaction.response.send_message( - error( - "Please provide a duration that is less than 28 days from the initial moderation." - ) - ) - return - - try: - member = await interaction.guild.fetch_member( - case_dict["target_id"] - ) - - await member.timeout( - parsed_time, - reason=f"Case #{case:,} edited by {interaction.user.id}", - ) - except discord.NotFound: - pass - - changes: list = case_dict["changes"] - if len(changes) > 25: - await interaction.response.send_message( + moderation = Moderation.from_sql(interaction.client, case, interaction.guild.id) + old_moderation = moderation + if moderation: + if len(moderation.changes) > 25: + return await interaction.response.send_message( content=error( "Due to limitations with Discord's embed system, you cannot edit a case more than 25 times." ), ephemeral=True, ) - return - if not changes: - changes.append( - { - "type": "ORIGINAL", - "timestamp": case_dict["timestamp"], - "reason": case_dict["reason"], - "user_id": case_dict["moderator_id"], - "duration": case_dict["duration"], - "end_timestamp": case_dict["end_timestamp"], - } - ) - if parsed_time: - changes.append( - { + if duration: + moderation.duration = parse_timedelta(duration) + if moderation.duration is None: + return await interaction.response.send_message( + error("Please provide a valid duration!"), ephemeral=True + ) + + moderation.end_timestamp = moderation.timestamp + moderation.duration.total_seconds() + + if moderation.type == "MUTE": + if ( + time.time() - moderation.unix_timestamp + ) + moderation.duration.total_seconds() > 2419200: + return await interaction.response.send_message( + error( + "Please provide a duration that is less than 28 days from the initial moderation." + ) + ) + + try: + member = await interaction.guild.fetch_member( + moderation.target_id + ) + + await member.timeout( + moderation.duration, + reason=f"Case #{case:,} edited by {interaction.user.id}", + ) + except discord.NotFound: + pass + + if not moderation.changes: + moderation.changes.append(Change.from_dict(interaction.client, { + "type": "ORIGINAL", + "timestamp": old_moderation.timestamp, + "reason": old_moderation.reason, + "user_id": old_moderation.moderator_id, + "duration": old_moderation.duration, + "end_timestamp": old_moderation.end_timestamp, + })) + if duration: + moderation.changes.append(Change.from_dict(interaction.client, { "type": "EDIT", "timestamp": int(time.time()), "reason": reason, "user_id": interaction.user.id, - "duration": convert_timedelta_to_str(parsed_time), - "end_timestamp": end_timestamp, - } - ) + "duration": moderation.duration, + "end_timestamp": moderation.end_timestamp, + })) else: - changes.append( - { + moderation.changes.append(Change.from_dict(interaction.client, { "type": "EDIT", "timestamp": int(time.time()), "reason": reason, "user_id": interaction.user.id, - "duration": case_dict["duration"], - "end_timestamp": case_dict["end_timestamp"], - } - ) + "duration": moderation.duration, + "end_timestamp": moderation.end_timestamp, + })) - database = connect() - cursor = database.cursor() - - if parsed_time: - update_query = f"UPDATE `moderation_{interaction.guild.id}` SET changes = ?, reason = ?, duration = ?, end_timestamp = ? WHERE moderation_id = ?" - cursor.execute( - update_query, - ( - dumps(changes), - reason, - convert_timedelta_to_str(parsed_time), - end_timestamp, - case, - ), - ) - else: - update_query = f"UPDATE `moderation_{interaction.guild.id}` SET changes = ?, reason = ? WHERE moderation_id = ?" - cursor.execute(update_query, (dumps(changes), reason, case)) - database.commit() - - new_case = await fetch_case(case, interaction.guild.id) - embed = await case_factory(interaction=interaction, case_dict=new_case) + moderation.update() + embed = await case_factory(interaction=interaction, moderation=moderation) await interaction.response.send_message( content=f"✅ Moderation #{case:,} edited!", @@ -1599,8 +1569,6 @@ class Aurora(commands.Cog): ) await log(interaction, case) - cursor.close() - database.close() return await interaction.response.send_message( content=error(f"No case with case number `{case}` found."), ephemeral=True diff --git a/aurora/models.py b/aurora/models.py index f00f0b5..dad4028 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -77,6 +77,16 @@ class Moderation(AuroraGuildModel): def __str__(self): return f"{self.moderation_type} {self.target_type} {self.target_id} {self.reason}" + def update(self): + from aurora.utilities.database import connect + query = f"UPDATE moderation_{self.guild_id} SET timestamp = ?, moderation_type = ?, target_type = ?, moderator_id = ?, role_id = ?, duration = ?, end_timestamp = ?, reason = ?, resolved = ?, resolved_by = ?, resolve_reason = ?, expired = ?, changes = ?, metadata = ? WHERE moderation_id = ?;" + changes = [change.to_json() for change in self.changes] + + with connect() as database: + cursor = database.cursor() + cursor.execute(query, (self.timestamp, self.moderation_type, self.target_type, self.moderator_id, self.role_id, self.duration, self.end_timestamp, self.reason, self.resolved, self.resolved_by, self.resolve_reason, self.expired, changes, self.metadata, self.moderation_id)) + cursor.close() + @classmethod def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> Optional["Moderation"]: from aurora.utilities.database import connect @@ -118,14 +128,29 @@ class Change(AuroraBaseModel): @classmethod def from_dict(cls, bot: Red, data: dict) -> "Change": - if "duration" in data and data["duration"] is not None: + if "duration" in data and data["duration"] and not isinstance(data["duration"], timedelta): hours, minutes, seconds = map(int, data["duration"].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) + elif duration in data and isinstance(data["duration"], timedelta): + duration = data["duration"] else: duration = None + + if "end_timestamp" in data and data["end_timestamp"] and not isinstance(data["end_timestamp"], datetime): + end_timestamp = datetime.fromtimestamp(data["end_timestamp"]) + elif "end_timestamp" in data and isinstance(data["end_timestamp"], datetime): + end_timestamp = data["end_timestamp"] + else: + end_timestamp = None + + if not isinstance(data["timestamp"], datetime): + timestamp = datetime.fromtimestamp(data["timestamp"]) + else: + timestamp = data["timestamp"] + data.update({ - "timestamp": datetime.fromtimestamp(data["timestamp"]), - "end_timestamp": datetime.fromtimestamp(data["end_timestamp"]) if "end_timestamp" in data and data["end_timestamp"] else None, + "timestamp": timestamp, + "end_timestamp": end_timestamp, "duration": duration }) return cls(bot=bot, **data) -- 2.45.2 From b03ce0424551cd63a4961eb3c16e96428e21c593 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:02:31 -0400 Subject: [PATCH 051/146] fix(aurora): fixed an unboundlocalerror in Change.from_dict() --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index dad4028..52cf3b7 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -131,7 +131,7 @@ class Change(AuroraBaseModel): if "duration" in data and data["duration"] and not isinstance(data["duration"], timedelta): hours, minutes, seconds = map(int, data["duration"].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) - elif duration in data and isinstance(data["duration"], timedelta): + elif "duration" in data and isinstance(data["duration"], timedelta): duration = data["duration"] else: duration = None -- 2.45.2 From 3f8cdf2012fa094e7bbbf395b2ba4eee2afe755e Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:04:29 -0400 Subject: [PATCH 052/146] fix(aurora): fixed an sqlite error --- aurora/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 52cf3b7..cef07fe 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -5,6 +5,7 @@ from discord import Forbidden, HTTPException, InvalidData, NotFound from pydantic import BaseModel, ConfigDict from redbot.core.bot import Red +from aurora.utilities.json import dumps from aurora.utilities.utils import generate_dict @@ -84,7 +85,7 @@ class Moderation(AuroraGuildModel): with connect() as database: cursor = database.cursor() - cursor.execute(query, (self.timestamp, self.moderation_type, self.target_type, self.moderator_id, self.role_id, self.duration, self.end_timestamp, self.reason, self.resolved, self.resolved_by, self.resolve_reason, self.expired, changes, self.metadata, self.moderation_id)) + cursor.execute(query, (self.timestamp, self.moderation_type, self.target_type, self.moderator_id, self.role_id, self.duration, self.end_timestamp, self.reason, self.resolved, self.resolved_by, self.resolve_reason, self.expired, dumps(changes), dumps(self.metadata), self.moderation_id)) cursor.close() @classmethod -- 2.45.2 From 557ac45fccad00c4ccd07f0274a55d739dd7772a Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:05:14 -0400 Subject: [PATCH 053/146] fix(aurora): fixed a circular import --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index cef07fe..198653b 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -5,7 +5,6 @@ from discord import Forbidden, HTTPException, InvalidData, NotFound from pydantic import BaseModel, ConfigDict from redbot.core.bot import Red -from aurora.utilities.json import dumps from aurora.utilities.utils import generate_dict @@ -80,6 +79,7 @@ class Moderation(AuroraGuildModel): def update(self): from aurora.utilities.database import connect + from aurora.utilities.json import dumps query = f"UPDATE moderation_{self.guild_id} SET timestamp = ?, moderation_type = ?, target_type = ?, moderator_id = ?, role_id = ?, duration = ?, end_timestamp = ?, reason = ?, resolved = ?, resolved_by = ?, resolve_reason = ?, expired = ?, changes = ?, metadata = ? WHERE moderation_id = ?;" changes = [change.to_json() for change in self.changes] -- 2.45.2 From 0b697f9f502103e2dfb2fa84ad60ccbe27a3c729 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:07:51 -0400 Subject: [PATCH 054/146] fix(aurora): change how Moderation.update() works --- aurora/models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 198653b..2e0c717 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -81,11 +81,10 @@ class Moderation(AuroraGuildModel): from aurora.utilities.database import connect from aurora.utilities.json import dumps query = f"UPDATE moderation_{self.guild_id} SET timestamp = ?, moderation_type = ?, target_type = ?, moderator_id = ?, role_id = ?, duration = ?, end_timestamp = ?, reason = ?, resolved = ?, resolved_by = ?, resolve_reason = ?, expired = ?, changes = ?, metadata = ? WHERE moderation_id = ?;" - changes = [change.to_json() for change in self.changes] with connect() as database: cursor = database.cursor() - cursor.execute(query, (self.timestamp, self.moderation_type, self.target_type, self.moderator_id, self.role_id, self.duration, self.end_timestamp, self.reason, self.resolved, self.resolved_by, self.resolve_reason, self.expired, dumps(changes), dumps(self.metadata), self.moderation_id)) + cursor.execute(query, (self.timestamp, self.moderation_type, self.target_type, self.moderator_id, self.role_id, self.duration, self.end_timestamp, self.reason, self.resolved, self.resolved_by, self.resolve_reason, self.expired, dumps(self.changes), dumps(self.metadata), self.moderation_id)) cursor.close() @classmethod -- 2.45.2 From 04d10d2cf8cf9deaf0dbaaac7f423c88abbbd9e1 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:12:00 -0400 Subject: [PATCH 055/146] fix(aurora): strip and replace the json dumps before inserting them into the db --- aurora/models.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 2e0c717..2129ad8 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -84,7 +84,23 @@ class Moderation(AuroraGuildModel): with connect() as database: cursor = database.cursor() - cursor.execute(query, (self.timestamp, self.moderation_type, self.target_type, self.moderator_id, self.role_id, self.duration, self.end_timestamp, self.reason, self.resolved, self.resolved_by, self.resolve_reason, self.expired, dumps(self.changes), dumps(self.metadata), self.moderation_id)) + cursor.execute(query, ( + self.timestamp, + self.moderation_type, + self.target_type, + self.moderator_id, + self.role_id, + self.duration, + self.end_timestamp, + self.reason, + self.resolved, + self.resolved_by, + self.resolve_reason, + self.expired, + dumps(self.changes).strip('"').replace('\\"', '"'), + dumps(self.metadata).strip('"').replace('\\"', '"'), + self.moderation_id + )) cursor.close() @classmethod -- 2.45.2 From acf3b0c68f03a235751c86b86657db686573797d Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:17:52 -0400 Subject: [PATCH 056/146] fix(aurora): added logging for the Moderation.update() method --- aurora/models.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/aurora/models.py b/aurora/models.py index 2129ad8..b542ab4 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -5,6 +5,7 @@ from discord import Forbidden, HTTPException, InvalidData, NotFound from pydantic import BaseModel, ConfigDict from redbot.core.bot import Red +from aurora.utilities.logger import logger from aurora.utilities.utils import generate_dict @@ -103,6 +104,25 @@ class Moderation(AuroraGuildModel): )) cursor.close() + logger.info("Updated moderation case %s in guild %s with the following data:\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", + self.moderation_id, + self.guild_id, + self.timestamp, + self.moderation_type, + self.target_type, + self.moderator_id, + self.role_id, + self.duration, + self.end_timestamp, + self.reason, + self.resolved, + self.resolved_by, + self.resolve_reason, + self.expired, + dumps(self.changes).strip('"').replace('\\"', '"'), + dumps(self.metadata).strip('"').replace('\\"', '"'), + ) + @classmethod def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> Optional["Moderation"]: from aurora.utilities.database import connect -- 2.45.2 From e8d210df2a16a397302f20e24d7986faa08485e2 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:19:29 -0400 Subject: [PATCH 057/146] fix(aurora): removed a %s --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index b542ab4..e80b345 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -104,7 +104,7 @@ class Moderation(AuroraGuildModel): )) cursor.close() - logger.info("Updated moderation case %s in guild %s with the following data:\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", + logger.info("Updated moderation case %s in guild %s with the following data:\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", self.moderation_id, self.guild_id, self.timestamp, -- 2.45.2 From 300d26dc7e2900c78af21af931093892631af1e9 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:50:30 -0400 Subject: [PATCH 058/146] fix(aurora): fixed a bunch of json issues --- aurora/models.py | 8 ++++---- aurora/utilities/utils.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index e80b345..66ed68a 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -98,8 +98,8 @@ class Moderation(AuroraGuildModel): self.resolved_by, self.resolve_reason, self.expired, - dumps(self.changes).strip('"').replace('\\"', '"'), - dumps(self.metadata).strip('"').replace('\\"', '"'), + dumps(self.changes).replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]'), + dumps(self.metadata).replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]'), self.moderation_id )) cursor.close() @@ -119,8 +119,8 @@ class Moderation(AuroraGuildModel): self.resolved_by, self.resolve_reason, self.expired, - dumps(self.changes).strip('"').replace('\\"', '"'), - dumps(self.metadata).strip('"').replace('\\"', '"'), + dumps(self.changes).replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]'), + dumps(self.metadata).replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]'), ) @classmethod diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index b15a865..67fb902 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -134,7 +134,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: duration = None if result[14] is not None: - changes = json.loads(result[14].strip('"').replace('\\"', '"')) + changes = json.loads(result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) change_obj_list = [] for change in changes: change_obj_list.append(Change.from_dict(bot=bot, data=change)) @@ -156,7 +156,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: "resolve_reason": result[12], "expired": bool(result[13]), "changes": change_obj_list if result[14] else [], - "metadata": json.loads(result[15].strip('"').replace('\\"', '"')) if result[15] else {}, + "metadata": json.loads(result[15].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) if result[15] else {}, } return case -- 2.45.2 From 8433c946fd88f09842b4035f9cac7d7e9d38c71f Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:52:54 -0400 Subject: [PATCH 059/146] fix(aurora): added a debug logging statement --- aurora/utilities/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 67fb902..b8e0c7b 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -11,6 +11,7 @@ from redbot.core.bot import Red from redbot.core.utils.chat_formatting import error from aurora.utilities.config import config +from aurora.utilities.logger import logger def check_permissions( @@ -134,6 +135,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: duration = None if result[14] is not None: + logger.debug(result[14]) changes = json.loads(result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) change_obj_list = [] for change in changes: -- 2.45.2 From a6371fd3674b814f9281debac6431efc4f50c761 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:55:17 -0400 Subject: [PATCH 060/146] fix(aurora): changed the logging statement slightly --- aurora/utilities/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index b8e0c7b..61687a7 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -135,7 +135,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: duration = None if result[14] is not None: - logger.debug(result[14]) + logger.debug('%s', result[14]) changes = json.loads(result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) change_obj_list = [] for change in changes: -- 2.45.2 From 293f77c228aebd2ff209579e52afbc6d89473569 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:55:50 -0400 Subject: [PATCH 061/146] fix(aurora): changed the logging statement again --- aurora/utilities/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 61687a7..fc92032 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -135,7 +135,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: duration = None if result[14] is not None: - logger.debug('%s', result[14]) + logger.debug('%s\n%s', result[14], result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) changes = json.loads(result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) change_obj_list = [] for change in changes: -- 2.45.2 From de90f6a8b7187e2c9a07b64d51b35f88bc4e2b57 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:57:34 -0400 Subject: [PATCH 062/146] fix(aurora): troubleshooting this annoying json issue --- aurora/utilities/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index fc92032..c6e7b19 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -135,8 +135,9 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: duration = None if result[14] is not None: - logger.debug('%s\n%s', result[14], result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) - changes = json.loads(result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) + json_str = f'{result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')}' + logger.debug('%s\n%s', result[14], json_str) + changes = json.loads(json_str) change_obj_list = [] for change in changes: change_obj_list.append(Change.from_dict(bot=bot, data=change)) -- 2.45.2 From cad24d852c846669bb12e949e2b602a3cf7cc63c Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 22:58:29 -0400 Subject: [PATCH 063/146] =?UTF-8?q?fix(aurora):=20ok=20fine=20bro=20?= =?UTF-8?q?=F0=9F=98=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aurora/utilities/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index c6e7b19..b856003 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -135,7 +135,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: duration = None if result[14] is not None: - json_str = f'{result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')}' + json_str = result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]') logger.debug('%s\n%s', result[14], json_str) changes = json.loads(json_str) change_obj_list = [] -- 2.45.2 From 278bd9834992e485cc9f18a8a15f5b3a9aa8346b Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 23:01:39 -0400 Subject: [PATCH 064/146] fix(aurora): trying ast --- aurora/utilities/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index b856003..b728a30 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -1,5 +1,6 @@ # pylint: disable=cyclic-import import json +from ast import literal_eval from datetime import datetime, timedelta from typing import Optional, Union @@ -137,7 +138,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: if result[14] is not None: json_str = result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]') logger.debug('%s\n%s', result[14], json_str) - changes = json.loads(json_str) + changes = literal_eval(json_str) change_obj_list = [] for change in changes: change_obj_list.append(Change.from_dict(bot=bot, data=change)) -- 2.45.2 From 4c8cd7bd1684aec166c8b997cc6d92b28c1d8f9a Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Sat, 4 May 2024 23:02:57 -0400 Subject: [PATCH 065/146] Revert "fix(aurora): trying ast" This reverts commit 278bd9834992e485cc9f18a8a15f5b3a9aa8346b. --- aurora/utilities/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index b728a30..b856003 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -1,6 +1,5 @@ # pylint: disable=cyclic-import import json -from ast import literal_eval from datetime import datetime, timedelta from typing import Optional, Union @@ -138,7 +137,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: if result[14] is not None: json_str = result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]') logger.debug('%s\n%s', result[14], json_str) - changes = literal_eval(json_str) + changes = json.loads(json_str) change_obj_list = [] for change in changes: change_obj_list.append(Change.from_dict(bot=bot, data=change)) -- 2.45.2 From 85a935f9b34ecc7c32450206f72c7b37fb9e3872 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 13:27:09 -0400 Subject: [PATCH 066/146] fix(aurora): fixed the json issue from yesterday --- aurora/utilities/utils.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index b856003..1f9c7e4 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -1,5 +1,6 @@ # pylint: disable=cyclic-import import json +from ast import literal_eval from datetime import datetime, timedelta from typing import Optional, Union @@ -11,7 +12,6 @@ from redbot.core.bot import Red from redbot.core.utils.chat_formatting import error from aurora.utilities.config import config -from aurora.utilities.logger import logger def check_permissions( @@ -135,9 +135,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: duration = None if result[14] is not None: - json_str = result[14].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]') - logger.debug('%s\n%s', result[14], json_str) - changes = json.loads(json_str) + changes = literal_eval(json.loads(result[14])) change_obj_list = [] for change in changes: change_obj_list.append(Change.from_dict(bot=bot, data=change)) -- 2.45.2 From 6745f0a4860034c56a51b6a2fd8ccc46869f3327 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 13:44:48 -0400 Subject: [PATCH 067/146] fix(aurora): fixed an issue with importing changes --- aurora/importers/aurora.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aurora/importers/aurora.py b/aurora/importers/aurora.py index d1905e7..fce790e 100644 --- a/aurora/importers/aurora.py +++ b/aurora/importers/aurora.py @@ -72,6 +72,10 @@ class ImportAuroraView(ui.View): if "changes" not in case or not case["changes"]: case["changes"] = [] + else: + case["changes"] = json.loads(case["changes"]) + if isinstance(case["changes"], str): + case["changes"] = json.loads(case["changes"]) if "metadata" not in case: metadata = {} -- 2.45.2 From 0553856aa90b8a110689fdc9fdcf2883100d1f90 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 13:51:23 -0400 Subject: [PATCH 068/146] fix(aurora): fixed utils.generate_dict() using literal_eval still even when it's unnecessary --- aurora/utilities/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 1f9c7e4..63a894a 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -1,6 +1,5 @@ # pylint: disable=cyclic-import import json -from ast import literal_eval from datetime import datetime, timedelta from typing import Optional, Union @@ -135,7 +134,7 @@ def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: duration = None if result[14] is not None: - changes = literal_eval(json.loads(result[14])) + changes = json.loads(result[14]) change_obj_list = [] for change in changes: change_obj_list.append(Change.from_dict(bot=bot, data=change)) -- 2.45.2 From 37e471fbaa5ceba0990335e42a1d8e7990dce4b6 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:15:05 -0400 Subject: [PATCH 069/146] fix(aurora): fixing a whole bunch of stuff --- aurora/importers/aurora.py | 6 +- aurora/models.py | 114 +++++++++++++++++++++++++++++++++-- aurora/utilities/database.py | 7 ++- aurora/utilities/utils.py | 4 +- 4 files changed, 118 insertions(+), 13 deletions(-) diff --git a/aurora/importers/aurora.py b/aurora/importers/aurora.py index fce790e..0885ee9 100644 --- a/aurora/importers/aurora.py +++ b/aurora/importers/aurora.py @@ -8,7 +8,8 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands from redbot.core.utils.chat_formatting import box, warning -from ..utilities.database import connect, create_guild_table, mysql_log +from aurora.models import Moderation +from aurora.utilities.database import connect, create_guild_table class ImportAuroraView(ui.View): @@ -91,7 +92,8 @@ class ImportAuroraView(ui.View): else: duration = None - await mysql_log( + Moderation.log( + interaction.client, self.ctx.guild.id, case["moderator_id"], case["moderation_type"], diff --git a/aurora/models.py b/aurora/models.py index 66ed68a..9d70330 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,4 +1,6 @@ +import sqlite3 from datetime import datetime, timedelta +from time import time from typing import Any, Dict, List, Literal, Optional, Union from discord import Forbidden, HTTPException, InvalidData, NotFound @@ -6,7 +8,7 @@ from pydantic import BaseModel, ConfigDict from redbot.core.bot import Red from aurora.utilities.logger import logger -from aurora.utilities.utils import generate_dict +from aurora.utilities.utils import generate_dict, get_next_case_number class AuroraBaseModel(BaseModel): @@ -80,7 +82,6 @@ class Moderation(AuroraGuildModel): def update(self): from aurora.utilities.database import connect - from aurora.utilities.json import dumps query = f"UPDATE moderation_{self.guild_id} SET timestamp = ?, moderation_type = ?, target_type = ?, moderator_id = ?, role_id = ?, duration = ?, end_timestamp = ?, reason = ?, resolved = ?, resolved_by = ?, resolve_reason = ?, expired = ?, changes = ?, metadata = ? WHERE moderation_id = ?;" with connect() as database: @@ -98,8 +99,8 @@ class Moderation(AuroraGuildModel): self.resolved_by, self.resolve_reason, self.expired, - dumps(self.changes).replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]'), - dumps(self.metadata).replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]'), + self.changes, + self.metadata, self.moderation_id )) cursor.close() @@ -119,8 +120,8 @@ class Moderation(AuroraGuildModel): self.resolved_by, self.resolve_reason, self.expired, - dumps(self.changes).replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]'), - dumps(self.metadata).replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]'), + self.changes, + self.metadata, ) @classmethod @@ -144,6 +145,107 @@ class Moderation(AuroraGuildModel): def from_dict(cls, bot: Red, data: dict) -> "Moderation": return cls(bot=bot, **data) + @classmethod + def log( + cls, + bot: Red, + guild_id: int, + author_id: int, + moderation_type: str, + target_type: str, + target_id: int, + role_id: int, + duration: timedelta = None, + reason: str = None, + database: sqlite3.Connection = None, + timestamp: datetime = None, + resolved: bool = False, + resolved_by: int = None, + resolved_reason: str = None, + expired: bool = None, + changes: list = None, + metadata: dict = None, + ) -> "Moderation": + from aurora.utilities.database import connect + if not timestamp: + timestamp = datetime.fromtimestamp(time()) + elif not isinstance(timestamp, datetime): + timestamp = datetime.fromtimestamp(timestamp) + + if duration != "NULL" and duration is not None: + end_timedelta = timestamp + duration + end_timestamp = int(end_timedelta.timestamp()) + else: + duration = None + end_timestamp = None + + if not expired: + if timestamp.timestamp() > end_timestamp: + expired = True + else: + expired = False + + if reason == "NULL": + reason = None + + if resolved_by == "NULL": + resolved_by = None + + if resolved_reason == "NULL": + resolved_reason = None + + if role_id == 0: + role_id = None + + if not database: + database = connect() + close_db = True + else: + close_db = False + cursor = database.cursor() + + moderation_id = get_next_case_number(guild_id=guild_id, cursor=cursor) + + case = { + "guild_id": guild_id, + "moderation_id": moderation_id, + "timestamp": timestamp, + "moderation_type": moderation_type, + "target_type": target_type, + "target_id": target_id, + "moderator_id": author_id, + "role_id": role_id, + "duration": duration, + "end_timestamp": end_timestamp, + "reason": reason, + "resolved": resolved, + "resolved_by": resolved_by, + "resolve_reason": resolved_reason, + "expired": expired, + "changes": changes, + "metadata": metadata + } + + case_safe = case.copy() + case_safe.pop("guild_id") + case_safe["timestamp"] = case_safe["timestamp"].timestamp() + case_safe["end_timestamp"] = case_safe["end_timestamp"].timestamp() if case_safe["end_timestamp"] else None + + sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + cursor.execute(sql, case_safe.values()) + + cursor.close() + database.commit() + if close_db: + database.close() + + logger.debug( + "Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", + guild_id, case_safe.values() + ) + + return cls.from_dict(bot=bot, **case) + class Change(AuroraBaseModel): type: Literal["ORIGINAL", "RESOLVE", "EDIT"] timestamp: datetime diff --git a/aurora/utilities/database.py b/aurora/utilities/database.py index 95ad7c8..88cec8b 100644 --- a/aurora/utilities/database.py +++ b/aurora/utilities/database.py @@ -8,7 +8,8 @@ from discord import Guild from redbot.core import data_manager from .logger import logger -from .utils import convert_timedelta_to_str, generate_dict, get_next_case_number +from .utils import (convert_timedelta_to_str, generate_dict, + get_next_case_number) def connect() -> sqlite3.Connection: @@ -103,7 +104,7 @@ async def create_guild_table(guild: Guild): database.close() -async def mysql_log( +def mysql_log( guild_id: str, author_id: str, moderation_type: str, @@ -158,7 +159,7 @@ async def mysql_log( close_db = False cursor = database.cursor() - moderation_id = await get_next_case_number(guild_id=guild_id, cursor=cursor) + moderation_id = get_next_case_number(guild_id=guild_id, cursor=cursor) sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" val = ( diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 63a894a..fcc8d61 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -111,9 +111,9 @@ async def check_moddable( return True -async def get_next_case_number(guild_id: str, cursor=None) -> int: +def get_next_case_number(guild_id: str, cursor=None) -> int: """This function returns the next case number from the MySQL table for a specific guild.""" - from .database import connect + from aurora.utilities.database import connect if not cursor: database = connect() -- 2.45.2 From a86348fae3faf137cd9a837782f821f418b26b5c Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:16:51 -0400 Subject: [PATCH 070/146] fix(aurora): fixed a programming error --- aurora/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aurora/models.py b/aurora/models.py index 9d70330..7a63220 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -167,6 +167,7 @@ class Moderation(AuroraGuildModel): metadata: dict = None, ) -> "Moderation": from aurora.utilities.database import connect + from aurora.utilities.json import dumps if not timestamp: timestamp = datetime.fromtimestamp(time()) elif not isinstance(timestamp, datetime): @@ -230,6 +231,11 @@ class Moderation(AuroraGuildModel): case_safe.pop("guild_id") case_safe["timestamp"] = case_safe["timestamp"].timestamp() case_safe["end_timestamp"] = case_safe["end_timestamp"].timestamp() if case_safe["end_timestamp"] else None + case_safe["duration"] = str(case_safe["duration"]) + case_safe["resolved"] = int(case_safe["resolved"]) + case_safe["expired"] = int(case_safe["expired"]) + case_safe["changes"] = dumps(case_safe["changes"]) + case_safe["metadata"] = dumps(case_safe["metadata"]) sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" cursor.execute(sql, case_safe.values()) -- 2.45.2 From e591b2c4a50b3bf23b917d7de75585a19d185d6b Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:18:54 -0400 Subject: [PATCH 071/146] fix(aurora): convert floats to ints --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 7a63220..07a4d04 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -229,8 +229,8 @@ class Moderation(AuroraGuildModel): case_safe = case.copy() case_safe.pop("guild_id") - case_safe["timestamp"] = case_safe["timestamp"].timestamp() - case_safe["end_timestamp"] = case_safe["end_timestamp"].timestamp() if case_safe["end_timestamp"] else None + case_safe["timestamp"] = int(case_safe["timestamp"].timestamp()) + case_safe["end_timestamp"] = int(case_safe["end_timestamp"].timestamp()) if case_safe["end_timestamp"] else None case_safe["duration"] = str(case_safe["duration"]) case_safe["resolved"] = int(case_safe["resolved"]) case_safe["expired"] = int(case_safe["expired"]) -- 2.45.2 From 65bb9af7a6678981d2c9f047b3112892cd4f82bf Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:20:49 -0400 Subject: [PATCH 072/146] fix(aurora): fixed an issue with dictionary values not being converted to a tuple --- aurora/models.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 07a4d04..c31675b 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -167,7 +167,6 @@ class Moderation(AuroraGuildModel): metadata: dict = None, ) -> "Moderation": from aurora.utilities.database import connect - from aurora.utilities.json import dumps if not timestamp: timestamp = datetime.fromtimestamp(time()) elif not isinstance(timestamp, datetime): @@ -229,16 +228,11 @@ class Moderation(AuroraGuildModel): case_safe = case.copy() case_safe.pop("guild_id") - case_safe["timestamp"] = int(case_safe["timestamp"].timestamp()) - case_safe["end_timestamp"] = int(case_safe["end_timestamp"].timestamp()) if case_safe["end_timestamp"] else None - case_safe["duration"] = str(case_safe["duration"]) - case_safe["resolved"] = int(case_safe["resolved"]) - case_safe["expired"] = int(case_safe["expired"]) - case_safe["changes"] = dumps(case_safe["changes"]) - case_safe["metadata"] = dumps(case_safe["metadata"]) + case_safe["timestamp"] = case_safe["timestamp"].timestamp() + case_safe["end_timestamp"] = case_safe["end_timestamp"].timestamp() if case_safe["end_timestamp"] else None sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - cursor.execute(sql, case_safe.values()) + cursor.execute(sql, tuple(case_safe.values())) cursor.close() database.commit() -- 2.45.2 From 52fcdcc96a2ed1ea4c03a05b06ee78826d14aad9 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:21:57 -0400 Subject: [PATCH 073/146] fix(aurora): fixed a logging issue --- aurora/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aurora/models.py b/aurora/models.py index c31675b..7caf331 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -167,6 +167,7 @@ class Moderation(AuroraGuildModel): metadata: dict = None, ) -> "Moderation": from aurora.utilities.database import connect + from aurora.utilities.json import dumps if not timestamp: timestamp = datetime.fromtimestamp(time()) elif not isinstance(timestamp, datetime): @@ -230,6 +231,8 @@ class Moderation(AuroraGuildModel): case_safe.pop("guild_id") case_safe["timestamp"] = case_safe["timestamp"].timestamp() case_safe["end_timestamp"] = case_safe["end_timestamp"].timestamp() if case_safe["end_timestamp"] else None + case_safe["changes"] = dumps(case_safe["changes"]) + case_safe["metadata"] = dumps(case_safe["metadata"]) sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" cursor.execute(sql, tuple(case_safe.values())) -- 2.45.2 From 158e7560f8f45dcfbdab85aacbe87b196c948209 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:22:40 -0400 Subject: [PATCH 074/146] fix(aurora): fixed a broken logging statement --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 7caf331..27375c4 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -244,7 +244,7 @@ class Moderation(AuroraGuildModel): logger.debug( "Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", - guild_id, case_safe.values() + guild_id, tuple(case_safe.values()) ) return cls.from_dict(bot=bot, **case) -- 2.45.2 From 5b6b04dfe0c8f76aae44b500120dd99ea35876bd Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:25:42 -0400 Subject: [PATCH 075/146] fix(aurora): actually fixed a broken logging statement --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 27375c4..4dfa38a 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -244,7 +244,7 @@ class Moderation(AuroraGuildModel): logger.debug( "Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", - guild_id, tuple(case_safe.values()) + tuple(guild_id, case_safe.values()) ) return cls.from_dict(bot=bot, **case) -- 2.45.2 From 1b322dfe50cb435cde8b974347a15a3910b3a757 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:29:09 -0400 Subject: [PATCH 076/146] fix(aurora): actually actually fixed the broken logging statement --- aurora/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 4dfa38a..091ff03 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,5 +1,6 @@ import sqlite3 from datetime import datetime, timedelta +from itertools import chain from time import time from typing import Any, Dict, List, Literal, Optional, Union @@ -244,7 +245,7 @@ class Moderation(AuroraGuildModel): logger.debug( "Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", - tuple(guild_id, case_safe.values()) + tuple(chain({"guild_id": guild_id}.values() + case_safe.values())) ) return cls.from_dict(bot=bot, **case) -- 2.45.2 From 335d1a2a4c531bcc10b27c3f4a249910ea79d3e5 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:29:57 -0400 Subject: [PATCH 077/146] fix(aurora): actually actually ACTUALLY fixed the broken logging statement --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 091ff03..cae9d23 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -245,7 +245,7 @@ class Moderation(AuroraGuildModel): logger.debug( "Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", - tuple(chain({"guild_id": guild_id}.values() + case_safe.values())) + tuple(chain({"guild_id": guild_id}.values(), case_safe.values())) ) return cls.from_dict(bot=bot, **case) -- 2.45.2 From 0386cb346ef1f06500b7499dc8c9d93e98035b9f Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:35:02 -0400 Subject: [PATCH 078/146] fix(aurora): finally fixed the debug logging statement (maybe) --- aurora/models.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index cae9d23..b758bab 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,6 +1,5 @@ import sqlite3 from datetime import datetime, timedelta -from itertools import chain from time import time from typing import Any, Dict, List, Literal, Optional, Union @@ -229,23 +228,25 @@ class Moderation(AuroraGuildModel): } case_safe = case.copy() - case_safe.pop("guild_id") case_safe["timestamp"] = case_safe["timestamp"].timestamp() case_safe["end_timestamp"] = case_safe["end_timestamp"].timestamp() if case_safe["end_timestamp"] else None case_safe["changes"] = dumps(case_safe["changes"]) case_safe["metadata"] = dumps(case_safe["metadata"]) + case_sql = case_safe.copy() + case_sql.pop("guild_id") sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - cursor.execute(sql, tuple(case_safe.values())) + cursor.execute(sql, tuple(case_sql.values())) cursor.close() database.commit() if close_db: database.close() + case_safe.update({"guild_id": guild_id}) logger.debug( "Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", - tuple(chain({"guild_id": guild_id}.values(), case_safe.values())) + tuple(case_safe.values()) ) return cls.from_dict(bot=bot, **case) -- 2.45.2 From 70c00a59d620f607d81c9ee4b49e443cbbe5950d Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:40:14 -0400 Subject: [PATCH 079/146] fix(aurora): fixed the broken logging statement --- aurora/models.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index b758bab..3304909 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -150,7 +150,7 @@ class Moderation(AuroraGuildModel): cls, bot: Red, guild_id: int, - author_id: int, + moderator_id: int, moderation_type: str, target_type: str, target_id: int, @@ -214,7 +214,7 @@ class Moderation(AuroraGuildModel): "moderation_type": moderation_type, "target_type": target_type, "target_id": target_id, - "moderator_id": author_id, + "moderator_id": moderator_id, "role_id": role_id, "duration": duration, "end_timestamp": end_timestamp, @@ -228,15 +228,14 @@ class Moderation(AuroraGuildModel): } case_safe = case.copy() + case_safe.pop("guild_id") case_safe["timestamp"] = case_safe["timestamp"].timestamp() case_safe["end_timestamp"] = case_safe["end_timestamp"].timestamp() if case_safe["end_timestamp"] else None case_safe["changes"] = dumps(case_safe["changes"]) case_safe["metadata"] = dumps(case_safe["metadata"]) - case_sql = case_safe.copy() - case_sql.pop("guild_id") sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - cursor.execute(sql, tuple(case_sql.values())) + cursor.execute(sql, tuple(case_safe.values())) cursor.close() database.commit() @@ -246,7 +245,23 @@ class Moderation(AuroraGuildModel): case_safe.update({"guild_id": guild_id}) logger.debug( "Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", - tuple(case_safe.values()) + guild_id, + case_safe["moderation_id"], + case_safe["timestamp"], + case_safe["moderation_type"], + case_safe["target_type"], + case_safe["target_id"], + case_safe["moderator_id"], + case_safe["role_id"], + case_safe["duration"], + case_safe["end_timestamp"], + case_safe["reason"], + case_safe["resolved"], + case_safe["resolved_by"], + case_safe["resolve_reason"], + case_safe["expired"], + case_safe["changes"], + case_safe["metadata"], ) return cls.from_dict(bot=bot, **case) -- 2.45.2 From 84235d6504a87af114716c561418a567c8993233 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:41:16 -0400 Subject: [PATCH 080/146] fix(aurora): call from_dict properly --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 3304909..57c8a80 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -264,7 +264,7 @@ class Moderation(AuroraGuildModel): case_safe["metadata"], ) - return cls.from_dict(bot=bot, **case) + return cls.from_dict(bot=bot, data=case) class Change(AuroraBaseModel): type: Literal["ORIGINAL", "RESOLVE", "EDIT"] -- 2.45.2 From b7b6dc2f2e68cabaa871f1d160ab0e98c9da4e8d Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:44:06 -0400 Subject: [PATCH 081/146] fix(aurora): don't convert end_timestamp to an integer --- aurora/models.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 57c8a80..4792f4a 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -174,14 +174,13 @@ class Moderation(AuroraGuildModel): timestamp = datetime.fromtimestamp(timestamp) if duration != "NULL" and duration is not None: - end_timedelta = timestamp + duration - end_timestamp = int(end_timedelta.timestamp()) + end_timestamp = timestamp + duration else: duration = None end_timestamp = None if not expired: - if timestamp.timestamp() > end_timestamp: + if timestamp > end_timestamp: expired = True else: expired = False -- 2.45.2 From 1e865643a03487d065ceebf03ced17a9971672c8 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:45:04 -0400 Subject: [PATCH 082/146] fix(aurora): fixed duration being a timedelta in case_safe --- aurora/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aurora/models.py b/aurora/models.py index 4792f4a..81963a0 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -228,6 +228,7 @@ class Moderation(AuroraGuildModel): case_safe = case.copy() case_safe.pop("guild_id") + case_safe["duration"] = str(case_safe["duration"]) if case_safe["duration"] else None case_safe["timestamp"] = case_safe["timestamp"].timestamp() case_safe["end_timestamp"] = case_safe["end_timestamp"].timestamp() if case_safe["end_timestamp"] else None case_safe["changes"] = dumps(case_safe["changes"]) -- 2.45.2 From 9c7e0b0b8953a9f01289de6e9639bbd7fff9c29f Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:48:16 -0400 Subject: [PATCH 083/146] fix(aurora): fixed a missing parameter --- aurora/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aurora/models.py b/aurora/models.py index 81963a0..589e198 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -264,6 +264,9 @@ class Moderation(AuroraGuildModel): case_safe["metadata"], ) + for change in case["changes"]: + change.update({"bot": bot}) + return cls.from_dict(bot=bot, data=case) class Change(AuroraBaseModel): -- 2.45.2 From a4e11fd82870b477edda3f4ebc96993d7c00604e Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 14:55:36 -0400 Subject: [PATCH 084/146] fix(aurora): fixing some model stuff --- aurora/models.py | 90 +++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 589e198..e857e5f 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -82,48 +82,53 @@ class Moderation(AuroraGuildModel): def update(self): from aurora.utilities.database import connect + from aurora.utilities.json import dumps query = f"UPDATE moderation_{self.guild_id} SET timestamp = ?, moderation_type = ?, target_type = ?, moderator_id = ?, role_id = ?, duration = ?, end_timestamp = ?, reason = ?, resolved = ?, resolved_by = ?, resolve_reason = ?, expired = ?, changes = ?, metadata = ? WHERE moderation_id = ?;" with connect() as database: cursor = database.cursor() cursor.execute(query, ( - self.timestamp, + self.timestamp.timestamp(), self.moderation_type, self.target_type, self.moderator_id, self.role_id, - self.duration, - self.end_timestamp, + str(self.duration) if self.duration else None, + self.end_timestamp.timestamp() if self.end_timestamp else None, self.reason, self.resolved, self.resolved_by, self.resolve_reason, self.expired, - self.changes, - self.metadata, - self.moderation_id + dumps(self.changes), + dumps(self.metadata), + self.moderation_id, )) cursor.close() logger.info("Updated moderation case %s in guild %s with the following data:\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", self.moderation_id, self.guild_id, - self.timestamp, + self.timestamp.timestamp(), self.moderation_type, self.target_type, self.moderator_id, self.role_id, - self.duration, - self.end_timestamp, + str(self.duration) if self.duration else None, + self.end_timestamp.timestamp() if self.end_timestamp else None, self.reason, self.resolved, self.resolved_by, self.resolve_reason, self.expired, - self.changes, - self.metadata, + dumps(self.changes), + dumps(self.metadata), ) + @classmethod + def from_dict(cls, bot: Red, data: dict) -> "Moderation": + return cls(bot=bot, **data) + @classmethod def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> Optional["Moderation"]: from aurora.utilities.database import connect @@ -141,10 +146,6 @@ class Moderation(AuroraGuildModel): return None - @classmethod - def from_dict(cls, bot: Red, data: dict) -> "Moderation": - return cls(bot=bot, **data) - @classmethod def log( cls, @@ -207,67 +208,54 @@ class Moderation(AuroraGuildModel): moderation_id = get_next_case_number(guild_id=guild_id, cursor=cursor) case = { - "guild_id": guild_id, "moderation_id": moderation_id, - "timestamp": timestamp, + "timestamp": timestamp.timestamp(), "moderation_type": moderation_type, "target_type": target_type, "target_id": target_id, "moderator_id": moderator_id, "role_id": role_id, - "duration": duration, - "end_timestamp": end_timestamp, + "duration": str(duration) if duration else None, + "end_timestamp": end_timestamp.timestamp() if end_timestamp else None, "reason": reason, "resolved": resolved, "resolved_by": resolved_by, "resolve_reason": resolved_reason, "expired": expired, - "changes": changes, - "metadata": metadata + "changes": dumps(changes), + "metadata": dumps(metadata) } - case_safe = case.copy() - case_safe.pop("guild_id") - case_safe["duration"] = str(case_safe["duration"]) if case_safe["duration"] else None - case_safe["timestamp"] = case_safe["timestamp"].timestamp() - case_safe["end_timestamp"] = case_safe["end_timestamp"].timestamp() if case_safe["end_timestamp"] else None - case_safe["changes"] = dumps(case_safe["changes"]) - case_safe["metadata"] = dumps(case_safe["metadata"]) - sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - cursor.execute(sql, tuple(case_safe.values())) + cursor.execute(sql, tuple(case.values())) cursor.close() database.commit() if close_db: database.close() - case_safe.update({"guild_id": guild_id}) logger.debug( "Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", guild_id, - case_safe["moderation_id"], - case_safe["timestamp"], - case_safe["moderation_type"], - case_safe["target_type"], - case_safe["target_id"], - case_safe["moderator_id"], - case_safe["role_id"], - case_safe["duration"], - case_safe["end_timestamp"], - case_safe["reason"], - case_safe["resolved"], - case_safe["resolved_by"], - case_safe["resolve_reason"], - case_safe["expired"], - case_safe["changes"], - case_safe["metadata"], + case["moderation_id"], + case["timestamp"], + case["moderation_type"], + case["target_type"], + case["target_id"], + case["moderator_id"], + case["role_id"], + case["duration"], + case["end_timestamp"], + case["reason"], + case["resolved"], + case["resolved_by"], + case["resolve_reason"], + case["expired"], + case["changes"], + case["metadata"], ) - for change in case["changes"]: - change.update({"bot": bot}) - - return cls.from_dict(bot=bot, data=case) + return cls.from_sql(bot=bot, moderation_id=moderation_id, guild_id=guild_id) class Change(AuroraBaseModel): type: Literal["ORIGINAL", "RESOLVE", "EDIT"] -- 2.45.2 From 8d1a57165d2df770ad7618d1321cb8cba0409547 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 15:00:54 -0400 Subject: [PATCH 085/146] fix(aurora): added debug logging --- aurora/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index e857e5f..07c73e6 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -106,7 +106,7 @@ class Moderation(AuroraGuildModel): )) cursor.close() - logger.info("Updated moderation case %s in guild %s with the following data:\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", + logger.info("Row updated in moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", self.moderation_id, self.guild_id, self.timestamp.timestamp(), @@ -277,6 +277,7 @@ class Change(AuroraBaseModel): @classmethod def from_dict(cls, bot: Red, data: dict) -> "Change": + logger.debug("Creating Change from dict: %s", data) if "duration" in data and data["duration"] and not isinstance(data["duration"], timedelta): hours, minutes, seconds = map(int, data["duration"].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) -- 2.45.2 From e14845ef85a688a87f6abb5423ad162ff5d58bf5 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 15:03:41 -0400 Subject: [PATCH 086/146] fix(aurora): make sure the user_id in changes is always an integer --- aurora/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 07c73e6..7f32944 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -301,7 +301,8 @@ class Change(AuroraBaseModel): data.update({ "timestamp": timestamp, "end_timestamp": end_timestamp, - "duration": duration + "duration": duration, + "user_id": int(data["user_id"]) }) return cls(bot=bot, **data) -- 2.45.2 From 5be5a39c54a055c610ab7808e6cf878f95c511a4 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 15:06:10 -0400 Subject: [PATCH 087/146] fix(aurora): check type of dictionary in debug log --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 7f32944..20b1bf4 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -277,7 +277,7 @@ class Change(AuroraBaseModel): @classmethod def from_dict(cls, bot: Red, data: dict) -> "Change": - logger.debug("Creating Change from dict: %s", data) + logger.debug("Creating Change from dict (%s): %s", type(data), data) if "duration" in data and data["duration"] and not isinstance(data["duration"], timedelta): hours, minutes, seconds = map(int, data["duration"].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) -- 2.45.2 From 16af26f93e4a0bfe56250ba27929fa259dbd4eff Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 15:11:11 -0400 Subject: [PATCH 088/146] fix(aurora): fixed a logging statement --- aurora/models.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 20b1bf4..396671e 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -1,3 +1,4 @@ +import json import sqlite3 from datetime import datetime, timedelta from time import time @@ -106,7 +107,7 @@ class Moderation(AuroraGuildModel): )) cursor.close() - logger.info("Row updated in moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", + logger.debug("Row updated in moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", self.moderation_id, self.guild_id, self.timestamp.timestamp(), @@ -278,6 +279,9 @@ class Change(AuroraBaseModel): @classmethod def from_dict(cls, bot: Red, data: dict) -> "Change": logger.debug("Creating Change from dict (%s): %s", type(data), data) + if isinstance(data, str): + data = json.loads(data) + logger.debug("Change data was a string, converted to dict: %s", data) if "duration" in data and data["duration"] and not isinstance(data["duration"], timedelta): hours, minutes, seconds = map(int, data["duration"].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) -- 2.45.2 From dcda128f113ef9093806667178a81420671ff29b Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 15:15:54 -0400 Subject: [PATCH 089/146] fix(aurora): edit command will now edit reasons as intended --- aurora/aurora.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 1572195..9731767 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -19,7 +19,8 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (box, error, humanize_list, + humanize_timedelta, warning) from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -29,11 +30,19 @@ from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides from aurora.models import Change, Moderation from aurora.utilities.config import config, register_config -from aurora.utilities.database import connect, create_guild_table, fetch_case, mysql_log -from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed +from aurora.utilities.database import (connect, create_guild_table, fetch_case, + mysql_log) +from aurora.utilities.factory import (addrole_embed, case_factory, + changes_factory, evidenceformat_factory, + guild_embed, immune_embed, + message_factory, overrides_embed) from aurora.utilities.json import dump, dumps from aurora.utilities.logger import logger -from aurora.utilities.utils import check_moddable, check_permissions, fetch_channel_dict, fetch_user_dict, generate_dict, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta +from aurora.utilities.utils import (check_moddable, check_permissions, + fetch_channel_dict, fetch_user_dict, + generate_dict, get_footer_image, log, + send_evidenceformat, + timedelta_from_relativedelta) class Aurora(commands.Cog): @@ -1531,6 +1540,9 @@ class Aurora(commands.Cog): except discord.NotFound: pass + if reason: + moderation.reason = reason + if not moderation.changes: moderation.changes.append(Change.from_dict(interaction.client, { "type": "ORIGINAL", -- 2.45.2 From d375716fbf24f596bb44bfa40822c728cf7341dc Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 15:29:48 -0400 Subject: [PATCH 090/146] feat(aurora): added a resolve function to the Moderation model --- aurora/models.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/aurora/models.py b/aurora/models.py index 396671e..2ad8de2 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta from time import time from typing import Any, Dict, List, Literal, Optional, Union +import discord from discord import Forbidden, HTTPException, InvalidData, NotFound from pydantic import BaseModel, ConfigDict from redbot.core.bot import Red @@ -81,6 +82,50 @@ class Moderation(AuroraGuildModel): def __str__(self): return f"{self.moderation_type} {self.target_type} {self.target_id} {self.reason}" + async def resolve(self, resolved_by: int, reason: str): + if self.resolved: + raise ValueError("Case is already resolved!") + + self.resolved = True + self.resolved_by = resolved_by + self.resolve_reason = reason + + if self.type == "MUTE": + try: + guild: discord.Guild = await self.bot.fetch_guild(self.guild_id) + member = await guild.fetch_member(self.target_id) + + await member.timeout( + None, reason=f"Case {self.moderation_id} resolved by {resolved_by}{" for" + reason if reason else ""}" + ) + except NotFound: + pass + + if self.type in ["BAN", "TEMPBAN"]: + try: + guild: discord.Guild = await self.bot.fetch_guild(self.guild_id) + await guild.unban(await self.get_target(), reason=f"Case {self.moderation_id} resolved by {resolved_by}{" for" + reason if reason else ""}") + except NotFound: + pass + + if not self.changes: + self.changes.append(Change.from_dict(self.bot, { + "type": "ORIGINAL", + "timestamp": self.timestamp, + "reason": self.reason, + "user_id": self.moderator_id, + "duration": self.duration, + "end_timestamp": self.end_timestamp, + })) + self.changes.append(Change.from_dict(self.bot, { + "type": "RESOLVE", + "timestamp": datetime.now(), + "reason": reason, + "user_id": resolved_by, + })) + + self.update() + def update(self): from aurora.utilities.database import connect from aurora.utilities.json import dumps -- 2.45.2 From 834d116b2035dfa2a1f116d8c9f0defe65db7ddd Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 15:32:01 -0400 Subject: [PATCH 091/146] fix(aurora): fixed a syntax error --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index 2ad8de2..6c37bd5 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -96,7 +96,7 @@ class Moderation(AuroraGuildModel): member = await guild.fetch_member(self.target_id) await member.timeout( - None, reason=f"Case {self.moderation_id} resolved by {resolved_by}{" for" + reason if reason else ""}" + None, reason=f"Case {self.moderation_id} resolved by {resolved_by}{' for '+ reason if reason else ''}" ) except NotFound: pass @@ -104,7 +104,7 @@ class Moderation(AuroraGuildModel): if self.type in ["BAN", "TEMPBAN"]: try: guild: discord.Guild = await self.bot.fetch_guild(self.guild_id) - await guild.unban(await self.get_target(), reason=f"Case {self.moderation_id} resolved by {resolved_by}{" for" + reason if reason else ""}") + await guild.unban(await self.get_target(), reason=f"Case {self.moderation_id} resolved by {resolved_by}{' for '+ reason if reason else ''}") except NotFound: pass -- 2.45.2 From f8968e8e9ee0a1ac3ca9791df17ac0f5ea3e37a2 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 15:59:43 -0400 Subject: [PATCH 092/146] feat(aurora): updated `/history` command --- aurora/aurora.py | 491 ++++++++++++++++++----------------------------- aurora/models.py | 13 +- 2 files changed, 199 insertions(+), 305 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 9731767..d764521 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -11,6 +11,7 @@ import sqlite3 import time from datetime import datetime, timedelta, timezone from math import ceil +from typing import List import discord from discord import Object @@ -19,8 +20,7 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import (box, error, humanize_list, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -30,19 +30,11 @@ from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides from aurora.models import Change, Moderation from aurora.utilities.config import config, register_config -from aurora.utilities.database import (connect, create_guild_table, fetch_case, - mysql_log) -from aurora.utilities.factory import (addrole_embed, case_factory, - changes_factory, evidenceformat_factory, - guild_embed, immune_embed, - message_factory, overrides_embed) -from aurora.utilities.json import dump, dumps +from aurora.utilities.database import connect, create_guild_table, fetch_case, mysql_log +from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed +from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import (check_moddable, check_permissions, - fetch_channel_dict, fetch_user_dict, - generate_dict, get_footer_image, log, - send_evidenceformat, - timedelta_from_relativedelta) +from aurora.utilities.utils import check_moddable, check_permissions, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta class Aurora(commands.Cog): @@ -1122,15 +1114,15 @@ class Aurora(commands.Cog): cursor.execute(query) results = cursor.fetchall() - result_dict_list = [] + moderation_list: List[Moderation] = [] for result in results: - case_dict = generate_dict(result) - if case_dict["moderation_id"] == 0: - continue - result_dict_list.append(case_dict) + if result["moderation_id"] != 0: + result.update({"guild_id": interaction.guild.id}) + moderation = Moderation.from_dict(interaction.client, dict(result)) + moderation_list.append(moderation) - case_quantity = len(result_dict_list) + case_quantity = len(moderation_list) page_quantity = ceil(case_quantity / pagesize) start_index = (page - 1) * pagesize end_index = page * pagesize @@ -1143,74 +1135,44 @@ class Aurora(commands.Cog): memory_dict = {} - for case in result_dict_list[start_index:end_index]: - if case["target_id"] not in memory_dict: - if case["target_type"] == "USER": - memory_dict[str(case["target_id"])] = await fetch_user_dict( - interaction.client, case["target_id"] - ) - elif case["target_type"] == "CHANNEL": - memory_dict[str(case["target_id"])] = await fetch_channel_dict( - interaction.guild, case["target_id"] - ) - target_user = memory_dict[str(case["target_id"])] + for mod in moderation_list[start_index:end_index]: + if mod.target_id not in memory_dict: + memory_dict.update({ + str(mod.target_id): await mod.get_target() + }) + target = memory_dict[str(mod.target_id)] - if case["target_type"] == "USER": - target_name = ( - f"`{target_user['name']}`" - if target_user["discriminator"] == "0" - else f"`{target_user['name']}#{target_user['discriminator']}`" - ) - elif case["target_type"] == "CHANNEL": - target_name = f"`{target_user['mention']}`" - - if case["moderator_id"] not in memory_dict: - memory_dict[str(case["moderator_id"])] = await fetch_user_dict( - interaction.client, case["moderator_id"] - ) - moderator_user = memory_dict[str(case["moderator_id"])] - moderator_name = ( - f"`{moderator_user['name']}`" - if moderator_user["discriminator"] == "0" - else f"`{moderator_user['name']}#{moderator_user['discriminator']}`" - ) + if mod.moderator_id not in memory_dict: + memory_dict.update({ + str(mod.moderator_id): await mod.get_moderator() + }) + moderator = memory_dict[str(mod.moderator_id)] field_name = f"Case #{case['moderation_id']:,} ({str.title(case['moderation_type'])})" - field_value = f"**Target:** {target_name} ({target_user['id']})\n**Moderator:** {moderator_name} ({moderator_user['id']})" + field_value = f"**Target:** `{target.name}` ({target.id})\n**Moderator:** `{moderator.name}` ({moderator.id})" if len(case["reason"]) > 125: - field_value += f"\n**Reason:** `{str(case['reason'])[:125]}...`" + field_value += f"\n**Reason:** `{str(mod.reason)[:125]}...`" else: - field_value += f"\n**Reason:** `{str(case['reason'])}`" + field_value += f"\n**Reason:** `{str(mod.reason)}`" - if case["duration"] != "NULL": - td = timedelta( - **{ - unit: int(val) - for unit, val in zip( - ["hours", "minutes", "seconds"], case["duration"].split(":") - ) - } - ) + if mod.duration: duration_embed = ( - f"{humanize_timedelta(timedelta=td)} | " - if bool(case["expired"]) is False - else f"{humanize_timedelta(timedelta=td)} | Expired" + f"{humanize_timedelta(timedelta=mod.duration)} | " + if mod.expired is False + else f"{humanize_timedelta(timedelta=mod.duration)} | Expired" ) field_value += f"\n**Duration:** {duration_embed}" field_value += ( - f"\n**Timestamp:** | " + f"\n**Timestamp:** | " ) - if case["role_id"] != "0": - role = interaction.guild.get_role(int(case["role_id"])) - if role is not None: - field_value += f"\n**Role:** {role.mention}" - else: - field_value += f"\n**Role:** Deleted Role ({case['role_id']})" + if mod.role_id: + role = await mod.get_role() + field_value += f"\n**Role:** {role.mention} ({role.id})" - if bool(case["resolved"]): + if mod.resolved: field_value += "\n**Resolved:** True" embed.add_field(name=field_name, value=field_value, inline=inline) @@ -1243,39 +1205,14 @@ class Aurora(commands.Cog): ) return - database = connect() - cursor = database.cursor() - - query_1 = ( - f"SELECT * FROM moderation_{interaction.guild.id} WHERE moderation_id = ?;" - ) - cursor.execute(query_1, (case,)) - result_1 = cursor.fetchone() - if result_1 is None or case == 0: + try: + moderation = Moderation.from_sql(interaction.client, case, interaction.guild.id) + except ValueError: await interaction.response.send_message( - content=error(f"There is no moderation with a case number of {case}."), - ephemeral=True, + content=error(f"Case #{case:,} does not exist!"), ephemeral=True ) return - - query_2 = f"SELECT * FROM moderation_{interaction.guild.id} WHERE moderation_id = ? AND resolved = 0;" - cursor.execute(query_2, (case,)) - result_2 = cursor.fetchone() - if result_2 is None: - await interaction.response.send_message( - content=error( - f"This moderation has already been resolved!\nUse `/case {case}` for more information." - ), - ephemeral=True, - ) - return - - case_dict = generate_dict(result_2) - if reason is None: - reason = "No reason given." - - changes: list = case_dict["changes"] - if len(changes) > 25: + if len(moderation.changes) > 25: await interaction.response.send_message( content=error( "Due to limitations with Discord's embed system, you cannot edit a case more than 25 times." @@ -1283,68 +1220,18 @@ class Aurora(commands.Cog): ephemeral=True, ) return - if not changes: - changes.append( - { - "type": "ORIGINAL", - "timestamp": case_dict["timestamp"], - "reason": case_dict["reason"], - "user_id": case_dict["moderator_id"], - } - ) - changes.append( - { - "type": "RESOLVE", - "timestamp": int(time.time()), - "reason": reason, - "user_id": interaction.user.id, - } - ) - if case_dict["moderation_type"] in ["UNMUTE", "UNBAN"]: - await interaction.response.send_message( - content=error("You cannot resolve this type of moderation!"), - ephemeral=True, - ) - return - - if case_dict["moderation_type"] in ["MUTE", "TEMPBAN", "BAN"]: - if case_dict["moderation_type"] == "MUTE": - try: - member = await interaction.guild.fetch_member( - case_dict["target_id"] - ) - - await member.timeout( - None, reason=f"Case #{case:,} resolved by {interaction.user.id}" - ) - except discord.NotFound: - pass - - if case_dict["moderation_type"] in ["TEMPBAN", "BAN"]: - try: - user = await interaction.client.fetch_user(case_dict["target_id"]) - - await interaction.guild.unban( - user, reason=f"Case #{case} resolved by {interaction.user.id}" - ) - except discord.NotFound: - pass - - resolve_query = f"UPDATE `moderation_{interaction.guild.id}` SET resolved = 1, changes = ?, resolved_by = ?, resolve_reason = ? WHERE moderation_id = ?" - else: - resolve_query = f"UPDATE `moderation_{interaction.guild.id}` SET resolved = 1, changes = ?, resolved_by = ?, resolve_reason = ? WHERE moderation_id = ?" - - cursor.execute( - resolve_query, - ( - dumps(changes), - interaction.user.id, - reason, - case_dict["moderation_id"], - ), - ) - database.commit() + try: + await moderation.resolve(interaction.user.id, reason) + except (ValueError, TypeError) as e: + if e == ValueError: + await interaction.response.send_message( + content=error("This case has already been resolved!"), ephemeral=True + ) + elif e == TypeError: + await interaction.response.send_message( + content=error("This case type cannot be resolved!"), ephemeral=True + ) embed = await case_factory( interaction=interaction, @@ -1355,9 +1242,6 @@ class Aurora(commands.Cog): ) await log(interaction, case, resolved=True) - cursor.close() - database.close() - @app_commands.command(name="case") @app_commands.choices( export=[ @@ -1405,67 +1289,69 @@ class Aurora(commands.Cog): or False ) - if case != 0: + try: mod = Moderation.from_sql(interaction.client, case, interaction.guild.id) - if mod: - if export: - if export.value == "file" or len(mod.to_json(2)) > 1800: - filename = ( - str(data_manager.cog_data_path(cog_instance=self)) - + str(os.sep) - + f"moderation_{interaction.guild.id}_case_{case}.json" - ) + except ValueError: + await interaction.response.send_message( + content=error(f"Case #{case:,} does not exist!"), ephemeral=True + ) + return - with open(filename, "w", encoding="utf-8") as f: - mod.to_json(2, f) - if export.value == "codeblock": - content = f"Case #{case:,} exported.\n" + warning( - "Case was too large to export as codeblock, so it has been uploaded as a `.json` file." - ) - else: - content = f"Case #{case:,} exported." + if export: + if export.value == "file" or len(mod.to_json(2)) > 1800: + filename = ( + str(data_manager.cog_data_path(cog_instance=self)) + + str(os.sep) + + f"moderation_{interaction.guild.id}_case_{case}.json" + ) - await interaction.response.send_message( - content=content, - file=discord.File( - filename, - f"moderation_{interaction.guild.id}_case_{case}.json", - ), - ephemeral=ephemeral, - ) - - os.remove(filename) - return - await interaction.response.send_message( - content=box(mod.to_json(2), 'json'), - ephemeral=ephemeral, - ) - return - if changes: - embed = await changes_factory( - interaction=interaction, moderation=mod - ) - await interaction.response.send_message( - embed=embed, ephemeral=ephemeral - ) - elif evidenceformat: - content = await evidenceformat_factory( - interaction=interaction, moderation=mod - ) - await interaction.response.send_message( - content=content, ephemeral=ephemeral + with open(filename, "w", encoding="utf-8") as f: + mod.to_json(2, f) + if export.value == "codeblock": + content = f"Case #{case:,} exported.\n" + warning( + "Case was too large to export as codeblock, so it has been uploaded as a `.json` file." ) else: - embed = await case_factory( - interaction=interaction, moderation=mod - ) - await interaction.response.send_message( - embed=embed, ephemeral=ephemeral - ) + content = f"Case #{case:,} exported." + + await interaction.response.send_message( + content=content, + file=discord.File( + filename, + f"moderation_{interaction.guild.id}_case_{case}.json", + ), + ephemeral=ephemeral, + ) + + os.remove(filename) return - await interaction.response.send_message( - content=f"No case with case number `{case}` found.", ephemeral=True - ) + await interaction.response.send_message( + content=box(mod.to_json(2), 'json'), + ephemeral=ephemeral, + ) + return + if changes: + embed = await changes_factory( + interaction=interaction, moderation=mod + ) + await interaction.response.send_message( + embed=embed, ephemeral=ephemeral + ) + elif evidenceformat: + content = await evidenceformat_factory( + interaction=interaction, moderation=mod + ) + await interaction.response.send_message( + content=content, ephemeral=ephemeral + ) + else: + embed = await case_factory( + interaction=interaction, moderation=mod + ) + await interaction.response.send_message( + embed=embed, ephemeral=ephemeral + ) + return @app_commands.command(name="edit") async def edit( @@ -1498,93 +1384,96 @@ class Aurora(commands.Cog): ) return - if case != 0: + try: moderation = Moderation.from_sql(interaction.client, case, interaction.guild.id) old_moderation = moderation - if moderation: - if len(moderation.changes) > 25: - return await interaction.response.send_message( - content=error( - "Due to limitations with Discord's embed system, you cannot edit a case more than 25 times." - ), - ephemeral=True, - ) - if duration: - moderation.duration = parse_timedelta(duration) - if moderation.duration is None: - return await interaction.response.send_message( - error("Please provide a valid duration!"), ephemeral=True - ) + except ValueError: + await interaction.response.send_message( + content=error(f"Case #{case:,} does not exist!"), ephemeral=True + ) + return - moderation.end_timestamp = moderation.timestamp + moderation.duration.total_seconds() + if len(moderation.changes) > 25: + return await interaction.response.send_message( + content=error( + "Due to limitations with Discord's embed system, you cannot edit a case more than 25 times." + ), + ephemeral=True, + ) - if moderation.type == "MUTE": - if ( - time.time() - moderation.unix_timestamp - ) + moderation.duration.total_seconds() > 2419200: - return await interaction.response.send_message( - error( - "Please provide a duration that is less than 28 days from the initial moderation." - ) - ) - - try: - member = await interaction.guild.fetch_member( - moderation.target_id - ) - - await member.timeout( - moderation.duration, - reason=f"Case #{case:,} edited by {interaction.user.id}", - ) - except discord.NotFound: - pass - - if reason: - moderation.reason = reason - - if not moderation.changes: - moderation.changes.append(Change.from_dict(interaction.client, { - "type": "ORIGINAL", - "timestamp": old_moderation.timestamp, - "reason": old_moderation.reason, - "user_id": old_moderation.moderator_id, - "duration": old_moderation.duration, - "end_timestamp": old_moderation.end_timestamp, - })) - if duration: - moderation.changes.append(Change.from_dict(interaction.client, { - "type": "EDIT", - "timestamp": int(time.time()), - "reason": reason, - "user_id": interaction.user.id, - "duration": moderation.duration, - "end_timestamp": moderation.end_timestamp, - })) - else: - moderation.changes.append(Change.from_dict(interaction.client, { - "type": "EDIT", - "timestamp": int(time.time()), - "reason": reason, - "user_id": interaction.user.id, - "duration": moderation.duration, - "end_timestamp": moderation.end_timestamp, - })) - - moderation.update() - embed = await case_factory(interaction=interaction, moderation=moderation) - - await interaction.response.send_message( - content=f"✅ Moderation #{case:,} edited!", - embed=embed, - ephemeral=True, + if duration: + moderation.duration = parse_timedelta(duration) + if moderation.duration is None: + return await interaction.response.send_message( + error("Please provide a valid duration!"), ephemeral=True ) - await log(interaction, case) - return + moderation.end_timestamp = moderation.timestamp + moderation.duration.total_seconds() + + if moderation.type == "MUTE": + if ( + time.time() - moderation.unix_timestamp + ) + moderation.duration.total_seconds() > 2419200: + return await interaction.response.send_message( + error( + "Please provide a duration that is less than 28 days from the initial moderation." + ) + ) + + try: + member = await interaction.guild.fetch_member( + moderation.target_id + ) + + await member.timeout( + moderation.duration, + reason=f"Case #{case:,} edited by {interaction.user.id}", + ) + except discord.NotFound: + pass + + if reason: + moderation.reason = reason + + if not moderation.changes: + moderation.changes.append(Change.from_dict(interaction.client, { + "type": "ORIGINAL", + "timestamp": old_moderation.timestamp, + "reason": old_moderation.reason, + "user_id": old_moderation.moderator_id, + "duration": old_moderation.duration, + "end_timestamp": old_moderation.end_timestamp, + })) + if duration: + moderation.changes.append(Change.from_dict(interaction.client, { + "type": "EDIT", + "timestamp": int(time.time()), + "reason": reason, + "user_id": interaction.user.id, + "duration": moderation.duration, + "end_timestamp": moderation.end_timestamp, + })) + else: + moderation.changes.append(Change.from_dict(interaction.client, { + "type": "EDIT", + "timestamp": int(time.time()), + "reason": reason, + "user_id": interaction.user.id, + "duration": moderation.duration, + "end_timestamp": moderation.end_timestamp, + })) + + moderation.update() + embed = await case_factory(interaction=interaction, moderation=moderation) + await interaction.response.send_message( - content=error(f"No case with case number `{case}` found."), ephemeral=True + content=f"✅ Moderation #{case:,} edited!", + embed=embed, + ephemeral=True, ) + await log(interaction, case) + + return @tasks.loop(minutes=1) async def handle_expiry(self): diff --git a/aurora/models.py b/aurora/models.py index 6c37bd5..8d42e21 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -90,6 +90,9 @@ class Moderation(AuroraGuildModel): self.resolved_by = resolved_by self.resolve_reason = reason + if self.type in ["UNMUTE", "UNBAN"]: + raise TypeError("Cannot resolve an unmute or unban case!") + if self.type == "MUTE": try: guild: discord.Guild = await self.bot.fetch_guild(self.guild_id) @@ -176,7 +179,7 @@ class Moderation(AuroraGuildModel): return cls(bot=bot, **data) @classmethod - def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> Optional["Moderation"]: + def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": from aurora.utilities.database import connect query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;" @@ -184,13 +187,13 @@ class Moderation(AuroraGuildModel): cursor = database.cursor() cursor.execute(query, (moderation_id,)) result = cursor.fetchone() + cursor.close() - if result: + if result and not moderation_id == 0: case = generate_dict(bot, result, guild_id) - cursor.close() return cls.from_dict(bot, case) - return None + raise ValueError(f"Case {moderation_id} not found in moderation_{guild_id}!") @classmethod def log( @@ -411,6 +414,8 @@ class PartialRole(AuroraGuildModel): @property def mention(self): + if self.name == "Deleted Role" or self.name == "Forbidden Role": + return self.name return f"<@&{self.id}>" def __str__(self): -- 2.45.2 From d7a8fbe36721cb8362f856a554f1038ba5db3f2d Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 16:02:00 -0400 Subject: [PATCH 093/146] fix(aurora): ignore moderations with the id 0 in the history command --- aurora/aurora.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index d764521..e071f15 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -20,7 +20,8 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (box, error, humanize_list, + humanize_timedelta, warning) from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -30,11 +31,17 @@ from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides from aurora.models import Change, Moderation from aurora.utilities.config import config, register_config -from aurora.utilities.database import connect, create_guild_table, fetch_case, mysql_log -from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed +from aurora.utilities.database import (connect, create_guild_table, fetch_case, + mysql_log) +from aurora.utilities.factory import (addrole_embed, case_factory, + changes_factory, evidenceformat_factory, + guild_embed, immune_embed, + message_factory, overrides_embed) from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import check_moddable, check_permissions, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta +from aurora.utilities.utils import (check_moddable, check_permissions, + get_footer_image, log, send_evidenceformat, + timedelta_from_relativedelta) class Aurora(commands.Cog): @@ -1098,18 +1105,19 @@ class Aurora(commands.Cog): if target: query = f"""SELECT * FROM moderation_{interaction.guild.id} - WHERE target_id = ? + WHERE target_id = ? AND moderation_id != 0 ORDER BY moderation_id DESC;""" cursor.execute(query, (target.id,)) elif moderator: query = f"""SELECT * FROM moderation_{interaction.guild.id} - WHERE moderator_id = ? + WHERE moderator_id = ? AND moderation_id != 0 ORDER BY moderation_id DESC;""" cursor.execute(query, (moderator.id,)) else: query = f"""SELECT * FROM moderation_{interaction.guild.id} + WHERE moderation_id != 0 ORDER BY moderation_id DESC;""" cursor.execute(query) @@ -1117,10 +1125,9 @@ class Aurora(commands.Cog): moderation_list: List[Moderation] = [] for result in results: - if result["moderation_id"] != 0: - result.update({"guild_id": interaction.guild.id}) - moderation = Moderation.from_dict(interaction.client, dict(result)) - moderation_list.append(moderation) + result.update({"guild_id": interaction.guild.id}) + moderation = Moderation.from_dict(interaction.client, dict(result)) + moderation_list.append(moderation) case_quantity = len(moderation_list) page_quantity = ceil(case_quantity / pagesize) -- 2.45.2 From 53b67e1c9513c01e9937a961a76855212031d2a8 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 16:07:35 -0400 Subject: [PATCH 094/146] fix(aurora): convert results into a dictionary before making a Moderation instance out of them --- aurora/aurora.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index e071f15..5777031 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -20,8 +20,7 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import (box, error, humanize_list, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -31,17 +30,11 @@ from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides from aurora.models import Change, Moderation from aurora.utilities.config import config, register_config -from aurora.utilities.database import (connect, create_guild_table, fetch_case, - mysql_log) -from aurora.utilities.factory import (addrole_embed, case_factory, - changes_factory, evidenceformat_factory, - guild_embed, immune_embed, - message_factory, overrides_embed) +from aurora.utilities.database import connect, create_guild_table, fetch_case, mysql_log +from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import (check_moddable, check_permissions, - get_footer_image, log, send_evidenceformat, - timedelta_from_relativedelta) +from aurora.utilities.utils import check_moddable, check_permissions, generate_dict, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta class Aurora(commands.Cog): @@ -1125,8 +1118,8 @@ class Aurora(commands.Cog): moderation_list: List[Moderation] = [] for result in results: - result.update({"guild_id": interaction.guild.id}) - moderation = Moderation.from_dict(interaction.client, dict(result)) + result_dictionary = generate_dict(result) + moderation = Moderation.from_dict(interaction.client, result_dictionary) moderation_list.append(moderation) case_quantity = len(moderation_list) -- 2.45.2 From d7ca5cab46e9cae8a7e962f15e8b8bf3c3d81b35 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 16:34:08 -0400 Subject: [PATCH 095/146] feat(aurora): cleaned up the codebase and fixed a whole bunch of bugs --- aurora/aurora.py | 165 ++++++++++++++++---------------- aurora/importers/galacticbot.py | 19 ++-- aurora/models.py | 42 +++++++- aurora/utilities/database.py | 138 ++------------------------ aurora/utilities/utils.py | 127 +++--------------------- 5 files changed, 148 insertions(+), 343 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 5777031..695320b 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -20,7 +20,8 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (box, error, humanize_list, + humanize_timedelta, warning) from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -30,11 +31,17 @@ from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides from aurora.models import Change, Moderation from aurora.utilities.config import config, register_config -from aurora.utilities.database import connect, create_guild_table, fetch_case, mysql_log -from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed +from aurora.utilities.database import connect, create_guild_table +from aurora.utilities.factory import (addrole_embed, case_factory, + changes_factory, evidenceformat_factory, + guild_embed, immune_embed, + message_factory, overrides_embed) + from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import check_moddable, check_permissions, generate_dict, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta +from aurora.utilities.utils import (check_moddable, check_permissions, + get_footer_image, log, send_evidenceformat, + timedelta_from_relativedelta) class Aurora(commands.Cog): @@ -142,7 +149,7 @@ class Aurora(commands.Cog): if entry.user.id == self.bot.user.id: return - duration = "NULL" + duration = None if entry.reason: reason = entry.reason + " (This action was performed without the bot.)" @@ -175,13 +182,14 @@ class Aurora(commands.Cog): else: return - await mysql_log( + Moderation.log( + self.bot, entry.guild.id, entry.user.id, moderation_type, "USER", entry.target.id, - 0, + None, duration, reason, ) @@ -231,23 +239,22 @@ class Aurora(commands.Cog): except discord.errors.HTTPException: pass - moderation_id = await mysql_log( + moderation = Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "NOTE", "USER", target.id, - 0, - "NULL", + None, + None, reason, ) await interaction.edit_original_response( - content=f"{target.mention} has received a note! (Case `#{moderation_id:,}`)\n**Reason** - `{reason}`" + content=f"{target.mention} has received a note! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`" ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) @app_commands.command(name="warn") async def warn( @@ -290,23 +297,22 @@ class Aurora(commands.Cog): except discord.errors.HTTPException: pass - moderation_id = await mysql_log( + moderation = Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "WARN", "USER", target.id, - 0, - "NULL", + None, + None, reason, ) await interaction.edit_original_response( - content=f"{target.mention} has been warned! (Case `#{moderation_id:,}`)\n**Reason** - `{reason}`" + content=f"{target.mention} has been warned! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`" ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) @app_commands.command(name="addrole") async def addrole( @@ -349,7 +355,7 @@ class Aurora(commands.Cog): ) return else: - parsed_time = "NULL" + parsed_time = None if role.id not in addrole_whitelist: await interaction.response.send_message( @@ -396,7 +402,8 @@ class Aurora(commands.Cog): content=f"{target.mention} has been given the {role.mention} role{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time != 'NULL' else ''}!\n**Reason** - `{reason}`" ) - moderation_id = await mysql_log( + moderation = Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "ADDROLE", @@ -407,12 +414,10 @@ class Aurora(commands.Cog): reason, ) await response.edit( - content=f"{target.mention} has been given the {role.mention} role{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time != 'NULL' else ''}! (Case `#{moderation_id:,}`)\n**Reason** - `{reason}`", + content=f"{target.mention} has been given the {role.mention} role{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time != 'NULL' else ''}! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`", ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) @app_commands.command(name="removerole") async def removerole( @@ -455,7 +460,7 @@ class Aurora(commands.Cog): ) return else: - parsed_time = "NULL" + parsed_time = None if role.id not in addrole_whitelist: await interaction.response.send_message( @@ -502,7 +507,8 @@ class Aurora(commands.Cog): content=f"{target.mention} has had the {role.mention} role removed{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time != 'NULL' else ''}!\n**Reason** - `{reason}`" ) - moderation_id = await mysql_log( + moderation = Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "REMOVEROLE", @@ -513,12 +519,10 @@ class Aurora(commands.Cog): reason, ) await response.edit( - content=f"{target.mention} has had the {role.mention} role removed{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time != 'NULL' else ''}! (Case `#{moderation_id:,}`)\n**Reason** - `{reason}`", + content=f"{target.mention} has had the {role.mention} role removed{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time != 'NULL' else ''}! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`", ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) @app_commands.command(name="mute") async def mute( @@ -590,23 +594,22 @@ class Aurora(commands.Cog): except discord.errors.HTTPException: pass - moderation_id = await mysql_log( + moderation = Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "MUTE", "USER", target.id, - 0, + None, parsed_time, reason, ) await interaction.edit_original_response( - content=f"{target.mention} has been muted for {humanize_timedelta(timedelta=parsed_time)}! (Case `#{moderation_id:,}`)\n**Reason** - `{reason}`" + content=f"{target.mention} has been muted for {humanize_timedelta(timedelta=parsed_time)}! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`" ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) @app_commands.command(name="unmute") async def unmute( @@ -665,23 +668,22 @@ class Aurora(commands.Cog): except discord.errors.HTTPException: pass - moderation_id = await mysql_log( + moderation = await Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "UNMUTE", "USER", target.id, - 0, - "NULL", + None, + None, reason, ) await interaction.edit_original_response( - content=f"{target.mention} has been unmuted! (Case `#{moderation_id:,}`)\n**Reason** - `{reason}`" + content=f"{target.mention} has been unmuted! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`" ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) @app_commands.command(name="kick") async def kick( @@ -726,23 +728,22 @@ class Aurora(commands.Cog): await target.kick(reason=f"Kicked by {interaction.user.id} for: {reason}") - moderation_id = await mysql_log( + moderation = Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "KICK", "USER", target.id, - 0, - "NULL", + None, + None, reason, ) await interaction.edit_original_response( - content=f"{target.mention} has been kicked! (Case `#{moderation_id:,}`)\n**Reason** - `{reason}`" + content=f"{target.mention} has been kicked! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`" ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) @app_commands.command(name="ban") @app_commands.choices( @@ -834,23 +835,22 @@ class Aurora(commands.Cog): delete_message_seconds=delete_messages_seconds, ) - moderation_id = await mysql_log( + moderation = Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "TEMPBAN", "USER", target.id, - 0, + None, parsed_time, reason, ) await interaction.edit_original_response( - content=f"{target.mention} has been banned for {humanize_timedelta(timedelta=parsed_time)}! (Case `#{moderation_id}`)\n**Reason** - `{reason}`" + content=f"{target.mention} has been banned for {humanize_timedelta(timedelta=parsed_time)}! (Case `#{moderation.id}`)\n**Reason** - `{reason}`" ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) else: await interaction.response.send_message( content=f"{target.mention} has been banned!\n**Reason** - `{reason}`" @@ -878,7 +878,8 @@ class Aurora(commands.Cog): delete_message_seconds=delete_messages_seconds, ) - moderation_id = await mysql_log( + moderation = Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "BAN", @@ -889,12 +890,10 @@ class Aurora(commands.Cog): reason, ) await interaction.edit_original_response( - content=f"{target.mention} has been banned! (Case `#{moderation_id:,}`)\n**Reason** - `{reason}`" + content=f"{target.mention} has been banned! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`" ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) @app_commands.command(name="unban") async def unban( @@ -955,23 +954,22 @@ class Aurora(commands.Cog): except discord.errors.HTTPException: pass - moderation_id = await mysql_log( + moderation = Moderation.log( + interaction.client, interaction.guild.id, interaction.user.id, "UNBAN", "USER", target.id, - 0, - "NULL", + None, + None, reason, ) await interaction.edit_original_response( - content=f"{target.mention} has been unbanned! (Case `#{moderation_id:,}`)\n**Reason** - `{reason}`" + content=f"{target.mention} has been unbanned! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`" ) - await log(interaction, moderation_id) - - case = await fetch_case(moderation_id, interaction.guild.id) - await send_evidenceformat(interaction, case) + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) @app_commands.command(name="history") async def history( @@ -1118,8 +1116,7 @@ class Aurora(commands.Cog): moderation_list: List[Moderation] = [] for result in results: - result_dictionary = generate_dict(result) - moderation = Moderation.from_dict(interaction.client, result_dictionary) + moderation = Moderation.from_result(interaction.client, result, interaction.guild.id) moderation_list.append(moderation) case_quantity = len(moderation_list) @@ -1235,7 +1232,7 @@ class Aurora(commands.Cog): embed = await case_factory( interaction=interaction, - case_dict=await fetch_case(case, interaction.guild.id), + moderation=moderation, ) await interaction.response.send_message( content=f"✅ Moderation #{case:,} resolved!", embed=embed diff --git a/aurora/importers/galacticbot.py b/aurora/importers/galacticbot.py index 3d13b8e..574d5ad 100644 --- a/aurora/importers/galacticbot.py +++ b/aurora/importers/galacticbot.py @@ -7,7 +7,8 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands from redbot.core.utils.chat_formatting import box, warning -from ..utilities.database import connect, create_guild_table, mysql_log +from aurora.models import Change, Moderation +from aurora.utilities.database import connect, create_guild_table class ImportGalacticBotView(ui.View): @@ -99,37 +100,37 @@ class ImportGalacticBotView(ui.View): if resolved_timestamp is None: resolved_timestamp = timestamp changes = [ - { + Change.from_dict({ "type": "ORIGINAL", "reason": case["reason"], "user_id": case["executor"], "timestamp": timestamp, - }, - { + }), + Change.from_dict({ "type": "RESOLVE", "reason": resolved_reason, "user_id": resolved_by, "timestamp": resolved_timestamp, - }, + }), ] else: - resolved = 0 + resolved = None resolved_by = None resolved_reason = None - changes = [] + changes = None if case["reason"] and case["reason"] != "N/A": reason = case["reason"] else: reason = None - await mysql_log( + Moderation.log( self.ctx.guild.id, case["executor"], case["type"], case["targetType"], case["target"], - 0, + None, duration, reason, timestamp=timestamp, diff --git a/aurora/models.py b/aurora/models.py index 8d42e21..0e8e751 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -2,7 +2,7 @@ import json import sqlite3 from datetime import datetime, timedelta from time import time -from typing import Any, Dict, List, Literal, Optional, Union +from typing import Any, Dict, Iterable, List, Literal, Optional, Union import discord from discord import Forbidden, HTTPException, InvalidData, NotFound @@ -10,7 +10,7 @@ from pydantic import BaseModel, ConfigDict from redbot.core.bot import Red from aurora.utilities.logger import logger -from aurora.utilities.utils import generate_dict, get_next_case_number +from aurora.utilities.utils import get_next_case_number class AuroraBaseModel(BaseModel): @@ -178,6 +178,41 @@ class Moderation(AuroraGuildModel): def from_dict(cls, bot: Red, data: dict) -> "Moderation": return cls(bot=bot, **data) + @classmethod + def from_result(bot: Red, result: Iterable, guild_id: int) -> dict: + if result[7] is not None: + hours, minutes, seconds = map(int, result[7].split(':')) + duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) + else: + duration = None + + if result[14] is not None: + changes = json.loads(result[14]) + change_obj_list = [] + for change in changes: + change_obj_list.append(Change.from_dict(bot=bot, data=change)) + + case = { + "moderation_id": int(result[0]), + "guild_id": int(guild_id), + "timestamp": datetime.fromtimestamp(result[1]), + "moderation_type": str(result[2]), + "target_type": str(result[3]), + "target_id": int(result[4]), + "moderator_id": int(result[5]), + "role_id": int(result[6]) if result[6] is not None else None, + "duration": duration, + "end_timestamp": datetime.fromtimestamp(result[8]) if result[8] is not None else None, + "reason": result[9], + "resolved": bool(result[10]), + "resolved_by": result[11], + "resolve_reason": result[12], + "expired": bool(result[13]), + "changes": change_obj_list if result[14] else [], + "metadata": json.loads(result[15].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) if result[15] else {}, + } + return case + @classmethod def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": from aurora.utilities.database import connect @@ -190,8 +225,7 @@ class Moderation(AuroraGuildModel): cursor.close() if result and not moderation_id == 0: - case = generate_dict(bot, result, guild_id) - return cls.from_dict(bot, case) + return cls.from_result(bot, result, guild_id) raise ValueError(f"Case {moderation_id} not found in moderation_{guild_id}!") diff --git a/aurora/utilities/database.py b/aurora/utilities/database.py index 88cec8b..588ad35 100644 --- a/aurora/utilities/database.py +++ b/aurora/utilities/database.py @@ -1,15 +1,11 @@ # pylint: disable=cyclic-import import json import sqlite3 -import time -from datetime import datetime, timedelta from discord import Guild from redbot.core import data_manager from .logger import logger -from .utils import (convert_timedelta_to_str, generate_dict, - get_next_case_number) def connect() -> sqlite3.Connection: @@ -79,13 +75,13 @@ async def create_guild_table(guild: Guild): "NULL", 0, 0, + None, + None, + None, + None, 0, - "NULL", - 0, - "NULL", - 0, - "NULL", - "NULL", + None, + None, 0, json.dumps([]), json.dumps({}), @@ -102,125 +98,3 @@ async def create_guild_table(guild: Guild): ) database.close() - - -def mysql_log( - guild_id: str, - author_id: str, - moderation_type: str, - target_type: str, - target_id: int, - role_id: int, - duration: timedelta = None, - reason: str = None, - database: sqlite3.Connection = None, - timestamp: int = None, - resolved: bool = False, - resolved_by: str = None, - resolved_reason: str = None, - expired: bool = None, - changes: list = None, - metadata: dict = None, -) -> int: - if not timestamp: - timestamp = int(time.time()) - - if duration != "NULL" and duration is not None: - end_timedelta = datetime.fromtimestamp(timestamp) + duration - end_timestamp = int(end_timedelta.timestamp()) - - duration = convert_timedelta_to_str(duration) - else: - duration = None - end_timestamp = None - - if not expired: - if int(time.time()) > end_timestamp: - expired = 1 - else: - expired = 0 - - if reason == "NULL": - reason = None - - if resolved_by == "NULL": - resolved_by = None - - if resolved_reason == "NULL": - resolved_reason = None - - if role_id == 0: - role_id = None - - if not database: - database = connect() - close_db = True - else: - close_db = False - cursor = database.cursor() - - moderation_id = get_next_case_number(guild_id=guild_id, cursor=cursor) - - sql = f"INSERT INTO `moderation_{guild_id}` (moderation_id, timestamp, moderation_type, target_type, target_id, moderator_id, role_id, duration, end_timestamp, reason, resolved, resolved_by, resolve_reason, expired, changes, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" - val = ( - moderation_id, - timestamp, - moderation_type, - target_type, - target_id, - author_id, - role_id, - duration, - end_timestamp, - reason, - int(resolved), - resolved_by, - resolved_reason, - expired, - json.dumps(changes if changes else []), - json.dumps(metadata if metadata else {}), - ) - cursor.execute(sql, val) - - cursor.close() - database.commit() - if close_db: - database.close() - - logger.debug( - "Row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", - guild_id, - moderation_id, - timestamp, - moderation_type, - target_type, - target_id, - author_id, - role_id, - duration, - end_timestamp, - reason, - int(resolved), - resolved_by, - resolved_reason, - expired, - changes, - metadata, - ) - - return moderation_id - - -async def fetch_case(moderation_id: int, guild_id: str) -> dict: - """This method fetches a case from the database and returns the case's dictionary.""" - database = connect() - cursor = database.cursor() - - query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;" - cursor.execute(query, (moderation_id,)) - result = cursor.fetchone() - - cursor.close() - database.close() - - return generate_dict(result) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index fcc8d61..bf3f23b 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -1,13 +1,11 @@ # pylint: disable=cyclic-import -import json from datetime import datetime, timedelta from typing import Optional, Union from dateutil.relativedelta import relativedelta as rd from discord import File, Guild, Interaction, Member, SelectOption, User -from discord.errors import Forbidden, NotFound +from discord.errors import Forbidden from redbot.core import commands, data_manager -from redbot.core.bot import Red from redbot.core.utils.chat_formatting import error from aurora.utilities.config import config @@ -125,123 +123,32 @@ def get_next_case_number(guild_id: str, cursor=None) -> int: return (result[0] + 1) if result else 1 -def generate_dict(bot: Red, result: dict, guild_id: int) -> dict: - from aurora.models import Change - if result[7] is not None: - hours, minutes, seconds = map(int, result[7].split(':')) - duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) - else: - duration = None - - if result[14] is not None: - changes = json.loads(result[14]) - change_obj_list = [] - for change in changes: - change_obj_list.append(Change.from_dict(bot=bot, data=change)) - - case = { - "moderation_id": int(result[0]), - "guild_id": int(guild_id), - "timestamp": datetime.fromtimestamp(result[1]), - "moderation_type": str(result[2]), - "target_type": str(result[3]), - "target_id": int(result[4]), - "moderator_id": int(result[5]), - "role_id": int(result[6]) if result[6] is not None else None, - "duration": duration, - "end_timestamp": datetime.fromtimestamp(result[8]) if result[8] is not None else None, - "reason": result[9], - "resolved": bool(result[10]), - "resolved_by": result[11], - "resolve_reason": result[12], - "expired": bool(result[13]), - "changes": change_obj_list if result[14] else [], - "metadata": json.loads(result[15].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) if result[15] else {}, - } - return case - - -async def fetch_user_dict(client: commands.Bot, user_id: str) -> dict: - """This function returns a dictionary containing either user information or a standard deleted user template.""" - if user_id == "?": - user_dict = {"id": "?", "name": "Unknown User", "discriminator": "0"} - - else: - try: - user = client.get_user(int(user_id)) - if user is None: - user = await client.fetch_user(int(user_id)) - - user_dict = { - "id": user.id, - "name": user.name, - "discriminator": user.discriminator, - } - - except NotFound: - user_dict = { - "id": user_id, - "name": "Deleted User", - "discriminator": "0", - } - - - return user_dict - - -async def fetch_channel_dict(guild: Guild, channel_id: int) -> dict: - """This function returns a dictionary containing either channel information or a standard deleted channel template.""" - try: - channel = guild.get_channel(int(channel_id)) - if not channel: - channel = await guild.fetch_channel(channel_id) - - channel_dict = { - "id": channel.id, - "name": channel.name, - "mention": channel.mention, - } - - except NotFound: - channel_dict = {"id": channel_id, "name": "Deleted Channel", "mention": None} - - return channel_dict - - -async def fetch_role_dict(guild: Guild, role_id: int) -> dict: - """This function returns a dictionary containing either role information or a standard deleted role template.""" - role = guild.get_role(int(role_id)) - if not role: - role_dict = {"id": role_id, "name": "Deleted Role"} - - role_dict = {"id": role.id, "name": role.name} - - return role_dict - - async def log(interaction: Interaction, moderation_id: int, resolved: bool = False) -> None: """This function sends a message to the guild's configured logging channel when an infraction takes place.""" - from .database import fetch_case - from .factory import log_factory + from aurora.models import Moderation + from aurora.utilities.factory import log_factory logging_channel_id = await config.guild(interaction.guild).log_channel() if logging_channel_id != " ": logging_channel = interaction.guild.get_channel(logging_channel_id) - case = await fetch_case(moderation_id, interaction.guild.id) - if case: + try: + moderation = Moderation.from_sql(interaction.client, moderation_id) embed = await log_factory( - interaction=interaction, case_dict=case, resolved=resolved + interaction=interaction, moderation=moderation, resolved=resolved ) try: await logging_channel.send(embed=embed) except Forbidden: return + except ValueError: + return -async def send_evidenceformat(interaction: Interaction, case_dict: dict) -> None: +async def send_evidenceformat(interaction: Interaction, moderation_id: int) -> None: """This function sends an ephemeral message to the moderator who took the moderation action, with a pre-made codeblock for use in the mod-evidence channel.""" - from .factory import evidenceformat_factory + from aurora.models import Moderation + from aurora.utilities.factory import evidenceformat_factory send_evidence_bool = ( await config.user(interaction.user).auto_evidenceformat() @@ -251,19 +158,11 @@ async def send_evidenceformat(interaction: Interaction, case_dict: dict) -> None if send_evidence_bool is False: return - content = await evidenceformat_factory(interaction=interaction, case_dict=case_dict) + moderation = Moderation.from_sql(interaction.client, moderation_id) + content = await evidenceformat_factory(interaction=interaction, moderation=moderation) await interaction.followup.send(content=content, ephemeral=True) -def convert_timedelta_to_str(td: timedelta) -> str: - """This function converts a timedelta object to a string.""" - total_seconds = int(td.total_seconds()) - hours = total_seconds // 3600 - minutes = (total_seconds % 3600) // 60 - seconds = total_seconds % 60 - return f"{hours}:{minutes}:{seconds}" - - def get_bool_emoji(value: Optional[bool]) -> str: """Returns a unicode emoji based on a boolean value.""" if value is True: -- 2.45.2 From c2e017339e37d7217a6e717a078cc80ff4e22a4f Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 16:35:06 -0400 Subject: [PATCH 096/146] fix(aurora): fixed a broken classmethod --- aurora/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models.py b/aurora/models.py index 0e8e751..e1821f0 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -179,7 +179,7 @@ class Moderation(AuroraGuildModel): return cls(bot=bot, **data) @classmethod - def from_result(bot: Red, result: Iterable, guild_id: int) -> dict: + def from_result(cls, bot: Red, result: Iterable, guild_id: int) -> dict: if result[7] is not None: hours, minutes, seconds = map(int, result[7].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) -- 2.45.2 From bb5ab8e61bd5e4ca4f1dcd273cf3731aeb11aab8 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 16:36:27 -0400 Subject: [PATCH 097/146] fix(aurora): make Moderration.from_result() return a Moderation object instead of a dictionary --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index e1821f0..f6ef0b9 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -179,7 +179,7 @@ class Moderation(AuroraGuildModel): return cls(bot=bot, **data) @classmethod - def from_result(cls, bot: Red, result: Iterable, guild_id: int) -> dict: + def from_result(cls, bot: Red, result: Iterable, guild_id: int) -> "Moderation": if result[7] is not None: hours, minutes, seconds = map(int, result[7].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) @@ -211,7 +211,7 @@ class Moderation(AuroraGuildModel): "changes": change_obj_list if result[14] else [], "metadata": json.loads(result[15].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) if result[15] else {}, } - return case + return cls.from_dict(bot=bot, data=case) @classmethod def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": -- 2.45.2 From 15ccd5530a461010543a02d6ff9e60aa94af4241 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 16:37:52 -0400 Subject: [PATCH 098/146] fix(aurora): fixed a few unboundlocalerrors --- aurora/aurora.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 695320b..cacee63 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -20,8 +20,7 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import (box, error, humanize_list, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -32,17 +31,10 @@ from aurora.menus.overrides import Overrides from aurora.models import Change, Moderation from aurora.utilities.config import config, register_config from aurora.utilities.database import connect, create_guild_table -from aurora.utilities.factory import (addrole_embed, case_factory, - changes_factory, evidenceformat_factory, - guild_embed, immune_embed, - message_factory, overrides_embed) - +from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import (check_moddable, check_permissions, - get_footer_image, log, send_evidenceformat, - timedelta_from_relativedelta) - +from aurora.utilities.utils import check_moddable, check_permissions, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta class Aurora(commands.Cog): """Aurora is a fully-featured moderation system. @@ -1145,10 +1137,10 @@ class Aurora(commands.Cog): }) moderator = memory_dict[str(mod.moderator_id)] - field_name = f"Case #{case['moderation_id']:,} ({str.title(case['moderation_type'])})" + field_name = f"Case #{mod.id:,} ({str.title(mod.type)})" field_value = f"**Target:** `{target.name}` ({target.id})\n**Moderator:** `{moderator.name}` ({moderator.id})" - if len(case["reason"]) > 125: + if len(mod.reason) > 125: field_value += f"\n**Reason:** `{str(mod.reason)[:125]}...`" else: field_value += f"\n**Reason:** `{str(mod.reason)}`" -- 2.45.2 From 09471a40272e60a9c18bfcad02bd7c52c1248ab8 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 16:39:13 -0400 Subject: [PATCH 099/146] misc(aurora): change the logging severity of Change.from_dict()'s logs to trace --- aurora/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models.py b/aurora/models.py index f6ef0b9..f5f758b 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -360,10 +360,10 @@ class Change(AuroraBaseModel): @classmethod def from_dict(cls, bot: Red, data: dict) -> "Change": - logger.debug("Creating Change from dict (%s): %s", type(data), data) + logger.trace("Creating Change from dict (%s): %s", type(data), data) if isinstance(data, str): data = json.loads(data) - logger.debug("Change data was a string, converted to dict: %s", data) + logger.trace("Change data was a string, converted to dict: %s", data) if "duration" in data and data["duration"] and not isinstance(data["duration"], timedelta): hours, minutes, seconds = map(int, data["duration"].split(':')) duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) -- 2.45.2 From ac8cefd779de5c109a1ddf85f174cbd849585f56 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 16:47:21 -0400 Subject: [PATCH 100/146] fix(aurora): pylint fixes --- aurora/aurora.py | 18 ++++++++++++------ aurora/importers/galacticbot.py | 4 ++-- aurora/models.py | 16 ++++++---------- aurora/utilities/factory.py | 11 ++++------- aurora/utilities/json.py | 1 - aurora/utilities/utils.py | 6 +++--- 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index cacee63..6e2e2fc 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -20,7 +20,8 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (box, error, humanize_list, + humanize_timedelta, warning) from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -31,10 +32,17 @@ from aurora.menus.overrides import Overrides from aurora.models import Change, Moderation from aurora.utilities.config import config, register_config from aurora.utilities.database import connect, create_guild_table -from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed +from aurora.utilities.factory import (addrole_embed, case_factory, + changes_factory, evidenceformat_factory, + guild_embed, immune_embed, + message_factory, overrides_embed) + from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import check_moddable, check_permissions, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta +from aurora.utilities.utils import (check_moddable, check_permissions, + get_footer_image, log, send_evidenceformat, + timedelta_from_relativedelta) + class Aurora(commands.Cog): """Aurora is a fully-featured moderation system. @@ -1327,9 +1335,7 @@ class Aurora(commands.Cog): embed=embed, ephemeral=ephemeral ) elif evidenceformat: - content = await evidenceformat_factory( - interaction=interaction, moderation=mod - ) + content = await evidenceformat_factory(moderation=mod) await interaction.response.send_message( content=content, ephemeral=ephemeral ) diff --git a/aurora/importers/galacticbot.py b/aurora/importers/galacticbot.py index 574d5ad..4cdc42e 100644 --- a/aurora/importers/galacticbot.py +++ b/aurora/importers/galacticbot.py @@ -100,13 +100,13 @@ class ImportGalacticBotView(ui.View): if resolved_timestamp is None: resolved_timestamp = timestamp changes = [ - Change.from_dict({ + Change.from_dict(interaction.client, { "type": "ORIGINAL", "reason": case["reason"], "user_id": case["executor"], "timestamp": timestamp, }), - Change.from_dict({ + Change.from_dict(interaction.client, { "type": "RESOLVE", "reason": resolved_reason, "user_id": resolved_by, diff --git a/aurora/models.py b/aurora/models.py index f5f758b..ab63be1 100644 --- a/aurora/models.py +++ b/aurora/models.py @@ -19,7 +19,7 @@ class AuroraBaseModel(BaseModel): bot: Red def to_json(self, indent: int = None, file: Any = None, **kwargs): - from aurora.utilities.json import dump, dumps + from aurora.utilities.json import dump, dumps # pylint: disable=cyclic-import return dump(self.model_dump(exclude={"bot"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) class AuroraGuildModel(AuroraBaseModel): @@ -27,7 +27,7 @@ class AuroraGuildModel(AuroraBaseModel): guild_id: int def to_json(self, indent: int = None, file: Any = None, **kwargs): - from aurora.utilities.json import dump, dumps + from aurora.utilities.json import dump, dumps # pylint: disable=cyclic-import return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) class Moderation(AuroraGuildModel): @@ -66,8 +66,7 @@ class Moderation(AuroraGuildModel): async def get_target(self) -> Union["PartialUser", "PartialChannel"]: if self.target_type == "USER": return await PartialUser.from_id(self.bot, self.target_id) - else: - return await PartialChannel.from_id(self.bot, self.target_id) + return await PartialChannel.from_id(self.bot, self.target_id) async def get_resolved_by(self) -> Optional["PartialUser"]: if self.resolved_by: @@ -264,10 +263,7 @@ class Moderation(AuroraGuildModel): end_timestamp = None if not expired: - if timestamp > end_timestamp: - expired = True - else: - expired = False + expired = bool(timestamp > end_timestamp) if reason == "NULL": reason = None @@ -422,7 +418,7 @@ class PartialChannel(AuroraGuildModel): @property def mention(self): - if self.name == "Deleted Channel" or self.name == "Forbidden Channel": + if self.name in ["Deleted Channel", "Forbidden Channel"]: return self.name return f"<#{self.id}>" @@ -448,7 +444,7 @@ class PartialRole(AuroraGuildModel): @property def mention(self): - if self.name == "Deleted Role" or self.name == "Forbidden Role": + if self.name in ["Deleted Role", "Forbidden Role"]: return self.name return f"<@&{self.id}>" diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 5977f26..11d00b9 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -2,16 +2,13 @@ from datetime import datetime, timedelta from typing import Union -from discord import (Color, Embed, Guild, Interaction, InteractionMessage, - Member, Role, User) +from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User from redbot.core import commands -from redbot.core.utils.chat_formatting import (bold, box, error, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning from aurora.models import Moderation, PartialUser from aurora.utilities.config import config -from aurora.utilities.utils import (get_bool_emoji, get_next_case_number, - get_pagesize_str) +from aurora.utilities.utils import get_bool_emoji, get_next_case_number, get_pagesize_str async def message_factory( @@ -261,7 +258,7 @@ async def changes_factory(interaction: Interaction, moderation: Moderation) -> E return embed -async def evidenceformat_factory(interaction: Interaction, moderation: Moderation) -> str: +async def evidenceformat_factory(moderation: Moderation) -> str: """This function creates a codeblock in evidence format from set parameters. Args: diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index cad264e..554679d 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -3,7 +3,6 @@ from datetime import datetime, timedelta from redbot.core.bot import Red - from aurora.models import AuroraBaseModel diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index bf3f23b..ca9cc85 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -133,7 +133,7 @@ async def log(interaction: Interaction, moderation_id: int, resolved: bool = Fal logging_channel = interaction.guild.get_channel(logging_channel_id) try: - moderation = Moderation.from_sql(interaction.client, moderation_id) + moderation = Moderation.from_sql(interaction.client, moderation_id, interaction.guild_id) embed = await log_factory( interaction=interaction, moderation=moderation, resolved=resolved ) @@ -158,8 +158,8 @@ async def send_evidenceformat(interaction: Interaction, moderation_id: int) -> N if send_evidence_bool is False: return - moderation = Moderation.from_sql(interaction.client, moderation_id) - content = await evidenceformat_factory(interaction=interaction, moderation=moderation) + moderation = Moderation.from_sql(interaction.client, moderation_id, interaction.guild.id) + content = await evidenceformat_factory(moderation=moderation) await interaction.followup.send(content=content, ephemeral=True) -- 2.45.2 From 7dfe94869c299603e761486552b38158d3fb1df1 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 17:23:59 -0400 Subject: [PATCH 101/146] misc(aurora): codebase cleanup --- aurora/aurora.py | 17 +-- aurora/importers/aurora.py | 2 +- aurora/importers/galacticbot.py | 2 +- aurora/models/base.py | 24 ++++ aurora/models/change.py | 62 +++++++++ aurora/{models.py => models/moderation.py} | 151 +-------------------- aurora/models/partials.py | 79 +++++++++++ aurora/utilities/database.py | 2 +- aurora/utilities/factory.py | 3 +- aurora/utilities/json.py | 2 +- aurora/utilities/utils.py | 4 +- 11 files changed, 183 insertions(+), 165 deletions(-) create mode 100644 aurora/models/base.py create mode 100644 aurora/models/change.py rename aurora/{models.py => models/moderation.py} (65%) create mode 100644 aurora/models/partials.py diff --git a/aurora/aurora.py b/aurora/aurora.py index 6e2e2fc..309bbb7 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -20,8 +20,7 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import (box, error, humanize_list, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -29,20 +28,14 @@ from aurora.menus.addrole import Addrole from aurora.menus.guild import Guild from aurora.menus.immune import Immune from aurora.menus.overrides import Overrides -from aurora.models import Change, Moderation +from aurora.models.change import Change +from aurora.models.moderation import Moderation from aurora.utilities.config import config, register_config from aurora.utilities.database import connect, create_guild_table -from aurora.utilities.factory import (addrole_embed, case_factory, - changes_factory, evidenceformat_factory, - guild_embed, immune_embed, - message_factory, overrides_embed) - +from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import (check_moddable, check_permissions, - get_footer_image, log, send_evidenceformat, - timedelta_from_relativedelta) - +from aurora.utilities.utils import check_moddable, check_permissions, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta class Aurora(commands.Cog): """Aurora is a fully-featured moderation system. diff --git a/aurora/importers/aurora.py b/aurora/importers/aurora.py index 0885ee9..7d1b273 100644 --- a/aurora/importers/aurora.py +++ b/aurora/importers/aurora.py @@ -8,7 +8,7 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands from redbot.core.utils.chat_formatting import box, warning -from aurora.models import Moderation +from aurora.models.moderation import Moderation from aurora.utilities.database import connect, create_guild_table diff --git a/aurora/importers/galacticbot.py b/aurora/importers/galacticbot.py index 4cdc42e..50d51b9 100644 --- a/aurora/importers/galacticbot.py +++ b/aurora/importers/galacticbot.py @@ -7,7 +7,7 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands from redbot.core.utils.chat_formatting import box, warning -from aurora.models import Change, Moderation +from aurora.models.moderation import Change, Moderation from aurora.utilities.database import connect, create_guild_table diff --git a/aurora/models/base.py b/aurora/models/base.py new file mode 100644 index 0000000..31d01ac --- /dev/null +++ b/aurora/models/base.py @@ -0,0 +1,24 @@ +from typing import Any + +from pydantic import BaseModel, ConfigDict +from redbot.core.bot import Red + + +class AuroraBaseModel(BaseModel): + """Base class for all models in Aurora.""" + model_config = ConfigDict(ignored_types=(Red,), arbitrary_types_allowed=True) + bot: Red + + def to_json(self, indent: int = None, file: Any = None, **kwargs): + from aurora.utilities.json import ( # pylint: disable=cyclic-import + dump, dumps) + return dump(self.model_dump(exclude={"bot"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) + +class AuroraGuildModel(AuroraBaseModel): + """Subclass of AuroraBaseModel that includes a guild_id attribute, and a modified to_json() method to match.""" + guild_id: int + + def to_json(self, indent: int = None, file: Any = None, **kwargs): + from aurora.utilities.json import ( # pylint: disable=cyclic-import + dump, dumps) + return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) diff --git a/aurora/models/change.py b/aurora/models/change.py new file mode 100644 index 0000000..ceb4641 --- /dev/null +++ b/aurora/models/change.py @@ -0,0 +1,62 @@ +import json +from datetime import datetime, timedelta +from typing import Literal, Optional + +from redbot.core.bot import Red + +from aurora.models.base import AuroraBaseModel +from aurora.models.partials import PartialUser +from aurora.utilities.logger import logger + + +class Change(AuroraBaseModel): + type: Literal["ORIGINAL", "RESOLVE", "EDIT"] + timestamp: datetime + reason: str + user_id: int + duration: Optional[timedelta] = None + end_timestamp: Optional[datetime] = None + + @property + def unix_timestamp(self) -> int: + return int(self.timestamp.timestamp()) + + def __str__(self): + return f"{self.type} {self.user_id} {self.reason}" + + async def get_user(self) -> "PartialUser": + return await PartialUser.from_id(self.bot, self.user_id) + + @classmethod + def from_dict(cls, bot: Red, data: dict) -> "Change": + logger.trace("Creating Change from dict (%s): %s", type(data), data) + if isinstance(data, str): + data = json.loads(data) + logger.trace("Change data was a string, converted to dict: %s", data) + if "duration" in data and data["duration"] and not isinstance(data["duration"], timedelta): + hours, minutes, seconds = map(int, data["duration"].split(':')) + duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) + elif "duration" in data and isinstance(data["duration"], timedelta): + duration = data["duration"] + else: + duration = None + + if "end_timestamp" in data and data["end_timestamp"] and not isinstance(data["end_timestamp"], datetime): + end_timestamp = datetime.fromtimestamp(data["end_timestamp"]) + elif "end_timestamp" in data and isinstance(data["end_timestamp"], datetime): + end_timestamp = data["end_timestamp"] + else: + end_timestamp = None + + if not isinstance(data["timestamp"], datetime): + timestamp = datetime.fromtimestamp(data["timestamp"]) + else: + timestamp = data["timestamp"] + + data.update({ + "timestamp": timestamp, + "end_timestamp": end_timestamp, + "duration": duration, + "user_id": int(data["user_id"]) + }) + return cls(bot=bot, **data) diff --git a/aurora/models.py b/aurora/models/moderation.py similarity index 65% rename from aurora/models.py rename to aurora/models/moderation.py index ab63be1..7bea736 100644 --- a/aurora/models.py +++ b/aurora/models/moderation.py @@ -2,34 +2,19 @@ import json import sqlite3 from datetime import datetime, timedelta from time import time -from typing import Any, Dict, Iterable, List, Literal, Optional, Union +from typing import Dict, Iterable, List, Optional, Union import discord -from discord import Forbidden, HTTPException, InvalidData, NotFound -from pydantic import BaseModel, ConfigDict +from discord import NotFound from redbot.core.bot import Red +from aurora.models.base import AuroraGuildModel +from aurora.models.change import Change +from aurora.models.partials import PartialChannel, PartialRole, PartialUser from aurora.utilities.logger import logger from aurora.utilities.utils import get_next_case_number -class AuroraBaseModel(BaseModel): - """Base class for all models in Aurora.""" - model_config = ConfigDict(ignored_types=(Red,), arbitrary_types_allowed=True) - bot: Red - - def to_json(self, indent: int = None, file: Any = None, **kwargs): - from aurora.utilities.json import dump, dumps # pylint: disable=cyclic-import - return dump(self.model_dump(exclude={"bot"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) - -class AuroraGuildModel(AuroraBaseModel): - """Subclass of AuroraBaseModel that includes a guild_id attribute, and a modified to_json() method to match.""" - guild_id: int - - def to_json(self, indent: int = None, file: Any = None, **kwargs): - from aurora.utilities.json import dump, dumps # pylint: disable=cyclic-import - return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) - class Moderation(AuroraGuildModel): moderation_id: int timestamp: datetime @@ -335,129 +320,3 @@ class Moderation(AuroraGuildModel): ) return cls.from_sql(bot=bot, moderation_id=moderation_id, guild_id=guild_id) - -class Change(AuroraBaseModel): - type: Literal["ORIGINAL", "RESOLVE", "EDIT"] - timestamp: datetime - reason: str - user_id: int - duration: Optional[timedelta] = None - end_timestamp: Optional[datetime] = None - - @property - def unix_timestamp(self) -> int: - return int(self.timestamp.timestamp()) - - def __str__(self): - return f"{self.type} {self.user_id} {self.reason}" - - async def get_user(self) -> "PartialUser": - return await PartialUser.from_id(self.bot, self.user_id) - - @classmethod - def from_dict(cls, bot: Red, data: dict) -> "Change": - logger.trace("Creating Change from dict (%s): %s", type(data), data) - if isinstance(data, str): - data = json.loads(data) - logger.trace("Change data was a string, converted to dict: %s", data) - if "duration" in data and data["duration"] and not isinstance(data["duration"], timedelta): - hours, minutes, seconds = map(int, data["duration"].split(':')) - duration = timedelta(hours=hours, minutes=minutes, seconds=seconds) - elif "duration" in data and isinstance(data["duration"], timedelta): - duration = data["duration"] - else: - duration = None - - if "end_timestamp" in data and data["end_timestamp"] and not isinstance(data["end_timestamp"], datetime): - end_timestamp = datetime.fromtimestamp(data["end_timestamp"]) - elif "end_timestamp" in data and isinstance(data["end_timestamp"], datetime): - end_timestamp = data["end_timestamp"] - else: - end_timestamp = None - - if not isinstance(data["timestamp"], datetime): - timestamp = datetime.fromtimestamp(data["timestamp"]) - else: - timestamp = data["timestamp"] - - data.update({ - "timestamp": timestamp, - "end_timestamp": end_timestamp, - "duration": duration, - "user_id": int(data["user_id"]) - }) - return cls(bot=bot, **data) - -class PartialUser(AuroraBaseModel): - id: int - username: str - discriminator: int - - @property - def name(self): - return f"{self.username}#{self.discriminator}" if self.discriminator != 0 else self.username - - def __str__(self): - return self.name - - @classmethod - async def from_id(cls, bot: Red, user_id: int) -> "PartialUser": - user = bot.get_user(user_id) - if not user: - try: - user = await bot.fetch_user(user_id) - return cls(bot=bot, id=user.id, username=user.name, discriminator=user.discriminator) - except NotFound: - return cls(bot=bot, id=user_id, username="Deleted User", discriminator=0) - return cls(bot=bot, id=user.id, username=user.name, discriminator=user.discriminator) - - -class PartialChannel(AuroraGuildModel): - id: int - name: str - - @property - def mention(self): - if self.name in ["Deleted Channel", "Forbidden Channel"]: - return self.name - return f"<#{self.id}>" - - def __str__(self): - return self.mention - - @classmethod - async def from_id(cls, bot: Red, channel_id: int) -> "PartialChannel": - channel = bot.get_channel(channel_id) - if not channel: - try: - channel = await bot.fetch_channel(channel_id) - return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) - except (NotFound, InvalidData, HTTPException, Forbidden) as e: - if e == Forbidden: - return cls(bot=bot, guild_id=0, id=channel_id, name="Forbidden Channel") - return cls(bot=bot, guild_id=0, id=channel_id, name="Deleted Channel") - return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) - -class PartialRole(AuroraGuildModel): - id: int - name: str - - @property - def mention(self): - if self.name in ["Deleted Role", "Forbidden Role"]: - return self.name - return f"<@&{self.id}>" - - def __str__(self): - return self.mention - - @classmethod - async def from_id(cls, bot: Red, guild_id: int, role_id: int) -> "PartialRole": - try: - guild = await bot.fetch_guild(guild_id, with_counts=False) - except (Forbidden, HTTPException): - return cls(bot=bot, guild_id=guild_id, id=role_id, name="Forbidden Role") - role = guild.get_role(role_id) - if not role: - return cls(bot=bot, guild_id=guild_id, id=role_id, name="Deleted Role") - return cls(bot=bot, guild_id=guild_id, id=role.id, name=role.name) diff --git a/aurora/models/partials.py b/aurora/models/partials.py new file mode 100644 index 0000000..48ff2a0 --- /dev/null +++ b/aurora/models/partials.py @@ -0,0 +1,79 @@ +from discord import Forbidden, HTTPException, InvalidData, NotFound +from redbot.core.bot import Red + +from aurora.models.base import AuroraBaseModel, AuroraGuildModel + + +class PartialUser(AuroraBaseModel): + id: int + username: str + discriminator: int + + @property + def name(self): + return f"{self.username}#{self.discriminator}" if self.discriminator != 0 else self.username + + def __str__(self): + return self.name + + @classmethod + async def from_id(cls, bot: Red, user_id: int) -> "PartialUser": + user = bot.get_user(user_id) + if not user: + try: + user = await bot.fetch_user(user_id) + return cls(bot=bot, id=user.id, username=user.name, discriminator=user.discriminator) + except NotFound: + return cls(bot=bot, id=user_id, username="Deleted User", discriminator=0) + return cls(bot=bot, id=user.id, username=user.name, discriminator=user.discriminator) + + +class PartialChannel(AuroraGuildModel): + id: int + name: str + + @property + def mention(self): + if self.name in ["Deleted Channel", "Forbidden Channel"]: + return self.name + return f"<#{self.id}>" + + def __str__(self): + return self.mention + + @classmethod + async def from_id(cls, bot: Red, channel_id: int) -> "PartialChannel": + channel = bot.get_channel(channel_id) + if not channel: + try: + channel = await bot.fetch_channel(channel_id) + return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) + except (NotFound, InvalidData, HTTPException, Forbidden) as e: + if e == Forbidden: + return cls(bot=bot, guild_id=0, id=channel_id, name="Forbidden Channel") + return cls(bot=bot, guild_id=0, id=channel_id, name="Deleted Channel") + return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) + +class PartialRole(AuroraGuildModel): + id: int + name: str + + @property + def mention(self): + if self.name in ["Deleted Role", "Forbidden Role"]: + return self.name + return f"<@&{self.id}>" + + def __str__(self): + return self.mention + + @classmethod + async def from_id(cls, bot: Red, guild_id: int, role_id: int) -> "PartialRole": + try: + guild = await bot.fetch_guild(guild_id, with_counts=False) + except (Forbidden, HTTPException): + return cls(bot=bot, guild_id=guild_id, id=role_id, name="Forbidden Role") + role = guild.get_role(role_id) + if not role: + return cls(bot=bot, guild_id=guild_id, id=role_id, name="Deleted Role") + return cls(bot=bot, guild_id=guild_id, id=role.id, name=role.name) diff --git a/aurora/utilities/database.py b/aurora/utilities/database.py index 588ad35..16f8a7e 100644 --- a/aurora/utilities/database.py +++ b/aurora/utilities/database.py @@ -5,7 +5,7 @@ import sqlite3 from discord import Guild from redbot.core import data_manager -from .logger import logger +from aurora.utilities.logger import logger def connect() -> sqlite3.Connection: diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 11d00b9..1b508bc 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -6,7 +6,8 @@ from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member from redbot.core import commands from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning -from aurora.models import Moderation, PartialUser +from aurora.models.moderation import Moderation +from aurora.models.partials import PartialUser from aurora.utilities.config import config from aurora.utilities.utils import get_bool_emoji, get_next_case_number, get_pagesize_str diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index 554679d..73595ee 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from redbot.core.bot import Red -from aurora.models import AuroraBaseModel +from aurora.models.base import AuroraBaseModel class JSONEncoder(json.JSONEncoder): diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index ca9cc85..e85fd58 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -125,7 +125,7 @@ def get_next_case_number(guild_id: str, cursor=None) -> int: async def log(interaction: Interaction, moderation_id: int, resolved: bool = False) -> None: """This function sends a message to the guild's configured logging channel when an infraction takes place.""" - from aurora.models import Moderation + from aurora.models.moderation import Moderation from aurora.utilities.factory import log_factory logging_channel_id = await config.guild(interaction.guild).log_channel() @@ -147,7 +147,7 @@ async def log(interaction: Interaction, moderation_id: int, resolved: bool = Fal async def send_evidenceformat(interaction: Interaction, moderation_id: int) -> None: """This function sends an ephemeral message to the moderator who took the moderation action, with a pre-made codeblock for use in the mod-evidence channel.""" - from aurora.models import Moderation + from aurora.models.moderation import Moderation from aurora.utilities.factory import evidenceformat_factory send_evidence_bool = ( -- 2.45.2 From 2b79e3b6a8baf83134da9bdcba1c2c95a36d48d5 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 17:29:57 -0400 Subject: [PATCH 102/146] fix(aurora): added an __init__.py to a directory that was missing one --- aurora/models/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 aurora/models/__init__.py diff --git a/aurora/models/__init__.py b/aurora/models/__init__.py new file mode 100644 index 0000000..e69de29 -- 2.45.2 From c69b3cd032a5a192b54afdacab9a05f26750e7de Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 17:33:28 -0400 Subject: [PATCH 103/146] misc(aurora): import change --- aurora/aurora.py | 14 +++++++++++--- aurora/utilities/factory.py | 9 ++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 309bbb7..3eb5a2e 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -20,7 +20,8 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (box, error, humanize_list, + humanize_timedelta, warning) from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -32,10 +33,17 @@ from aurora.models.change import Change from aurora.models.moderation import Moderation from aurora.utilities.config import config, register_config from aurora.utilities.database import connect, create_guild_table -from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed +from aurora.utilities.factory import (addrole_embed, case_factory, + changes_factory, evidenceformat_factory, + guild_embed, immune_embed, + message_factory, overrides_embed) + from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import check_moddable, check_permissions, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta +from aurora.utilities.utils import (check_moddable, check_permissions, + get_footer_image, log, send_evidenceformat, + timedelta_from_relativedelta) + class Aurora(commands.Cog): """Aurora is a fully-featured moderation system. diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 1b508bc..d5978c7 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -2,14 +2,17 @@ from datetime import datetime, timedelta from typing import Union -from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User +from discord import (Color, Embed, Guild, Interaction, InteractionMessage, + Member, Role, User) from redbot.core import commands -from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (bold, box, error, + humanize_timedelta, warning) from aurora.models.moderation import Moderation from aurora.models.partials import PartialUser from aurora.utilities.config import config -from aurora.utilities.utils import get_bool_emoji, get_next_case_number, get_pagesize_str +from aurora.utilities.utils import (get_bool_emoji, get_next_case_number, + get_pagesize_str) async def message_factory( -- 2.45.2 From 260dd3ef4c737ccce840986969fc7ae60b770a10 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 17:35:43 -0400 Subject: [PATCH 104/146] fix(aurora): fixing a ModuleNotFound error --- aurora/__init__.py | 2 +- aurora/aurora.py | 18 +++++------------- aurora/importers/{aurora.py => auroraview.py} | 0 .../{galacticbot.py => galacticbotview.py} | 0 4 files changed, 6 insertions(+), 14 deletions(-) rename aurora/importers/{aurora.py => auroraview.py} (100%) rename aurora/importers/{galacticbot.py => galacticbotview.py} (100%) diff --git a/aurora/__init__.py b/aurora/__init__.py index 49ef5ec..a66006c 100644 --- a/aurora/__init__.py +++ b/aurora/__init__.py @@ -1,4 +1,4 @@ -from .aurora import Aurora +from aurora.aurora import Aurora async def setup(bot): diff --git a/aurora/aurora.py b/aurora/aurora.py index 3eb5a2e..4d7780f 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -20,11 +20,10 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import (box, error, humanize_list, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning -from aurora.importers.aurora import ImportAuroraView -from aurora.importers.galacticbot import ImportGalacticBotView +from aurora.importers.auroraview import ImportAuroraView +from aurora.importers.galacticbotview import ImportGalacticBotView from aurora.menus.addrole import Addrole from aurora.menus.guild import Guild from aurora.menus.immune import Immune @@ -33,17 +32,10 @@ from aurora.models.change import Change from aurora.models.moderation import Moderation from aurora.utilities.config import config, register_config from aurora.utilities.database import connect, create_guild_table -from aurora.utilities.factory import (addrole_embed, case_factory, - changes_factory, evidenceformat_factory, - guild_embed, immune_embed, - message_factory, overrides_embed) - +from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import (check_moddable, check_permissions, - get_footer_image, log, send_evidenceformat, - timedelta_from_relativedelta) - +from aurora.utilities.utils import check_moddable, check_permissions, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta class Aurora(commands.Cog): """Aurora is a fully-featured moderation system. diff --git a/aurora/importers/aurora.py b/aurora/importers/auroraview.py similarity index 100% rename from aurora/importers/aurora.py rename to aurora/importers/auroraview.py diff --git a/aurora/importers/galacticbot.py b/aurora/importers/galacticbotview.py similarity index 100% rename from aurora/importers/galacticbot.py rename to aurora/importers/galacticbotview.py -- 2.45.2 From bcc4aa384f7f4d92f89c6b4f75588397a29f90ce Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 17:41:14 -0400 Subject: [PATCH 105/146] fix(aurora): hopefully fixed the module not found issue? --- aurora/__init__.py | 2 +- aurora/{aurora.py => main.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename aurora/{aurora.py => main.py} (100%) diff --git a/aurora/__init__.py b/aurora/__init__.py index a66006c..c9a519d 100644 --- a/aurora/__init__.py +++ b/aurora/__init__.py @@ -1,4 +1,4 @@ -from aurora.aurora import Aurora +from aurora.main import Aurora async def setup(bot): diff --git a/aurora/aurora.py b/aurora/main.py similarity index 100% rename from aurora/aurora.py rename to aurora/main.py -- 2.45.2 From 946e14ee3c9d80a234f74a419a603f51440ec5eb Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 20:45:22 -0400 Subject: [PATCH 106/146] fix(aurora): something idk --- aurora/__init__.py | 2 +- aurora/{main.py => aurora.py} | 18 +++++-- aurora/importers/{auroraview.py => aurora.py} | 0 .../{galacticbotview.py => galacticbot.py} | 0 poetry.lock | 48 ++++++++++++++++++- pyproject.toml | 1 + 6 files changed, 62 insertions(+), 7 deletions(-) rename aurora/{main.py => aurora.py} (98%) rename aurora/importers/{auroraview.py => aurora.py} (100%) rename aurora/importers/{galacticbotview.py => galacticbot.py} (100%) diff --git a/aurora/__init__.py b/aurora/__init__.py index c9a519d..a66006c 100644 --- a/aurora/__init__.py +++ b/aurora/__init__.py @@ -1,4 +1,4 @@ -from aurora.main import Aurora +from aurora.aurora import Aurora async def setup(bot): diff --git a/aurora/main.py b/aurora/aurora.py similarity index 98% rename from aurora/main.py rename to aurora/aurora.py index 4d7780f..3eb5a2e 100644 --- a/aurora/main.py +++ b/aurora/aurora.py @@ -20,10 +20,11 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning +from redbot.core.utils.chat_formatting import (box, error, humanize_list, + humanize_timedelta, warning) -from aurora.importers.auroraview import ImportAuroraView -from aurora.importers.galacticbotview import ImportGalacticBotView +from aurora.importers.aurora import ImportAuroraView +from aurora.importers.galacticbot import ImportGalacticBotView from aurora.menus.addrole import Addrole from aurora.menus.guild import Guild from aurora.menus.immune import Immune @@ -32,10 +33,17 @@ from aurora.models.change import Change from aurora.models.moderation import Moderation from aurora.utilities.config import config, register_config from aurora.utilities.database import connect, create_guild_table -from aurora.utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed +from aurora.utilities.factory import (addrole_embed, case_factory, + changes_factory, evidenceformat_factory, + guild_embed, immune_embed, + message_factory, overrides_embed) + from aurora.utilities.json import dump from aurora.utilities.logger import logger -from aurora.utilities.utils import check_moddable, check_permissions, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta +from aurora.utilities.utils import (check_moddable, check_permissions, + get_footer_image, log, send_evidenceformat, + timedelta_from_relativedelta) + class Aurora(commands.Cog): """Aurora is a fully-featured moderation system. diff --git a/aurora/importers/auroraview.py b/aurora/importers/aurora.py similarity index 100% rename from aurora/importers/auroraview.py rename to aurora/importers/aurora.py diff --git a/aurora/importers/galacticbotview.py b/aurora/importers/galacticbot.py similarity index 100% rename from aurora/importers/galacticbotview.py rename to aurora/importers/galacticbot.py diff --git a/poetry.lock b/poetry.lock index 0a5918b..6f6d265 100644 --- a/poetry.lock +++ b/poetry.lock @@ -195,6 +195,20 @@ files = [ {file = "apsw-3.45.2.0.tar.gz", hash = "sha256:4fe81f5e390969d08d3048f357a68b347316b8f09455ff4657d94c56acfa255c"}, ] +[[package]] +name = "argcomplete" +version = "3.3.0" +description = "Bash tab completion for argparse" +optional = false +python-versions = ">=3.8" +files = [ + {file = "argcomplete-3.3.0-py3-none-any.whl", hash = "sha256:c168c3723482c031df3c207d4ba8fa702717ccb9fc0bfe4117166c1f537b4a54"}, + {file = "argcomplete-3.3.0.tar.gz", hash = "sha256:fd03ff4a5b9e6580569d34b273f741e85cd9e072f3feeeee3eba4891c70eda62"}, +] + +[package.extras] +test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] + [[package]] name = "astroid" version = "3.1.0" @@ -1491,6 +1505,24 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa typing = ["typing-extensions"] xmp = ["defusedxml"] +[[package]] +name = "pipx" +version = "1.5.0" +description = "Install and Run Python Applications in Isolated Environments" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pipx-1.5.0-py3-none-any.whl", hash = "sha256:801a55a9d58004bb18a464f668508e79fbffc22deb6f07982832d3ce3ff3756d"}, + {file = "pipx-1.5.0.tar.gz", hash = "sha256:2371af2b772954cdb5c1dbfa0170219e3d2c09d9ff9b18e975f65562eeb7ab0a"}, +] + +[package.dependencies] +argcomplete = ">=1.9.4" +colorama = {version = ">=0.4.4", markers = "sys_platform == \"win32\""} +packaging = ">=20" +platformdirs = ">=2.1" +userpath = ">=1.6,<1.9.0 || >1.9.0" + [[package]] name = "platformdirs" version = "4.2.0" @@ -2262,6 +2294,20 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "userpath" +version = "1.9.2" +description = "Cross-platform tool for adding locations to the user PATH" +optional = false +python-versions = ">=3.7" +files = [ + {file = "userpath-1.9.2-py3-none-any.whl", hash = "sha256:2cbf01a23d655a1ff8fc166dfb78da1b641d1ceabf0fe5f970767d380b14e89d"}, + {file = "userpath-1.9.2.tar.gz", hash = "sha256:6c52288dab069257cc831846d15d48133522455d4677ee69a9781f11dbefd815"}, +] + +[package.dependencies] +click = "*" + [[package]] name = "uvloop" version = "0.19.0" @@ -2545,4 +2591,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "67eb5e616951979332b6f32bcb39d85171cbf8377f566ea1862c51b5068b52f3" +content-hash = "25dce93989e94b49932b4f5bb8489eb37b2e9aa5860679f7150e2beca8b0471c" diff --git a/pyproject.toml b/pyproject.toml index 91c350e..507b821 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ optional = true [tool.poetry.group.dev.dependencies] ruff = "^0.3.1" pylint = "^3.1.0" +pipx = "^1.5.0" [tool.poetry.group.docs] optional = true -- 2.45.2 From ab878739c4eecdfe92077a47b438270d9ab02adc Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 21:04:08 -0400 Subject: [PATCH 107/146] misc(aurora): pep 604 compliance --- aurora/aurora.py | 48 ++++++++++++++++++------------------- aurora/menus/addrole.py | 2 +- aurora/menus/guild.py | 2 +- aurora/menus/immune.py | 2 +- aurora/menus/overrides.py | 2 +- aurora/models/base.py | 4 ++-- aurora/models/moderation.py | 18 +++++++------- aurora/utilities/factory.py | 8 +++---- aurora/utilities/utils.py | 4 ++-- 9 files changed, 45 insertions(+), 45 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 3eb5a2e..fe9abcb 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -205,7 +205,7 @@ class Aurora(commands.Cog): interaction: discord.Interaction, target: discord.User, reason: str, - silent: bool = None, + silent: bool | None = None, ): """Add a note to a user. @@ -263,7 +263,7 @@ class Aurora(commands.Cog): interaction: discord.Interaction, target: discord.Member, reason: str, - silent: bool = None, + silent: bool | None = None, ): """Warn a user. @@ -322,8 +322,8 @@ class Aurora(commands.Cog): target: discord.Member, role: discord.Role, reason: str, - duration: str = None, - silent: bool = None, + duration: str | None = None, + silent: bool | None = None, ): """Add a role to a user. @@ -427,8 +427,8 @@ class Aurora(commands.Cog): target: discord.Member, role: discord.Role, reason: str, - duration: str = None, - silent: bool = None, + duration: str | None = None, + silent: bool | None = None, ): """Remove a role from a user. @@ -532,7 +532,7 @@ class Aurora(commands.Cog): target: discord.Member, duration: str, reason: str, - silent: bool = None, + silent: bool | None = None, ): """Mute a user. @@ -617,8 +617,8 @@ class Aurora(commands.Cog): self, interaction: discord.Interaction, target: discord.Member, - reason: str = None, - silent: bool = None, + reason: str | None = None, + silent: bool | None = None, ): """Unmute a user. @@ -692,7 +692,7 @@ class Aurora(commands.Cog): interaction: discord.Interaction, target: discord.Member, reason: str, - silent: bool = None, + silent: bool | None = None, ): """Kick a user. @@ -762,9 +762,9 @@ class Aurora(commands.Cog): interaction: discord.Interaction, target: discord.User, reason: str, - duration: str = None, - delete_messages: Choice[int] = None, - silent: bool = None, + duration: str | None = None, + delete_messages: Choice[int] | None = None, + silent: bool | None = None, ): """Ban a user. @@ -901,8 +901,8 @@ class Aurora(commands.Cog): self, interaction: discord.Interaction, target: discord.User, - reason: str = None, - silent: bool = None, + reason: str | None = None, + silent: bool | None = None, ): """Unban a user. @@ -976,12 +976,12 @@ class Aurora(commands.Cog): async def history( self, interaction: discord.Interaction, - target: discord.User = None, - moderator: discord.User = None, - pagesize: app_commands.Range[int, 1, 20] = None, + target: discord.User | None = None, + moderator: discord.User | None = None, + pagesize: app_commands.Range[int, 1, 20] | None = None, page: int = 1, - ephemeral: bool = None, - inline: bool = None, + ephemeral: bool | None = None, + inline: bool | None = None, export: bool = False, ): """List previous infractions. @@ -1179,7 +1179,7 @@ class Aurora(commands.Cog): @app_commands.command(name="resolve") async def resolve( - self, interaction: discord.Interaction, case: int, reason: str = None + self, interaction: discord.Interaction, case: int, reason: str | None = None ): """Resolve a specific case. @@ -1251,10 +1251,10 @@ class Aurora(commands.Cog): self, interaction: discord.Interaction, case: int, - ephemeral: bool = None, + ephemeral: bool | None = None, evidenceformat: bool = False, changes: bool = False, - export: Choice[str] = None, + export: Choice[str] | None = None, ): """Check the details of a specific case. @@ -1355,7 +1355,7 @@ class Aurora(commands.Cog): interaction: discord.Interaction, case: int, reason: str, - duration: str = None, + duration: str | None = None, ): """Edit the reason of a specific case. diff --git a/aurora/menus/addrole.py b/aurora/menus/addrole.py index 11f6b32..9f4d1ff 100644 --- a/aurora/menus/addrole.py +++ b/aurora/menus/addrole.py @@ -7,7 +7,7 @@ from aurora.utilities.factory import addrole_embed class Addrole(ui.View): - def __init__(self, ctx: commands.Context, message: Message, timeout: int = None): + def __init__(self, ctx: commands.Context, message: Message, timeout: int | None = None): super().__init__() self.ctx = ctx self.message = message diff --git a/aurora/menus/guild.py b/aurora/menus/guild.py index f99e552..ece8131 100644 --- a/aurora/menus/guild.py +++ b/aurora/menus/guild.py @@ -7,7 +7,7 @@ from aurora.utilities.utils import create_pagesize_options class Guild(ui.View): - def __init__(self, ctx: commands.Context, message: Message, timeout: int = None): + def __init__(self, ctx: commands.Context, message: Message, timeout: int | None = None): super().__init__() self.ctx = ctx self.message = message diff --git a/aurora/menus/immune.py b/aurora/menus/immune.py index 2a5c007..7d85d12 100644 --- a/aurora/menus/immune.py +++ b/aurora/menus/immune.py @@ -7,7 +7,7 @@ from aurora.utilities.factory import immune_embed class Immune(ui.View): - def __init__(self, ctx: commands.Context, message: Message, timeout: int = None): + def __init__(self, ctx: commands.Context, message: Message, timeout: int | None = None): super().__init__() self.ctx = ctx self.message = message diff --git a/aurora/menus/overrides.py b/aurora/menus/overrides.py index c5d0e68..beefaef 100644 --- a/aurora/menus/overrides.py +++ b/aurora/menus/overrides.py @@ -7,7 +7,7 @@ from aurora.utilities.utils import create_pagesize_options class Overrides(ui.View): - def __init__(self, ctx: commands.Context, message: Message, timeout: int = None): + def __init__(self, ctx: commands.Context, message: Message, timeout: int | None = None): super().__init__() self.ctx = ctx self.message = message diff --git a/aurora/models/base.py b/aurora/models/base.py index 31d01ac..45c8a60 100644 --- a/aurora/models/base.py +++ b/aurora/models/base.py @@ -9,7 +9,7 @@ class AuroraBaseModel(BaseModel): model_config = ConfigDict(ignored_types=(Red,), arbitrary_types_allowed=True) bot: Red - def to_json(self, indent: int = None, file: Any = None, **kwargs): + def to_json(self, indent: int | None = None, file: Any | None = None, **kwargs): from aurora.utilities.json import ( # pylint: disable=cyclic-import dump, dumps) return dump(self.model_dump(exclude={"bot"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) @@ -18,7 +18,7 @@ class AuroraGuildModel(AuroraBaseModel): """Subclass of AuroraBaseModel that includes a guild_id attribute, and a modified to_json() method to match.""" guild_id: int - def to_json(self, indent: int = None, file: Any = None, **kwargs): + def to_json(self, indent: int | None = None, file: Any | None = None, **kwargs): from aurora.utilities.json import ( # pylint: disable=cyclic-import dump, dumps) return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 7bea736..11678b6 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -223,16 +223,16 @@ class Moderation(AuroraGuildModel): target_type: str, target_id: int, role_id: int, - duration: timedelta = None, - reason: str = None, - database: sqlite3.Connection = None, - timestamp: datetime = None, + duration: timedelta | None = None, + reason: str | None = None, + database: sqlite3.Connection | None = None, + timestamp: datetime | None = None, resolved: bool = False, - resolved_by: int = None, - resolved_reason: str = None, - expired: bool = None, - changes: list = None, - metadata: dict = None, + resolved_by: int | None = None, + resolved_reason: str | None = None, + expired: bool | None = None, + changes: list | None = None, + metadata: dict | None = None, ) -> "Moderation": from aurora.utilities.database import connect from aurora.utilities.json import dumps diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index d5978c7..e7886ff 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -20,10 +20,10 @@ async def message_factory( guild: Guild, reason: str, moderation_type: str, - moderator: Union[Member, User] = None, - duration: timedelta = None, - response: InteractionMessage = None, - role: Role = None, + moderator: Union[Member, User] | None = None, + duration: timedelta | None = None, + response: InteractionMessage | None = None, + role: Role | None = None, ) -> Embed: """This function creates a message from set parameters, meant for contacting the moderated user. diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index e85fd58..ff950b1 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -14,8 +14,8 @@ from aurora.utilities.config import config def check_permissions( user: User, permissions: list, - ctx: Union[commands.Context, Interaction] = None, - guild: Guild = None, + ctx: Union[commands.Context, Interaction] | None = None, + guild: Guild | None = None, ) -> Union[bool, str]: """Checks if a user has a specific permission (or a list of permissions) in a channel.""" if ctx: -- 2.45.2 From d91a4f49f9bc36d4d751012f6e6a91ede2912a73 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 21:39:43 -0400 Subject: [PATCH 108/146] fix(aurora): fixed import errors --- aurora/aurora.py | 38 ++++++++++++++++----------------- aurora/importers/aurora.py | 4 ++-- aurora/importers/galacticbot.py | 4 ++-- aurora/menus/addrole.py | 4 ++-- aurora/menus/guild.py | 6 +++--- aurora/menus/immune.py | 4 ++-- aurora/menus/overrides.py | 6 +++--- aurora/models/change.py | 6 +++--- aurora/models/moderation.py | 10 ++++----- aurora/models/partials.py | 2 +- aurora/utilities/database.py | 2 +- aurora/utilities/factory.py | 16 ++++++-------- aurora/utilities/json.py | 2 +- aurora/utilities/utils.py | 12 +++++------ 14 files changed, 55 insertions(+), 61 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index fe9abcb..01655a9 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -23,26 +23,24 @@ from redbot.core.commands.converter import parse_relativedelta, parse_timedelta from redbot.core.utils.chat_formatting import (box, error, humanize_list, humanize_timedelta, warning) -from aurora.importers.aurora import ImportAuroraView -from aurora.importers.galacticbot import ImportGalacticBotView -from aurora.menus.addrole import Addrole -from aurora.menus.guild import Guild -from aurora.menus.immune import Immune -from aurora.menus.overrides import Overrides -from aurora.models.change import Change -from aurora.models.moderation import Moderation -from aurora.utilities.config import config, register_config -from aurora.utilities.database import connect, create_guild_table -from aurora.utilities.factory import (addrole_embed, case_factory, - changes_factory, evidenceformat_factory, - guild_embed, immune_embed, - message_factory, overrides_embed) - -from aurora.utilities.json import dump -from aurora.utilities.logger import logger -from aurora.utilities.utils import (check_moddable, check_permissions, - get_footer_image, log, send_evidenceformat, - timedelta_from_relativedelta) +from .importers.aurora import ImportAuroraView +from .importers.galacticbot import ImportGalacticBotView +from .menus.addrole import Addrole +from .menus.guild import Guild +from .menus.immune import Immune +from .menus.overrides import Overrides +from .models.change import Change +from .models.moderation import Moderation +from .utilities.config import config, register_config +from .utilities.database import connect, create_guild_table +from .utilities.factory import (addrole_embed, case_factory, changes_factory, + evidenceformat_factory, guild_embed, + immune_embed, message_factory, overrides_embed) +from .utilities.json import dump +from .utilities.logger import logger +from .utilities.utils import (check_moddable, check_permissions, + get_footer_image, log, send_evidenceformat, + timedelta_from_relativedelta) class Aurora(commands.Cog): diff --git a/aurora/importers/aurora.py b/aurora/importers/aurora.py index 7d1b273..e9622a4 100644 --- a/aurora/importers/aurora.py +++ b/aurora/importers/aurora.py @@ -8,8 +8,8 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands from redbot.core.utils.chat_formatting import box, warning -from aurora.models.moderation import Moderation -from aurora.utilities.database import connect, create_guild_table +from ..models.moderation import Moderation +from ..utilities.database import connect, create_guild_table class ImportAuroraView(ui.View): diff --git a/aurora/importers/galacticbot.py b/aurora/importers/galacticbot.py index 50d51b9..a7c2d27 100644 --- a/aurora/importers/galacticbot.py +++ b/aurora/importers/galacticbot.py @@ -7,8 +7,8 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands from redbot.core.utils.chat_formatting import box, warning -from aurora.models.moderation import Change, Moderation -from aurora.utilities.database import connect, create_guild_table +from ..models.moderation import Change, Moderation +from ..utilities.database import connect, create_guild_table class ImportGalacticBotView(ui.View): diff --git a/aurora/menus/addrole.py b/aurora/menus/addrole.py index 9f4d1ff..36c9056 100644 --- a/aurora/menus/addrole.py +++ b/aurora/menus/addrole.py @@ -2,8 +2,8 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands from redbot.core.utils.chat_formatting import error -from aurora.utilities.config import config -from aurora.utilities.factory import addrole_embed +from ..utilities.config import config +from ..utilities.factory import addrole_embed class Addrole(ui.View): diff --git a/aurora/menus/guild.py b/aurora/menus/guild.py index ece8131..71186b7 100644 --- a/aurora/menus/guild.py +++ b/aurora/menus/guild.py @@ -1,9 +1,9 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands -from aurora.utilities.config import config -from aurora.utilities.factory import guild_embed -from aurora.utilities.utils import create_pagesize_options +from ..utilities.config import config +from ..utilities.factory import guild_embed +from ..utilities.utils import create_pagesize_options class Guild(ui.View): diff --git a/aurora/menus/immune.py b/aurora/menus/immune.py index 7d85d12..dd710b1 100644 --- a/aurora/menus/immune.py +++ b/aurora/menus/immune.py @@ -2,8 +2,8 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands from redbot.core.utils.chat_formatting import error -from aurora.utilities.config import config -from aurora.utilities.factory import immune_embed +from ..utilities.config import config +from ..utilities.factory import immune_embed class Immune(ui.View): diff --git a/aurora/menus/overrides.py b/aurora/menus/overrides.py index beefaef..4327131 100644 --- a/aurora/menus/overrides.py +++ b/aurora/menus/overrides.py @@ -1,9 +1,9 @@ from discord import ButtonStyle, Interaction, Message, ui from redbot.core import commands -from aurora.utilities.config import config -from aurora.utilities.factory import overrides_embed -from aurora.utilities.utils import create_pagesize_options +from ..utilities.config import config +from ..utilities.factory import overrides_embed +from ..utilities.utils import create_pagesize_options class Overrides(ui.View): diff --git a/aurora/models/change.py b/aurora/models/change.py index ceb4641..029f76b 100644 --- a/aurora/models/change.py +++ b/aurora/models/change.py @@ -4,9 +4,9 @@ from typing import Literal, Optional from redbot.core.bot import Red -from aurora.models.base import AuroraBaseModel -from aurora.models.partials import PartialUser -from aurora.utilities.logger import logger +from ..models.base import AuroraBaseModel +from ..models.partials import PartialUser +from ..utilities.logger import logger class Change(AuroraBaseModel): diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 11678b6..d63df3e 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -8,11 +8,11 @@ import discord from discord import NotFound from redbot.core.bot import Red -from aurora.models.base import AuroraGuildModel -from aurora.models.change import Change -from aurora.models.partials import PartialChannel, PartialRole, PartialUser -from aurora.utilities.logger import logger -from aurora.utilities.utils import get_next_case_number +from ..models.base import AuroraGuildModel +from ..models.change import Change +from ..models.partials import PartialChannel, PartialRole, PartialUser +from ..utilities.logger import logger +from ..utilities.utils import get_next_case_number class Moderation(AuroraGuildModel): diff --git a/aurora/models/partials.py b/aurora/models/partials.py index 48ff2a0..0402e88 100644 --- a/aurora/models/partials.py +++ b/aurora/models/partials.py @@ -1,7 +1,7 @@ from discord import Forbidden, HTTPException, InvalidData, NotFound from redbot.core.bot import Red -from aurora.models.base import AuroraBaseModel, AuroraGuildModel +from ..models.base import AuroraBaseModel, AuroraGuildModel class PartialUser(AuroraBaseModel): diff --git a/aurora/utilities/database.py b/aurora/utilities/database.py index 16f8a7e..588ad35 100644 --- a/aurora/utilities/database.py +++ b/aurora/utilities/database.py @@ -5,7 +5,7 @@ import sqlite3 from discord import Guild from redbot.core import data_manager -from aurora.utilities.logger import logger +from .logger import logger def connect() -> sqlite3.Connection: diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index e7886ff..e032a6e 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -2,18 +2,14 @@ from datetime import datetime, timedelta from typing import Union -from discord import (Color, Embed, Guild, Interaction, InteractionMessage, - Member, Role, User) +from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User from redbot.core import commands -from redbot.core.utils.chat_formatting import (bold, box, error, - humanize_timedelta, warning) - -from aurora.models.moderation import Moderation -from aurora.models.partials import PartialUser -from aurora.utilities.config import config -from aurora.utilities.utils import (get_bool_emoji, get_next_case_number, - get_pagesize_str) +from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning +from ..models.moderation import Moderation +from ..models.partials import PartialUser +from .config import config +from .utils import get_bool_emoji, get_next_case_number, get_pagesize_str async def message_factory( color: Color, diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index 73595ee..7a0cd15 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from redbot.core.bot import Red -from aurora.models.base import AuroraBaseModel +from ..models.base import AuroraBaseModel class JSONEncoder(json.JSONEncoder): diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index ff950b1..ed115de 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -8,7 +8,7 @@ from discord.errors import Forbidden from redbot.core import commands, data_manager from redbot.core.utils.chat_formatting import error -from aurora.utilities.config import config +from ..utilities.config import config def check_permissions( @@ -111,7 +111,7 @@ async def check_moddable( def get_next_case_number(guild_id: str, cursor=None) -> int: """This function returns the next case number from the MySQL table for a specific guild.""" - from aurora.utilities.database import connect + from .database import connect if not cursor: database = connect() @@ -125,8 +125,8 @@ def get_next_case_number(guild_id: str, cursor=None) -> int: async def log(interaction: Interaction, moderation_id: int, resolved: bool = False) -> None: """This function sends a message to the guild's configured logging channel when an infraction takes place.""" - from aurora.models.moderation import Moderation - from aurora.utilities.factory import log_factory + from ..models.moderation import Moderation + from .factory import log_factory logging_channel_id = await config.guild(interaction.guild).log_channel() if logging_channel_id != " ": @@ -147,8 +147,8 @@ async def log(interaction: Interaction, moderation_id: int, resolved: bool = Fal async def send_evidenceformat(interaction: Interaction, moderation_id: int) -> None: """This function sends an ephemeral message to the moderator who took the moderation action, with a pre-made codeblock for use in the mod-evidence channel.""" - from aurora.models.moderation import Moderation - from aurora.utilities.factory import evidenceformat_factory + from ..models.moderation import Moderation + from .factory import evidenceformat_factory send_evidence_bool = ( await config.user(interaction.user).auto_evidenceformat() -- 2.45.2 From 92d221ff70f3a5c7b33726b3a786ab96c0be0731 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 21:43:08 -0400 Subject: [PATCH 109/146] fix(aurora): forgot a file! --- aurora/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/__init__.py b/aurora/__init__.py index a66006c..49ef5ec 100644 --- a/aurora/__init__.py +++ b/aurora/__init__.py @@ -1,4 +1,4 @@ -from aurora.aurora import Aurora +from .aurora import Aurora async def setup(bot): -- 2.45.2 From 904fd1c914882ade025b86612b969e9ccacf1d4c Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Mon, 6 May 2024 21:46:01 -0400 Subject: [PATCH 110/146] fix(aurora): fixed more import errors --- aurora/models/base.py | 6 ++---- aurora/models/moderation.py | 10 +++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/aurora/models/base.py b/aurora/models/base.py index 45c8a60..08e1cd1 100644 --- a/aurora/models/base.py +++ b/aurora/models/base.py @@ -10,8 +10,7 @@ class AuroraBaseModel(BaseModel): bot: Red def to_json(self, indent: int | None = None, file: Any | None = None, **kwargs): - from aurora.utilities.json import ( # pylint: disable=cyclic-import - dump, dumps) + from ..utilities.json import dump, dumps # pylint: disable=cyclic-import return dump(self.model_dump(exclude={"bot"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) class AuroraGuildModel(AuroraBaseModel): @@ -19,6 +18,5 @@ class AuroraGuildModel(AuroraBaseModel): guild_id: int def to_json(self, indent: int | None = None, file: Any | None = None, **kwargs): - from aurora.utilities.json import ( # pylint: disable=cyclic-import - dump, dumps) + from ..utilities.json import dump, dumps # pylint: disable=cyclic-import return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index d63df3e..a5c6fc0 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -114,8 +114,8 @@ class Moderation(AuroraGuildModel): self.update() def update(self): - from aurora.utilities.database import connect - from aurora.utilities.json import dumps + from ..utilities.database import connect + from ..utilities.json import dumps query = f"UPDATE moderation_{self.guild_id} SET timestamp = ?, moderation_type = ?, target_type = ?, moderator_id = ?, role_id = ?, duration = ?, end_timestamp = ?, reason = ?, resolved = ?, resolved_by = ?, resolve_reason = ?, expired = ?, changes = ?, metadata = ? WHERE moderation_id = ?;" with connect() as database: @@ -199,7 +199,7 @@ class Moderation(AuroraGuildModel): @classmethod def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": - from aurora.utilities.database import connect + from ..utilities.database import connect query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;" with connect() as database: @@ -234,8 +234,8 @@ class Moderation(AuroraGuildModel): changes: list | None = None, metadata: dict | None = None, ) -> "Moderation": - from aurora.utilities.database import connect - from aurora.utilities.json import dumps + from ..utilities.database import connect + from ..utilities.json import dumps if not timestamp: timestamp = datetime.fromtimestamp(time()) elif not isinstance(timestamp, datetime): -- 2.45.2 From dc51aa7bdce0bac95b5a1d45540be52ebc789caf Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Thu, 9 May 2024 21:27:26 -0400 Subject: [PATCH 111/146] fix(aurora): import fixes --- aurora/models/change.py | 4 ++-- aurora/models/moderation.py | 11 +++++++---- aurora/models/partials.py | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/aurora/models/change.py b/aurora/models/change.py index 029f76b..0c7148a 100644 --- a/aurora/models/change.py +++ b/aurora/models/change.py @@ -4,9 +4,9 @@ from typing import Literal, Optional from redbot.core.bot import Red -from ..models.base import AuroraBaseModel -from ..models.partials import PartialUser from ..utilities.logger import logger +from .base import AuroraBaseModel +from .partials import PartialUser class Change(AuroraBaseModel): diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index a5c6fc0..c216218 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -8,11 +8,11 @@ import discord from discord import NotFound from redbot.core.bot import Red -from ..models.base import AuroraGuildModel -from ..models.change import Change -from ..models.partials import PartialChannel, PartialRole, PartialUser from ..utilities.logger import logger from ..utilities.utils import get_next_case_number +from .base import AuroraGuildModel +from .change import Change +from .partials import PartialChannel, PartialRole, PartialUser class Moderation(AuroraGuildModel): @@ -241,7 +241,10 @@ class Moderation(AuroraGuildModel): elif not isinstance(timestamp, datetime): timestamp = datetime.fromtimestamp(timestamp) - if duration != "NULL" and duration is not None: + if duration == "NULL": + duration = None + + if duration is not None: end_timestamp = timestamp + duration else: duration = None diff --git a/aurora/models/partials.py b/aurora/models/partials.py index 0402e88..4b9e2b6 100644 --- a/aurora/models/partials.py +++ b/aurora/models/partials.py @@ -1,7 +1,7 @@ from discord import Forbidden, HTTPException, InvalidData, NotFound from redbot.core.bot import Red -from ..models.base import AuroraBaseModel, AuroraGuildModel +from .base import AuroraBaseModel, AuroraGuildModel class PartialUser(AuroraBaseModel): -- 2.45.2 From 67b33a2eb8e120daa0bb6222a142b3dd564b4a31 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 03:46:20 -0400 Subject: [PATCH 112/146] feat(aurora): added a slowmode command --- aurora/aurora.py | 53 +++++++++++++++++++++++++++++++------ aurora/models/moderation.py | 2 +- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 01655a9..5d4e983 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -20,8 +20,7 @@ from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red from redbot.core.commands.converter import parse_relativedelta, parse_timedelta -from redbot.core.utils.chat_formatting import (box, error, humanize_list, - humanize_timedelta, warning) +from redbot.core.utils.chat_formatting import box, error, humanize_list, humanize_timedelta, warning from .importers.aurora import ImportAuroraView from .importers.galacticbot import ImportGalacticBotView @@ -33,14 +32,10 @@ from .models.change import Change from .models.moderation import Moderation from .utilities.config import config, register_config from .utilities.database import connect, create_guild_table -from .utilities.factory import (addrole_embed, case_factory, changes_factory, - evidenceformat_factory, guild_embed, - immune_embed, message_factory, overrides_embed) +from .utilities.factory import addrole_embed, case_factory, changes_factory, evidenceformat_factory, guild_embed, immune_embed, message_factory, overrides_embed from .utilities.json import dump from .utilities.logger import logger -from .utilities.utils import (check_moddable, check_permissions, - get_footer_image, log, send_evidenceformat, - timedelta_from_relativedelta) +from .utilities.utils import check_moddable, check_permissions, get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta class Aurora(commands.Cog): @@ -970,6 +965,48 @@ class Aurora(commands.Cog): await log(interaction, moderation.id) await send_evidenceformat(interaction, moderation.id) + @app_commands.command(name="slowmode") + async def slowmode( + self, + interaction: discord.Interaction, + interval: int, + channel: discord.TextChannel | None = None, + reason: str | None = None, + ): + """Set the slowmode of a channel. + + Parameters + ----------- + interval: int + The slowmode interval in seconds + channel: discord.TextChannel + The channel to set the slowmode in + reason: str + Why are you setting the slowmode?""" + if channel is None: + channel = interaction.channel + + if not await check_moddable(channel, interaction, ["manage_channels"]): + return + + await channel.edit(slowmode_delay=interval) + await interaction.response.send_message(f"Slowmode in {channel.mention} has been set to {interval} seconds!\n**Reason** - `{reason}`") + + moderation = Moderation.log( + interaction.client, + interaction.guild.id, + interaction.user.id, + "SLOWMODE", + "CHANNEL", + channel.id, + None, + None, + reason, + ) + await interaction.edit_original_response(content=f"Slowmode in {channel.mention} has been set to {interval} seconds! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`") + await log(interaction, moderation.id) + await send_evidenceformat(interaction, moderation.id) + @app_commands.command(name="history") async def history( self, diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index c216218..9ebb733 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -222,7 +222,7 @@ class Moderation(AuroraGuildModel): moderation_type: str, target_type: str, target_id: int, - role_id: int, + role_id: int | None = None, duration: timedelta | None = None, reason: str | None = None, database: sqlite3.Connection | None = None, -- 2.45.2 From 0411e3dab732bb4efbc4d5678dccfb8794084829 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 03:49:55 -0400 Subject: [PATCH 113/146] fix(aurora): fixed a typeerror in the check_moddable function --- aurora/utilities/utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index ed115de..42ce1cb 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from typing import Optional, Union from dateutil.relativedelta import relativedelta as rd -from discord import File, Guild, Interaction, Member, SelectOption, User +from discord import File, Guild, Interaction, Member, SelectOption, TextChannel, User from discord.errors import Forbidden from redbot.core import commands, data_manager from redbot.core.utils.chat_formatting import error @@ -40,9 +40,10 @@ def check_permissions( async def check_moddable( - target: Union[User, Member], interaction: Interaction, permissions: list + target: Union[User, Member, TextChannel], interaction: Interaction, permissions: list ) -> bool: """Checks if a moderator can moderate a target.""" + is_channel = isinstance(target, TextChannel) if check_permissions(interaction.client.user, permissions, guild=interaction.guild): await interaction.response.send_message( error( @@ -68,7 +69,7 @@ async def check_moddable( ) return False - if target.bot: + if not is_channel and target.bot: await interaction.response.send_message( content="You cannot moderate bots!", ephemeral=True ) -- 2.45.2 From 51d3245703aec322b18ec395a5e329294b86ee1b Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 03:51:43 -0400 Subject: [PATCH 114/146] fix(aurora): fixed a typeerror in Moderation.log() --- aurora/models/moderation.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 9ebb733..272a165 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -251,7 +251,10 @@ class Moderation(AuroraGuildModel): end_timestamp = None if not expired: - expired = bool(timestamp > end_timestamp) + if end_timestamp: + expired = bool(timestamp > end_timestamp) + else: + expired = False if reason == "NULL": reason = None -- 2.45.2 From 797fd561c919fef491610cdb0639ab49be161fb4 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 03:53:38 -0400 Subject: [PATCH 115/146] fix(aurora): fixed another typeerror --- aurora/models/moderation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 272a165..10262cc 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -173,8 +173,9 @@ class Moderation(AuroraGuildModel): if result[14] is not None: changes = json.loads(result[14]) change_obj_list = [] - for change in changes: - change_obj_list.append(Change.from_dict(bot=bot, data=change)) + if changes: + for change in changes: + change_obj_list.append(Change.from_dict(bot=bot, data=change)) case = { "moderation_id": int(result[0]), -- 2.45.2 From 599ab8c51dede7bb5d840b3fe3d33425dcba9897 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 03:56:32 -0400 Subject: [PATCH 116/146] fix(aurora): fixed a non-async function being awaited --- aurora/utilities/factory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index e032a6e..76e83f3 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -11,6 +11,7 @@ from ..models.partials import PartialUser from .config import config from .utils import get_bool_emoji, get_next_case_number, get_pagesize_str + async def message_factory( color: Color, guild: Guild, @@ -87,7 +88,7 @@ async def message_factory( embed.set_author(name=guild.name) embed.set_footer( - text=f"Case #{await get_next_case_number(guild.id):,}", + text=f"Case #{get_next_case_number(guild.id):,}", icon_url="attachment://arrow.png", ) -- 2.45.2 From 0b1d1d29e6c09089d930a789ae39e4490d2a93a4 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 03:57:54 -0400 Subject: [PATCH 117/146] fix(aurora): hopefully fixed a pydantic validation error --- aurora/models/moderation.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 10262cc..555ca0f 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -177,6 +177,9 @@ class Moderation(AuroraGuildModel): for change in changes: change_obj_list.append(Change.from_dict(bot=bot, data=change)) + if result[15] is not None: + metadata = json.loads(result[15]) + case = { "moderation_id": int(result[0]), "guild_id": int(guild_id), @@ -194,7 +197,7 @@ class Moderation(AuroraGuildModel): "resolve_reason": result[12], "expired": bool(result[13]), "changes": change_obj_list if result[14] else [], - "metadata": json.loads(result[15].replace('\\"', '"').replace('["{', '[{').replace('}"]', '}]')) if result[15] else {}, + "metadata": metadata if result[15] else {}, } return cls.from_dict(bot=bot, data=case) -- 2.45.2 From c90796f6b3b603baa64940f0527b06e7514c744e Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:01:15 -0400 Subject: [PATCH 118/146] fix(aurora): hopefully actually fixed the pydantic validation error --- aurora/models/moderation.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 555ca0f..381c29a 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -176,9 +176,13 @@ class Moderation(AuroraGuildModel): if changes: for change in changes: change_obj_list.append(Change.from_dict(bot=bot, data=change)) + else: + change_obj_list = [] if result[15] is not None: metadata = json.loads(result[15]) + else: + metadata = {} case = { "moderation_id": int(result[0]), @@ -196,8 +200,8 @@ class Moderation(AuroraGuildModel): "resolved_by": result[11], "resolve_reason": result[12], "expired": bool(result[13]), - "changes": change_obj_list if result[14] else [], - "metadata": metadata if result[15] else {}, + "changes": change_obj_list, + "metadata": metadata, } return cls.from_dict(bot=bot, data=case) -- 2.45.2 From 39cb5feb50c828f2fc2b7286be92395b938756c5 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:02:15 -0400 Subject: [PATCH 119/146] misc(aurora): adding temporary debug logging --- aurora/models/moderation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 381c29a..512ddf7 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -203,6 +203,7 @@ class Moderation(AuroraGuildModel): "changes": change_obj_list, "metadata": metadata, } + logger.debug(case) return cls.from_dict(bot=bot, data=case) @classmethod -- 2.45.2 From ed923f1d9be1a17111af05a9c570d6ce84588e31 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:04:21 -0400 Subject: [PATCH 120/146] fix(aurora): finally actually maybe fixed the pydantic validation error --- aurora/models/moderation.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 512ddf7..caac217 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -176,8 +176,6 @@ class Moderation(AuroraGuildModel): if changes: for change in changes: change_obj_list.append(Change.from_dict(bot=bot, data=change)) - else: - change_obj_list = [] if result[15] is not None: metadata = json.loads(result[15]) @@ -201,7 +199,7 @@ class Moderation(AuroraGuildModel): "resolve_reason": result[12], "expired": bool(result[13]), "changes": change_obj_list, - "metadata": metadata, + "metadata": metadata if metadata else {}, } logger.debug(case) return cls.from_dict(bot=bot, data=case) -- 2.45.2 From 0cc7d6079dd011e6459b89b31a446f7eabf5f784 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:06:05 -0400 Subject: [PATCH 121/146] fix(aurora): removed useless debug statement --- aurora/models/moderation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index caac217..7018909 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -201,7 +201,6 @@ class Moderation(AuroraGuildModel): "changes": change_obj_list, "metadata": metadata if metadata else {}, } - logger.debug(case) return cls.from_dict(bot=bot, data=case) @classmethod -- 2.45.2 From 7a9c9846de5c99e5b934baed4572418523a6a8e1 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:09:27 -0400 Subject: [PATCH 122/146] fix(aurora): fixed a broken from_id method --- aurora/models/partials.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aurora/models/partials.py b/aurora/models/partials.py index 4b9e2b6..48f4030 100644 --- a/aurora/models/partials.py +++ b/aurora/models/partials.py @@ -47,12 +47,12 @@ class PartialChannel(AuroraGuildModel): if not channel: try: channel = await bot.fetch_channel(channel_id) - return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) + return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, name=channel.name) except (NotFound, InvalidData, HTTPException, Forbidden) as e: if e == Forbidden: return cls(bot=bot, guild_id=0, id=channel_id, name="Forbidden Channel") return cls(bot=bot, guild_id=0, id=channel_id, name="Deleted Channel") - return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, username=channel.name) + return cls(bot=bot, guild_id=channel.guild.id, id=channel.id, name=channel.name) class PartialRole(AuroraGuildModel): id: int -- 2.45.2 From 9a4f19f4a1c52b144cc31b868b978dab1c57dc86 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:12:28 -0400 Subject: [PATCH 123/146] fix(aurora): show metadata key/value pairs in `/case` --- aurora/aurora.py | 1 + aurora/utilities/factory.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/aurora/aurora.py b/aurora/aurora.py index 5d4e983..0eaee86 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -1002,6 +1002,7 @@ class Aurora(commands.Cog): None, None, reason, + metadata={"interval": interval} ) await interaction.edit_original_response(content=f"Slowmode in {channel.mention} has been set to {interval} seconds! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`") await log(interaction, moderation.id) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 76e83f3..f7568d9 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -188,13 +188,17 @@ async def case_factory(interaction: Interaction, moderation: Moderation) -> Embe if moderation.metadata: if moderation.metadata["imported_from"]: + del moderation.metadata["imported_from"] embed.description += ( f"\n**Imported From:** {moderation.metadata['imported_from']}" ) if moderation.metadata["imported_timestamp"]: + del moderation.metadata["imported_timestamp"] embed.description += ( f"\n**Imported Timestamp:** | " ) + for key, value in moderation.metadata.items(): + embed.description += f"\n**{key.title()}:** {value}" embed.add_field(name="Reason", value=box(moderation.reason), inline=False) -- 2.45.2 From 3dcc6379202735abed10f3d77668674dcf7a4bc8 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:15:27 -0400 Subject: [PATCH 124/146] fix(aurora): avoid keyerrors --- aurora/utilities/factory.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index f7568d9..282e02d 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -187,18 +187,19 @@ async def case_factory(interaction: Interaction, moderation: Moderation) -> Embe embed.description += f"\n**Role:** {role.name}" if moderation.metadata: - if moderation.metadata["imported_from"]: - del moderation.metadata["imported_from"] + if moderation.metadata.get("imported_from"): embed.description += ( f"\n**Imported From:** {moderation.metadata['imported_from']}" ) - if moderation.metadata["imported_timestamp"]: - del moderation.metadata["imported_timestamp"] + moderation.metadata.pop("imported_from") + if moderation.metadata.get("imported_timestamp"): embed.description += ( f"\n**Imported Timestamp:** | " ) - for key, value in moderation.metadata.items(): - embed.description += f"\n**{key.title()}:** {value}" + moderation.metadata.pop("imported_timestamp") + if moderation.metadata.items(): + for key, value in moderation.metadata.items(): + embed.description += f"\n**{key.title()}:** {value}" embed.add_field(name="Reason", value=box(moderation.reason), inline=False) -- 2.45.2 From 7f71ca3d6d078a3e054e47c86fe24d7596e4ab96 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:16:48 -0400 Subject: [PATCH 125/146] fix(aurora): change interval metadata --- aurora/aurora.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 0eaee86..e22776a 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -1002,7 +1002,7 @@ class Aurora(commands.Cog): None, None, reason, - metadata={"interval": interval} + metadata={"interval": f"{interval} seconds"} ) await interaction.edit_original_response(content=f"Slowmode in {channel.mention} has been set to {interval} seconds! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`") await log(interaction, moderation.id) -- 2.45.2 From bfb4d8768dbee884016476088febcdf1389967dc Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:18:17 -0400 Subject: [PATCH 126/146] fix(aurora): add moderation metadata to the log factory --- aurora/utilities/factory.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 282e02d..50b7067 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -127,6 +127,10 @@ async def log_factory( + f"\n**Duration:** {duration_embed}\n**Expired:** {moderation.expired}" ) + if moderation.metadata.items(): + for key, value in moderation.metadata.items(): + embed.description += f"\n**{key.title()}:** {value}" + embed.add_field(name="Reason", value=box(moderation.reason), inline=False) embed.add_field( @@ -148,6 +152,10 @@ async def log_factory( + f"\n**Duration:** {humanize_timedelta(timedelta=moderation.duration)} | " ) + if moderation.metadata.items(): + for key, value in moderation.metadata.items(): + embed.description += f"\n**{key.title()}:** {value}" + embed.add_field(name="Reason", value=box(moderation.reason), inline=False) return embed -- 2.45.2 From 5b64ee957831eaec8b8a7f35f146280abdf5984e Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:22:42 -0400 Subject: [PATCH 127/146] feat(aurora): add metadata to evidenceformat --- aurora/utilities/factory.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 50b7067..0485af7 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -289,6 +289,9 @@ async def evidenceformat_factory(moderation: Moderation) -> str: content += f"\nReason: {moderation.reason}" + for key, value in moderation.metadata.items(): + content += f"\n{key.title()}: {value}" + return box(content, "prolog") -- 2.45.2 From 73c910488248d9a3eb1cc378b0de7264ae449e90 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Fri, 24 May 2024 04:26:58 -0400 Subject: [PATCH 128/146] fix(aurora): cast to string before checking length to avoid typeerrors when reason is None --- aurora/aurora.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index e22776a..ec8a0e6 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -1185,7 +1185,7 @@ class Aurora(commands.Cog): field_name = f"Case #{mod.id:,} ({str.title(mod.type)})" field_value = f"**Target:** `{target.name}` ({target.id})\n**Moderator:** `{moderator.name}` ({moderator.id})" - if len(mod.reason) > 125: + if len(str(mod.reason)) > 125: field_value += f"\n**Reason:** `{str(mod.reason)[:125]}...`" else: field_value += f"\n**Reason:** `{str(mod.reason)}`" -- 2.45.2 From 641f45d1262c80f2205b72c2dbf1fdf141f3508e Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Sun, 2 Jun 2024 23:53:19 -0400 Subject: [PATCH 129/146] feat(aurora): add the from_sql_all classmethod to the Moderation model --- aurora/models/moderation.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 7018909..f990693 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -119,8 +119,7 @@ class Moderation(AuroraGuildModel): query = f"UPDATE moderation_{self.guild_id} SET timestamp = ?, moderation_type = ?, target_type = ?, moderator_id = ?, role_id = ?, duration = ?, end_timestamp = ?, reason = ?, resolved = ?, resolved_by = ?, resolve_reason = ?, expired = ?, changes = ?, metadata = ? WHERE moderation_id = ?;" with connect() as database: - cursor = database.cursor() - cursor.execute(query, ( + database.execute(query, ( self.timestamp.timestamp(), self.moderation_type, self.target_type, @@ -137,7 +136,6 @@ class Moderation(AuroraGuildModel): dumps(self.metadata), self.moderation_id, )) - cursor.close() logger.debug("Row updated in moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s", self.moderation_id, @@ -219,6 +217,25 @@ class Moderation(AuroraGuildModel): raise ValueError(f"Case {moderation_id} not found in moderation_{guild_id}!") + @classmethod + def from_sql_all(cls, bot: Red, guild_id: int) -> List["Moderation"]: + from ..utilities.database import connect + query = f"SELECT * FROM moderation_{guild_id};" + + with connect() as database: + cursor = database.cursor() + cursor.execute(query) + results = cursor.fetchall() + cursor.close() + + if results: + cases = [] + for result in results: + cases.append(cls.from_result(bot, result, guild_id)) + return cases + + return [] + @classmethod def log( cls, -- 2.45.2 From 1990b97518a32fca0caed2552c12e90a51a2460d Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Sun, 2 Jun 2024 23:59:17 -0400 Subject: [PATCH 130/146] fix(aurora): ignore moderation cases where the moderation_id is 0 --- aurora/models/moderation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index f990693..e5c852d 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -231,7 +231,9 @@ class Moderation(AuroraGuildModel): if results: cases = [] for result in results: - cases.append(cls.from_result(bot, result, guild_id)) + case = cls.from_result(bot, result, guild_id) + if case.moderation_id != 0: + cases.append(case) return cases return [] -- 2.45.2 From db477c4744fd4de1e58c3aa60ce744c9b9e5c07d Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:07:52 -0400 Subject: [PATCH 131/146] fix(aurora): fixed an issue with json encoding --- aurora/models/base.py | 10 ++++++++-- aurora/utilities/json.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/aurora/models/base.py b/aurora/models/base.py index 08e1cd1..e6005ee 100644 --- a/aurora/models/base.py +++ b/aurora/models/base.py @@ -9,14 +9,20 @@ class AuroraBaseModel(BaseModel): model_config = ConfigDict(ignored_types=(Red,), arbitrary_types_allowed=True) bot: Red + def dump(self) -> dict: + return self.model_dump(exclude={"bot"}) + def to_json(self, indent: int | None = None, file: Any | None = None, **kwargs): from ..utilities.json import dump, dumps # pylint: disable=cyclic-import - return dump(self.model_dump(exclude={"bot"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) + return dump(self.dump(), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot"}), indent=indent, **kwargs) class AuroraGuildModel(AuroraBaseModel): """Subclass of AuroraBaseModel that includes a guild_id attribute, and a modified to_json() method to match.""" guild_id: int + def dump(self) -> dict: + return self.model_dump(exclude={"bot", "guild_id"}) + def to_json(self, indent: int | None = None, file: Any | None = None, **kwargs): from ..utilities.json import dump, dumps # pylint: disable=cyclic-import - return dump(self.model_dump(exclude={"bot", "guild_id"}), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) + return dump(self.dump(), file, indent=indent, **kwargs) if file else dumps(self.model_dump(exclude={"bot", "guild_id"}), indent=indent, **kwargs) diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index 7a0cd15..892a227 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -13,7 +13,7 @@ class JSONEncoder(json.JSONEncoder): if isinstance(o, timedelta): return str(o) if isinstance(o, AuroraBaseModel): - return o.to_json() + return o.dump() if isinstance(o, Red): return None return super().default(o) -- 2.45.2 From 21e51dc3208b651e95dcfb0a6a06cd010e3592dc Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:09:39 -0400 Subject: [PATCH 132/146] misc(aurora): minor syntax change --- aurora/utilities/json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/utilities/json.py b/aurora/utilities/json.py index 892a227..80f1259 100644 --- a/aurora/utilities/json.py +++ b/aurora/utilities/json.py @@ -70,7 +70,7 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, default=default, sort_keys=sort_keys, **kw - ) + ) # This is a wrapper around the json module's dump function that uses our custom JSONEncoder class def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, -- 2.45.2 From 7fc6235abe3382f1f1ac0f0b503fe3e392127636 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:15:17 -0400 Subject: [PATCH 133/146] fix(aurora): remove bot keys from the change import when importing from aurora --- aurora/importers/aurora.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/aurora/importers/aurora.py b/aurora/importers/aurora.py index e9622a4..1a1777d 100644 --- a/aurora/importers/aurora.py +++ b/aurora/importers/aurora.py @@ -72,11 +72,15 @@ class ImportAuroraView(ui.View): case["moderator_id"] = int(case["moderator_id"]) if "changes" not in case or not case["changes"]: - case["changes"] = [] + changes = [] else: - case["changes"] = json.loads(case["changes"]) - if isinstance(case["changes"], str): - case["changes"] = json.loads(case["changes"]) + changes = json.loads(case["changes"]) + if isinstance(changes, str): + changes: list[dict] = json.loads(changes) + + for change in changes: + if change.get("bot"): + del change["bot"] if "metadata" not in case: metadata = {} @@ -107,7 +111,7 @@ class ImportAuroraView(ui.View): resolved_by=case["resolved_by"], resolved_reason=case["resolve_reason"], expired=case["expired"], - changes=case["changes"], + changes=changes, metadata=metadata, database=database, ) -- 2.45.2 From c35580c576da87c4cd7089f55e92a0480fd43f13 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:17:33 -0400 Subject: [PATCH 134/146] misc(aurora): change to aurora importer log arguments --- aurora/importers/aurora.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/aurora/importers/aurora.py b/aurora/importers/aurora.py index 1a1777d..c184c1c 100644 --- a/aurora/importers/aurora.py +++ b/aurora/importers/aurora.py @@ -97,15 +97,15 @@ class ImportAuroraView(ui.View): duration = None Moderation.log( - interaction.client, - self.ctx.guild.id, - case["moderator_id"], - case["moderation_type"], - case["target_type"], - case["target_id"], - case["role_id"], - duration, - case["reason"], + bot=interaction.client, + guild_id=self.ctx.guild.id, + moderator_id=case["moderator_id"], + moderation_type=case["moderation_type"], + target_type=case["target_type"], + target_id=case["target_id"], + role_id=case["role_id"], + duration=duration, + reason=case["reason"], timestamp=case["timestamp"], resolved=case["resolved"], resolved_by=case["resolved_by"], -- 2.45.2 From 76f176d4ccf7f61f91207f2ac34e8c91c5540915 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:22:32 -0400 Subject: [PATCH 135/146] feat(aurora): change history to use `Moderation.from_sql_all()` --- aurora/aurora.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index ec8a0e6..e4f9c2b 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -1083,21 +1083,6 @@ class Aurora(commands.Cog): database = connect() if export: - database.row_factory = sqlite3.Row - cursor = database.cursor() - - query = f"""SELECT * - FROM moderation_{interaction.guild.id} - ORDER BY moderation_id DESC;""" - cursor.execute(query) - - results = cursor.fetchall() - - cases = [] - for result in results: - case = dict(result) - cases.append(case) - try: filename = ( str(data_manager.cog_data_path(cog_instance=self)) @@ -1105,8 +1090,10 @@ class Aurora(commands.Cog): + f"moderation_{interaction.guild.id}.json" ) + cases = Moderation.from_sql_all(interaction.client, interaction.guild.id) + with open(filename, "w", encoding="utf-8") as f: - dump(cases, f, indent=2) + dump(obj=cases, fp=f, indent=2) await interaction.followup.send( file=discord.File( @@ -1124,7 +1111,6 @@ class Aurora(commands.Cog): + box(e, "py"), ephemeral=ephemeral, ) - cursor.close() database.close() return -- 2.45.2 From d6467544666a0635da7d3eed22b2515dd7a2076f Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:47:56 -0400 Subject: [PATCH 136/146] feat(aurora): added a bunch of functionality to the Moderation model --- aurora/aurora.py | 2 +- aurora/models/moderation.py | 65 ++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index e4f9c2b..4497d40 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -1090,7 +1090,7 @@ class Aurora(commands.Cog): + f"moderation_{interaction.guild.id}.json" ) - cases = Moderation.from_sql_all(interaction.client, interaction.guild.id) + cases = Moderation.get_all_cases(bot=interaction.client, guild_id=interaction.guild.id) with open(filename, "w", encoding="utf-8") as f: dump(obj=cases, fp=f, indent=2) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index e5c852d..db9e681 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -202,42 +202,63 @@ class Moderation(AuroraGuildModel): return cls.from_dict(bot=bot, data=case) @classmethod - def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": + def execute(cls, bot: Red, query: str, parameters: tuple | None = None) -> List["Moderation"]: from ..utilities.database import connect - query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;" - + if not parameters: + parameters = () with connect() as database: cursor = database.cursor() - cursor.execute(query, (moderation_id,)) - result = cursor.fetchone() - cursor.close() - - if result and not moderation_id == 0: - return cls.from_result(bot, result, guild_id) - - raise ValueError(f"Case {moderation_id} not found in moderation_{guild_id}!") - - @classmethod - def from_sql_all(cls, bot: Red, guild_id: int) -> List["Moderation"]: - from ..utilities.database import connect - query = f"SELECT * FROM moderation_{guild_id};" - - with connect() as database: - cursor = database.cursor() - cursor.execute(query) + cursor.execute(query, parameters=parameters) results = cursor.fetchall() cursor.close() if results: cases = [] for result in results: - case = cls.from_result(bot, result, guild_id) + case = cls.from_result(bot=bot, result=result) if case.moderation_id != 0: cases.append(case) return cases - return [] + @classmethod + def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": + return cls.find_by_id(bot=bot, moderation_id=moderation_id, guild_id=guild_id) + + @classmethod + def find_by_id(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": + query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;" + case = cls.execute(bot=bot, query=query, parameters=(moderation_id,)) + if case: + return case[0] + raise ValueError(f"Case {moderation_id} not found in moderation_{guild_id}!") + + @classmethod + def find_by_target(cls, bot: Red, guild_id: int, target: int, types: list | None = None) -> List["Moderation"]: + query = f"SELECT * FROM moderation_{guild_id} WHERE target_id = ?" + if types: + query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})" + query += " ORDER BY moderation_id DESC;" + + return cls.execute(bot=bot, query=query, parameters=(target, *types) if types else (target,)) + + @classmethod + def find_by_moderator(cls, bot: Red, guild_id: int, moderator: int, types: list | None = None) -> List["Moderation"]: + query = f"SELECT * FROM moderation_{guild_id} WHERE moderator_id = ?" + if types: + query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})" + query += " ORDER BY moderation_id DESC;" + + return cls.execute(bot=bot, query=query, parameters=(moderator, *types) if types else (moderator,)) + + @classmethod + def get_all_cases(cls, bot: Red, guild_id: int, types: list | None = None) -> List["Moderation"]: + query = f"SELECT * FROM moderation_{guild_id}" + if types: + query += f" WHERE moderation_type IN ({', '.join(['?' for _ in types])})" + query += " ORDER BY moderation_id DESC;" + return cls.execute(bot=bot, query=query, parameters=tuple(iterable=types) if types else None) + @classmethod def log( cls, -- 2.45.2 From 4c284531736422f71afafb349fd5ed4da13497c1 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:49:08 -0400 Subject: [PATCH 137/146] fix(aurora): fixed a TypeError --- aurora/models/moderation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index db9e681..23b6a5d 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -208,7 +208,7 @@ class Moderation(AuroraGuildModel): parameters = () with connect() as database: cursor = database.cursor() - cursor.execute(query, parameters=parameters) + cursor.execute(sql=query, parameters=parameters) results = cursor.fetchall() cursor.close() -- 2.45.2 From 028e22ebec7ad44eee0e842ec66fe77548638892 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:50:17 -0400 Subject: [PATCH 138/146] fix(aurora): happy now? --- aurora/models/moderation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 23b6a5d..17d4e13 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -208,7 +208,7 @@ class Moderation(AuroraGuildModel): parameters = () with connect() as database: cursor = database.cursor() - cursor.execute(sql=query, parameters=parameters) + cursor.execute(query, parameters) results = cursor.fetchall() cursor.close() -- 2.45.2 From 99d95afe07d5c8f02fd2b20135ea3289f690c4f0 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:51:51 -0400 Subject: [PATCH 139/146] fix(aurora): fixed another TypeError --- aurora/models/moderation.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 17d4e13..17a48f5 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -202,7 +202,7 @@ class Moderation(AuroraGuildModel): return cls.from_dict(bot=bot, data=case) @classmethod - def execute(cls, bot: Red, query: str, parameters: tuple | None = None) -> List["Moderation"]: + def execute(cls, bot: Red, guild_id: int, query: str, parameters: tuple | None = None) -> List["Moderation"]: from ..utilities.database import connect if not parameters: parameters = () @@ -215,7 +215,7 @@ class Moderation(AuroraGuildModel): if results: cases = [] for result in results: - case = cls.from_result(bot=bot, result=result) + case = cls.from_result(bot=bot, result=result, guild_id=guild_id) if case.moderation_id != 0: cases.append(case) return cases @@ -228,7 +228,7 @@ class Moderation(AuroraGuildModel): @classmethod def find_by_id(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;" - case = cls.execute(bot=bot, query=query, parameters=(moderation_id,)) + case = cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(moderation_id,)) if case: return case[0] raise ValueError(f"Case {moderation_id} not found in moderation_{guild_id}!") @@ -240,7 +240,7 @@ class Moderation(AuroraGuildModel): query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})" query += " ORDER BY moderation_id DESC;" - return cls.execute(bot=bot, query=query, parameters=(target, *types) if types else (target,)) + return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(target, *types) if types else (target,)) @classmethod def find_by_moderator(cls, bot: Red, guild_id: int, moderator: int, types: list | None = None) -> List["Moderation"]: @@ -249,7 +249,7 @@ class Moderation(AuroraGuildModel): query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})" query += " ORDER BY moderation_id DESC;" - return cls.execute(bot=bot, query=query, parameters=(moderator, *types) if types else (moderator,)) + return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(moderator, *types) if types else (moderator,)) @classmethod def get_all_cases(cls, bot: Red, guild_id: int, types: list | None = None) -> List["Moderation"]: @@ -257,7 +257,7 @@ class Moderation(AuroraGuildModel): if types: query += f" WHERE moderation_type IN ({', '.join(['?' for _ in types])})" query += " ORDER BY moderation_id DESC;" - return cls.execute(bot=bot, query=query, parameters=tuple(iterable=types) if types else None) + return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=tuple(iterable=types) if types else None) @classmethod def log( -- 2.45.2 From 499cfbe8a9ddb3e2a4e9afcaa403a2f56dae478e Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:55:09 -0400 Subject: [PATCH 140/146] misc(aurora): use tuples instead of lists --- aurora/models/moderation.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 17a48f5..2e5fd5f 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -2,7 +2,7 @@ import json import sqlite3 from datetime import datetime, timedelta from time import time -from typing import Dict, Iterable, List, Optional, Union +from typing import Dict, Iterable, List, Optional, Tuple, Union import discord from discord import NotFound @@ -202,7 +202,7 @@ class Moderation(AuroraGuildModel): return cls.from_dict(bot=bot, data=case) @classmethod - def execute(cls, bot: Red, guild_id: int, query: str, parameters: tuple | None = None) -> List["Moderation"]: + def execute(cls, bot: Red, guild_id: int, query: str, parameters: tuple | None = None) -> Tuple["Moderation"]: from ..utilities.database import connect if not parameters: parameters = () @@ -218,8 +218,8 @@ class Moderation(AuroraGuildModel): case = cls.from_result(bot=bot, result=result, guild_id=guild_id) if case.moderation_id != 0: cases.append(case) - return cases - return [] + return tuple(iterable=cases) + return () @classmethod def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": @@ -234,7 +234,7 @@ class Moderation(AuroraGuildModel): raise ValueError(f"Case {moderation_id} not found in moderation_{guild_id}!") @classmethod - def find_by_target(cls, bot: Red, guild_id: int, target: int, types: list | None = None) -> List["Moderation"]: + def find_by_target(cls, bot: Red, guild_id: int, target: int, types: list | None = None) -> Tuple["Moderation"]: query = f"SELECT * FROM moderation_{guild_id} WHERE target_id = ?" if types: query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})" @@ -243,7 +243,7 @@ class Moderation(AuroraGuildModel): return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(target, *types) if types else (target,)) @classmethod - def find_by_moderator(cls, bot: Red, guild_id: int, moderator: int, types: list | None = None) -> List["Moderation"]: + def find_by_moderator(cls, bot: Red, guild_id: int, moderator: int, types: list | None = None) -> Tuple["Moderation"]: query = f"SELECT * FROM moderation_{guild_id} WHERE moderator_id = ?" if types: query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})" @@ -252,7 +252,7 @@ class Moderation(AuroraGuildModel): return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(moderator, *types) if types else (moderator,)) @classmethod - def get_all_cases(cls, bot: Red, guild_id: int, types: list | None = None) -> List["Moderation"]: + def get_all_cases(cls, bot: Red, guild_id: int, types: list | None = None) -> Tuple["Moderation"]: query = f"SELECT * FROM moderation_{guild_id}" if types: query += f" WHERE moderation_type IN ({', '.join(['?' for _ in types])})" -- 2.45.2 From 22f9ce52d1f3648c72efc41139b91ed53d8daaef Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 00:55:56 -0400 Subject: [PATCH 141/146] fix(aurora): fixed a typeerror --- aurora/models/moderation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 2e5fd5f..e3a6e3f 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -218,7 +218,7 @@ class Moderation(AuroraGuildModel): case = cls.from_result(bot=bot, result=result, guild_id=guild_id) if case.moderation_id != 0: cases.append(case) - return tuple(iterable=cases) + return tuple(cases) return () @classmethod -- 2.45.2 From be253b668b4c3cfe3faa3054a3589a876988478f Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 01:07:00 -0400 Subject: [PATCH 142/146] feat(aurora): converted `/history` to use the new Moderation sql queries --- aurora/aurora.py | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 4497d40..8e422e0 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -11,7 +11,6 @@ import sqlite3 import time from datetime import datetime, timedelta, timezone from math import ceil -from typing import List import discord from discord import Object @@ -1114,35 +1113,14 @@ class Aurora(commands.Cog): database.close() return - cursor = database.cursor() - if target: - query = f"""SELECT * - FROM moderation_{interaction.guild.id} - WHERE target_id = ? AND moderation_id != 0 - ORDER BY moderation_id DESC;""" - cursor.execute(query, (target.id,)) + moderations = Moderation.find_by_target(interaction.client, interaction.guild.id, target.id) elif moderator: - query = f"""SELECT * - FROM moderation_{interaction.guild.id} - WHERE moderator_id = ? AND moderation_id != 0 - ORDER BY moderation_id DESC;""" - cursor.execute(query, (moderator.id,)) + moderations = Moderation.find_by_moderator(interaction.client, interaction.guild.id, moderator.id) else: - query = f"""SELECT * - FROM moderation_{interaction.guild.id} - WHERE moderation_id != 0 - ORDER BY moderation_id DESC;""" - cursor.execute(query) + moderations = Moderation.get_all_cases(interaction.client, interaction.guild.id) - results = cursor.fetchall() - moderation_list: List[Moderation] = [] - - for result in results: - moderation = Moderation.from_result(interaction.client, result, interaction.guild.id) - moderation_list.append(moderation) - - case_quantity = len(moderation_list) + case_quantity = len(moderations) page_quantity = ceil(case_quantity / pagesize) start_index = (page - 1) * pagesize end_index = page * pagesize @@ -1155,7 +1133,7 @@ class Aurora(commands.Cog): memory_dict = {} - for mod in moderation_list[start_index:end_index]: + for mod in moderations[start_index:end_index]: if mod.target_id not in memory_dict: memory_dict.update({ str(mod.target_id): await mod.get_target() -- 2.45.2 From bbe8b281d1c3650fbe91b406c42e49eb2a58a98a Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 23:46:22 -0400 Subject: [PATCH 143/146] misc(aurora): changed two typehints in aurora.utilities.utils and added other typehints --- aurora/aurora.py | 2 +- aurora/models/moderation.py | 6 +++--- aurora/utilities/utils.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 8e422e0..1d4052b 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -1191,7 +1191,7 @@ class Aurora(commands.Cog): Reason for resolving case""" permissions = check_permissions( interaction.client.user, - ["embed_links", "moderate_members", "ban_members"], + ("embed_links", "moderate_members", "ban_members"), interaction, ) if permissions: diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index e3a6e3f..478768f 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -63,10 +63,10 @@ class Moderation(AuroraGuildModel): return await PartialRole.from_id(self.bot, self.guild_id, self.role_id) return None - def __str__(self): + def __str__(self) -> str: return f"{self.moderation_type} {self.target_type} {self.target_id} {self.reason}" - async def resolve(self, resolved_by: int, reason: str): + async def resolve(self, resolved_by: int, reason: str) -> None: if self.resolved: raise ValueError("Case is already resolved!") @@ -113,7 +113,7 @@ class Moderation(AuroraGuildModel): self.update() - def update(self): + def update(self) -> None: from ..utilities.database import connect from ..utilities.json import dumps query = f"UPDATE moderation_{self.guild_id} SET timestamp = ?, moderation_type = ?, target_type = ?, moderator_id = ?, role_id = ?, duration = ?, end_timestamp = ?, reason = ?, resolved = ?, resolved_by = ?, resolve_reason = ?, expired = ?, changes = ?, metadata = ? WHERE moderation_id = ?;" diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 42ce1cb..b70ec83 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -1,6 +1,6 @@ # pylint: disable=cyclic-import from datetime import datetime, timedelta -from typing import Optional, Union +from typing import Optional, Tuple, Union from dateutil.relativedelta import relativedelta as rd from discord import File, Guild, Interaction, Member, SelectOption, TextChannel, User @@ -13,7 +13,7 @@ from ..utilities.config import config def check_permissions( user: User, - permissions: list, + permissions: Tuple[str], ctx: Union[commands.Context, Interaction] | None = None, guild: Guild | None = None, ) -> Union[bool, str]: @@ -40,7 +40,7 @@ def check_permissions( async def check_moddable( - target: Union[User, Member, TextChannel], interaction: Interaction, permissions: list + target: Union[User, Member, TextChannel], interaction: Interaction, permissions: Tuple[str] ) -> bool: """Checks if a moderator can moderate a target.""" is_channel = isinstance(target, TextChannel) -- 2.45.2 From 460d5a31fcc6c6c3cd4a96dcf31283e1d1075912 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Mon, 3 Jun 2024 23:48:21 -0400 Subject: [PATCH 144/146] feat(aurora): allow the `Moderation.execute()` classmethod to accept a cursor, to prevent opening a new database connection on every call --- aurora/models/moderation.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 478768f..2874344 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -202,23 +202,30 @@ class Moderation(AuroraGuildModel): return cls.from_dict(bot=bot, data=case) @classmethod - def execute(cls, bot: Red, guild_id: int, query: str, parameters: tuple | None = None) -> Tuple["Moderation"]: + def execute(cls, bot: Red, guild_id: int, query: str, parameters: tuple | None = None, cursor: Cursor | None = None) -> Tuple["Moderation"]: from ..utilities.database import connect if not parameters: parameters = () - with connect() as database: + if not cursor: + no_cursor = True + database = connect() cursor = database.cursor() - cursor.execute(query, parameters) - results = cursor.fetchall() - cursor.close() + else: + no_cursor = False - if results: - cases = [] - for result in results: - case = cls.from_result(bot=bot, result=result, guild_id=guild_id) - if case.moderation_id != 0: - cases.append(case) - return tuple(cases) + cursor.execute(query, parameters) + results = cursor.fetchall() + if no_cursor: + cursor.close() + database.close() + + if results: + cases = [] + for result in results: + case = cls.from_result(bot=bot, result=result, guild_id=guild_id) + if case.moderation_id != 0: + cases.append(case) + return tuple(cases) return () @classmethod -- 2.45.2 From 9b0f977016d15d04091197f06f40185634546d0b Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Tue, 4 Jun 2024 00:04:11 -0400 Subject: [PATCH 145/146] feat(aurora): moved `get_next_case_number()` into the `Moderation` class, along with removing `from_sql` and `get_all_cases`. also added some other classmethods to replace those. also modified message_factory and its calls to add a new kwarg --- aurora/aurora.py | 45 +++++++++++++++++++++++-------------- aurora/models/moderation.py | 44 ++++++++++++++++++++---------------- aurora/utilities/factory.py | 7 ++++-- aurora/utilities/utils.py | 18 ++------------- 4 files changed, 60 insertions(+), 54 deletions(-) diff --git a/aurora/aurora.py b/aurora/aurora.py index 1d4052b..1efb03c 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -221,7 +221,8 @@ class Aurora(commands.Cog): if silent is False: try: embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -279,7 +280,8 @@ class Aurora(commands.Cog): if silent is False: try: embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -374,7 +376,8 @@ class Aurora(commands.Cog): if silent is False: try: embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -479,7 +482,8 @@ class Aurora(commands.Cog): if silent is False: try: embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -575,7 +579,8 @@ class Aurora(commands.Cog): if silent is False: try: embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -650,7 +655,8 @@ class Aurora(commands.Cog): if silent is False: try: embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -708,7 +714,8 @@ class Aurora(commands.Cog): if silent is False: try: embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -810,7 +817,8 @@ class Aurora(commands.Cog): try: embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -853,8 +861,9 @@ class Aurora(commands.Cog): silent = not await config.guild(interaction.guild).dm_users() if silent is False: try: - embed = embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + embed = await message_factory( + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -936,7 +945,8 @@ class Aurora(commands.Cog): if silent is False: try: embed = await message_factory( - await self.bot.get_embed_color(interaction.channel), + bot=interaction.client, + color=await self.bot.get_embed_color(interaction.channel), guild=interaction.guild, moderator=interaction.user, reason=reason, @@ -1089,7 +1099,7 @@ class Aurora(commands.Cog): + f"moderation_{interaction.guild.id}.json" ) - cases = Moderation.get_all_cases(bot=interaction.client, guild_id=interaction.guild.id) + cases = Moderation.get_latest(bot=interaction.client, guild_id=interaction.guild.id) with open(filename, "w", encoding="utf-8") as f: dump(obj=cases, fp=f, indent=2) @@ -1118,7 +1128,7 @@ class Aurora(commands.Cog): elif moderator: moderations = Moderation.find_by_moderator(interaction.client, interaction.guild.id, moderator.id) else: - moderations = Moderation.get_all_cases(interaction.client, interaction.guild.id) + moderations = Moderation.get_latest(interaction.client, interaction.guild.id) case_quantity = len(moderations) page_quantity = ceil(case_quantity / pagesize) @@ -1204,7 +1214,7 @@ class Aurora(commands.Cog): return try: - moderation = Moderation.from_sql(interaction.client, case, interaction.guild.id) + moderation = Moderation.find_by_id(interaction.client, case, interaction.guild.id) except ValueError: await interaction.response.send_message( content=error(f"Case #{case:,} does not exist!"), ephemeral=True @@ -1288,7 +1298,7 @@ class Aurora(commands.Cog): ) try: - mod = Moderation.from_sql(interaction.client, case, interaction.guild.id) + mod = Moderation.find_by_id(interaction.client, case, interaction.guild.id) except ValueError: await interaction.response.send_message( content=error(f"Case #{case:,} does not exist!"), ephemeral=True @@ -1381,7 +1391,7 @@ class Aurora(commands.Cog): return try: - moderation = Moderation.from_sql(interaction.client, case, interaction.guild.id) + moderation = Moderation.find_by_id(interaction.client, case, interaction.guild.id) old_moderation = moderation except ValueError: await interaction.response.send_message( @@ -1511,7 +1521,8 @@ class Aurora(commands.Cog): ) embed = await message_factory( - await self.bot.get_embed_color(guild.channels[0]), + bot=self.bot, + color=await self.bot.get_embed_color(guild.channels[0]), guild=guild, reason=f"Automatic unban from case #{moderation_id}", moderation_type="unbanned", diff --git a/aurora/models/moderation.py b/aurora/models/moderation.py index 2874344..5462ba3 100644 --- a/aurora/models/moderation.py +++ b/aurora/models/moderation.py @@ -1,6 +1,7 @@ import json import sqlite3 from datetime import datetime, timedelta +from sqlite3 import Cursor from time import time from typing import Dict, Iterable, List, Optional, Tuple, Union @@ -9,7 +10,6 @@ from discord import NotFound from redbot.core.bot import Red from ..utilities.logger import logger -from ..utilities.utils import get_next_case_number from .base import AuroraGuildModel from .change import Change from .partials import PartialChannel, PartialRole, PartialUser @@ -229,42 +229,48 @@ class Moderation(AuroraGuildModel): return () @classmethod - def from_sql(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": - return cls.find_by_id(bot=bot, moderation_id=moderation_id, guild_id=guild_id) + def get_latest(cls, bot: Red, guild_id: int, limit: int | None = None, types: Iterable | None = None, cursor: Cursor | None = None) -> Tuple["Moderation"]: + params = [] + query = f"SELECT * FROM moderation_{guild_id} ORDER BY moderation_id DESC" + if limit: + query += " LIMIT ?" + params.append(limit) + if types: + query += f" WHERE moderation_type IN ({', '.join(['?' for _ in types])})" + params.extend(types) + query += ";" + return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=tuple(params) if limit else (), cursor=cursor) @classmethod - def find_by_id(cls, bot: Red, moderation_id: int, guild_id: int) -> "Moderation": + def get_next_case_number(cls, bot: Red, guild_id: int, cursor: Cursor | None = None) -> int: + result = cls.get_latest(bot=bot, guild_id=guild_id, cursor=cursor) + return (result[0].moderation_id + 1) if result else 1 + + @classmethod + def find_by_id(cls, bot: Red, moderation_id: int, guild_id: int, cursor: Cursor | None = None) -> "Moderation": query = f"SELECT * FROM moderation_{guild_id} WHERE moderation_id = ?;" - case = cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(moderation_id,)) + case = cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(moderation_id,), cursor=cursor) if case: return case[0] raise ValueError(f"Case {moderation_id} not found in moderation_{guild_id}!") @classmethod - def find_by_target(cls, bot: Red, guild_id: int, target: int, types: list | None = None) -> Tuple["Moderation"]: + def find_by_target(cls, bot: Red, guild_id: int, target: int, types: Iterable | None = None, cursor: Cursor | None = None) -> Tuple["Moderation"]: query = f"SELECT * FROM moderation_{guild_id} WHERE target_id = ?" if types: query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})" query += " ORDER BY moderation_id DESC;" - return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(target, *types) if types else (target,)) + return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(target, *types) if types else (target,), cursor=cursor) @classmethod - def find_by_moderator(cls, bot: Red, guild_id: int, moderator: int, types: list | None = None) -> Tuple["Moderation"]: + def find_by_moderator(cls, bot: Red, guild_id: int, moderator: int, types: Iterable | None = None, cursor: Cursor | None = None) -> Tuple["Moderation"]: query = f"SELECT * FROM moderation_{guild_id} WHERE moderator_id = ?" if types: query += f" AND moderation_type IN ({', '.join(['?' for _ in types])})" query += " ORDER BY moderation_id DESC;" - return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(moderator, *types) if types else (moderator,)) - - @classmethod - def get_all_cases(cls, bot: Red, guild_id: int, types: list | None = None) -> Tuple["Moderation"]: - query = f"SELECT * FROM moderation_{guild_id}" - if types: - query += f" WHERE moderation_type IN ({', '.join(['?' for _ in types])})" - query += " ORDER BY moderation_id DESC;" - return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=tuple(iterable=types) if types else None) + return cls.execute(bot=bot, guild_id=guild_id, query=query, parameters=(moderator, *types) if types else (moderator,), cursor=cursor) @classmethod def log( @@ -328,7 +334,7 @@ class Moderation(AuroraGuildModel): close_db = False cursor = database.cursor() - moderation_id = get_next_case_number(guild_id=guild_id, cursor=cursor) + moderation_id = cls.get_next_case_number(bot=bot, guild_id=guild_id, cursor=cursor) case = { "moderation_id": moderation_id, @@ -378,4 +384,4 @@ class Moderation(AuroraGuildModel): case["metadata"], ) - return cls.from_sql(bot=bot, moderation_id=moderation_id, guild_id=guild_id) + return cls.find_by_id(bot=bot, moderation_id=moderation_id, guild_id=guild_id) diff --git a/aurora/utilities/factory.py b/aurora/utilities/factory.py index 0485af7..c30ef1b 100644 --- a/aurora/utilities/factory.py +++ b/aurora/utilities/factory.py @@ -4,15 +4,17 @@ from typing import Union from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User from redbot.core import commands +from redbot.core.bot import Red from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning from ..models.moderation import Moderation from ..models.partials import PartialUser from .config import config -from .utils import get_bool_emoji, get_next_case_number, get_pagesize_str +from .utils import get_bool_emoji, get_pagesize_str async def message_factory( + bot: Red, color: Color, guild: Guild, reason: str, @@ -25,6 +27,7 @@ async def message_factory( """This function creates a message from set parameters, meant for contacting the moderated user. Args: + bot (Red): The bot instance. color (Color): The color of the embed. guild (Guild): The guild the moderation occurred in. reason (str): The reason for the moderation. @@ -88,7 +91,7 @@ async def message_factory( embed.set_author(name=guild.name) embed.set_footer( - text=f"Case #{get_next_case_number(guild.id):,}", + text=f"Case #{Moderation.get_next_case_number(bot=bot, guild_id=guild.id):,}", icon_url="attachment://arrow.png", ) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index b70ec83..96eea2b 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -110,20 +110,6 @@ async def check_moddable( return True -def get_next_case_number(guild_id: str, cursor=None) -> int: - """This function returns the next case number from the MySQL table for a specific guild.""" - from .database import connect - - if not cursor: - database = connect() - cursor = database.cursor() - cursor.execute( - f"SELECT moderation_id FROM `moderation_{guild_id}` ORDER BY moderation_id DESC LIMIT 1" - ) - result = cursor.fetchone() - return (result[0] + 1) if result else 1 - - async def log(interaction: Interaction, moderation_id: int, resolved: bool = False) -> None: """This function sends a message to the guild's configured logging channel when an infraction takes place.""" from ..models.moderation import Moderation @@ -134,7 +120,7 @@ async def log(interaction: Interaction, moderation_id: int, resolved: bool = Fal logging_channel = interaction.guild.get_channel(logging_channel_id) try: - moderation = Moderation.from_sql(interaction.client, moderation_id, interaction.guild_id) + moderation = Moderation.find_by_id(interaction.client, moderation_id, interaction.guild_id) embed = await log_factory( interaction=interaction, moderation=moderation, resolved=resolved ) @@ -159,7 +145,7 @@ async def send_evidenceformat(interaction: Interaction, moderation_id: int) -> N if send_evidence_bool is False: return - moderation = Moderation.from_sql(interaction.client, moderation_id, interaction.guild.id) + moderation = Moderation.find_by_id(interaction.client, moderation_id, interaction.guild.id) content = await evidenceformat_factory(moderation=moderation) await interaction.followup.send(content=content, ephemeral=True) -- 2.45.2 From 1c6d2456ed844c1c48e3d910649f09486e0089b7 Mon Sep 17 00:00:00 2001 From: Seaswimmer Date: Tue, 4 Jun 2024 00:04:46 -0400 Subject: [PATCH 146/146] misc(aurora): changed `aurora.utilities.utils.get_bool_emoji` to use `match/case` instead of `if/else` --- aurora/utilities/utils.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/aurora/utilities/utils.py b/aurora/utilities/utils.py index 96eea2b..f2adae0 100644 --- a/aurora/utilities/utils.py +++ b/aurora/utilities/utils.py @@ -152,11 +152,13 @@ async def send_evidenceformat(interaction: Interaction, moderation_id: int) -> N def get_bool_emoji(value: Optional[bool]) -> str: """Returns a unicode emoji based on a boolean value.""" - if value is True: - return "\N{WHITE HEAVY CHECK MARK}" - if value is False: - return "\N{NO ENTRY SIGN}" - return "\N{BLACK QUESTION MARK ORNAMENT}\N{VARIATION SELECTOR-16}" + match value: + case True: + return "\N{WHITE HEAVY CHECK MARK}" + case False: + return "\N{NO ENTRY SIGN}" + case _: + return "\N{BLACK QUESTION MARK ORNAMENT}\N{VARIATION SELECTOR-16}" def get_pagesize_str(value: Union[int, None]) -> str: -- 2.45.2