From d29af1ad6cf9f0abf76cb9a979261d5a49c1e03c Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sun, 22 Aug 2021 09:29:05 +0530 Subject: [PATCH 01/23] feat(db): Add database URL configuration This commit also cleans up redundant imports in `config.py` and fixes some type hints. --- modmail/config-default.toml | 1 + modmail/config.py | 20 +++++++------------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/modmail/config-default.toml b/modmail/config-default.toml index db98e228..bd19b018 100644 --- a/modmail/config-default.toml +++ b/modmail/config-default.toml @@ -1,5 +1,6 @@ [bot] prefix = "?" +sqlalchemy_database_uri = "postgres://jasonthedestroyer:ihavemnesia33@localhost/modmail" [dev] log_level = 25 # "NOTICE" diff --git a/modmail/config.py b/modmail/config.py index 7345d25b..7c0ce06e 100644 --- a/modmail/config.py +++ b/modmail/config.py @@ -1,19 +1,12 @@ -import asyncio -import datetime -import json import logging import os -import sys import typing from pathlib import Path from typing import Any, Dict, Optional, Tuple -import discord import toml -from discord.ext.commands import BadArgument -from pydantic import BaseModel from pydantic import BaseSettings as PydanticBaseSettings -from pydantic import Field, SecretStr +from pydantic import PostgresDsn from pydantic.env_settings import SettingsSourceCallable from pydantic.types import conint @@ -35,7 +28,7 @@ def determine_file_path( for file_path in paths: config_file = Path(file_path) if (config_file).exists(): - path = config_file + path = str(config_file) log.debug(f"Found {config_type} config at {file_path}") break return path or None @@ -45,7 +38,7 @@ def determine_file_path( USER_CONFIG_PATH = determine_file_path(CONFIG_PATHS, config_type="") -def toml_default_config_source(settings: PydanticBaseSettings) -> Dict[str, Any]: +def toml_default_config_source(_: PydanticBaseSettings) -> Dict[str, Any]: """ A simple settings source that loads variables from a toml file from within the module's source folder. @@ -56,7 +49,7 @@ def toml_default_config_source(settings: PydanticBaseSettings) -> Dict[str, Any] return dict(**toml.load(DEFAULT_CONFIG_PATH)) -def toml_user_config_source(settings: PydanticBaseSettings) -> Dict[str, Any]: +def toml_user_config_source(_: PydanticBaseSettings) -> Dict[str, Any]: """ A simple settings source that loads variables from a toml file from within the module's source folder. @@ -93,8 +86,9 @@ def customise_sources( class BotConfig(BaseSettings): - prefix: str = "?" - token: str = None + prefix: Optional[str] = "?" + token: str + sqlalchemy_database_uri: Optional[PostgresDsn] = None class Config: # env_prefix = "bot." From f08a1186cf2f3f8f2fe973470902461156722bbe Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 16:45:08 +0530 Subject: [PATCH 02/23] deps: Add SQLAlchemy for db-client, limited to v1.4.23 --- poetry.lock | 157 +++++++++++++++++++++++++++++++++++++++++-------- pyproject.toml | 1 + 2 files changed, 135 insertions(+), 23 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9b45d696..99bee637 100644 --- a/poetry.lock +++ b/poetry.lock @@ -252,7 +252,7 @@ toml = ["toml"] [[package]] name = "discord.py" -version = "2.0.0a3470+gfeae059c" +version = "2.0.0a3559+gefec816d" description = "A Python wrapper for the Discord API" category = "main" optional = false @@ -271,7 +271,7 @@ voice = ["PyNaCl (>=1.3.0,<1.5)"] type = "git" url = "https://github.com/Rapptz/discord.py.git" reference = "master" -resolved_reference = "feae059c6858e419552ec4096f1ad2692bb4c484" +resolved_reference = "efec816de2a9783d98f07aaf02ad7d32cba2b7b9" [[package]] name = "distlib" @@ -462,6 +462,17 @@ python-versions = ">=3.6" gitdb = ">=4.0.1,<5" typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} +[[package]] +name = "greenlet" +version = "1.1.1" +description = "Lightweight in-process concurrent programming" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" + +[package.extras] +docs = ["sphinx"] + [[package]] name = "humanfriendly" version = "9.2" @@ -1066,6 +1077,37 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "sqlalchemy" +version = "1.4.22" +description = "Database Abstraction Library" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\""} + +[package.extras] +aiomysql = ["greenlet (!=0.4.17)", "aiomysql"] +aiosqlite = ["greenlet (!=0.4.17)", "aiosqlite"] +asyncio = ["greenlet (!=0.4.17)"] +mariadb_connector = ["mariadb (>=1.0.1)"] +mssql = ["pyodbc"] +mssql_pymssql = ["pymssql"] +mssql_pyodbc = ["pyodbc"] +mypy = ["sqlalchemy2-stubs", "mypy (>=0.800)"] +mysql = ["mysqlclient (>=1.4.0,<2)", "mysqlclient (>=1.4.0)"] +mysql_connector = ["mysqlconnector"] +oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"] +postgresql_pg8000 = ["pg8000 (>=1.16.6)"] +postgresql_psycopg2binary = ["psycopg2-binary"] +postgresql_psycopg2cffi = ["psycopg2cffi"] +pymysql = ["pymysql (<1)", "pymysql"] +sqlcipher = ["sqlcipher3-binary"] + [[package]] name = "stevedore" version = "3.3.0" @@ -1198,7 +1240,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "b2fe2e490de66438a4a582dbe69e4b9f1417af90f47d928dc59ef6686a22c01a" +content-hash = "c86844053fa186b247e6c05c8a663621e84a7012ce792ec30ae75cb6adbcda74" [metadata.files] aiodns = [ @@ -1358,6 +1400,11 @@ cffi = [ {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"}, {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"}, {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"}, + {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"}, + {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"}, + {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"}, {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"}, {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"}, {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"}, @@ -1537,6 +1584,58 @@ gitpython = [ {file = "GitPython-3.1.20-py3-none-any.whl", hash = "sha256:b1e1c269deab1b08ce65403cf14e10d2ef1f6c89e33ea7c5e5bb0222ea593b8a"}, {file = "GitPython-3.1.20.tar.gz", hash = "sha256:df0e072a200703a65387b0cfdf0466e3bab729c0458cf6b7349d0e9877636519"}, ] +greenlet = [ + {file = "greenlet-1.1.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:476ba9435afaead4382fbab8f1882f75e3fb2285c35c9285abb3dd30237f9142"}, + {file = "greenlet-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:44556302c0ab376e37939fd0058e1f0db2e769580d340fb03b01678d1ff25f68"}, + {file = "greenlet-1.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40abb7fec4f6294225d2b5464bb6d9552050ded14a7516588d6f010e7e366dcc"}, + {file = "greenlet-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:a11b6199a0b9dc868990456a2667167d0ba096c5224f6258e452bfbe5a9742c5"}, + {file = "greenlet-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e22a82d2b416d9227a500c6860cf13e74060cf10e7daf6695cbf4e6a94e0eee4"}, + {file = "greenlet-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bad269e442f1b7ffa3fa8820b3c3aa66f02a9f9455b5ba2db5a6f9eea96f56de"}, + {file = "greenlet-1.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8ddb38fb6ad96c2ef7468ff73ba5c6876b63b664eebb2c919c224261ae5e8378"}, + {file = "greenlet-1.1.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:84782c80a433d87530ae3f4b9ed58d4a57317d9918dfcc6a59115fa2d8731f2c"}, + {file = "greenlet-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac991947ca6533ada4ce7095f0e28fe25d5b2f3266ad5b983ed4201e61596acf"}, + {file = "greenlet-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5317701c7ce167205c0569c10abc4bd01c7f4cf93f642c39f2ce975fa9b78a3c"}, + {file = "greenlet-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4870b018ca685ff573edd56b93f00a122f279640732bb52ce3a62b73ee5c4a92"}, + {file = "greenlet-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:990e0f5e64bcbc6bdbd03774ecb72496224d13b664aa03afd1f9b171a3269272"}, + {file = "greenlet-1.1.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:a414f8e14aa7bacfe1578f17c11d977e637d25383b6210587c29210af995ef04"}, + {file = "greenlet-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:e02780da03f84a671bb4205c5968c120f18df081236d7b5462b380fd4f0b497b"}, + {file = "greenlet-1.1.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:dfcb5a4056e161307d103bc013478892cfd919f1262c2bb8703220adcb986362"}, + {file = "greenlet-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:655ab836324a473d4cd8cf231a2d6f283ed71ed77037679da554e38e606a7117"}, + {file = "greenlet-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:6ce9d0784c3c79f3e5c5c9c9517bbb6c7e8aa12372a5ea95197b8a99402aa0e6"}, + {file = "greenlet-1.1.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3fc6a447735749d651d8919da49aab03c434a300e9f0af1c886d560405840fd1"}, + {file = "greenlet-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8039f5fe8030c43cd1732d9a234fdcbf4916fcc32e21745ca62e75023e4d4649"}, + {file = "greenlet-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fddfb31aa2ac550b938d952bca8a87f1db0f8dc930ffa14ce05b5c08d27e7fd1"}, + {file = "greenlet-1.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97a807437b81f90f85022a9dcfd527deea38368a3979ccb49d93c9198b2c722"}, + {file = "greenlet-1.1.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf31e894dabb077a35bbe6963285d4515a387ff657bd25b0530c7168e48f167f"}, + {file = "greenlet-1.1.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eae94de9924bbb4d24960185363e614b1b62ff797c23dc3c8a7c75bbb8d187e"}, + {file = "greenlet-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:c1862f9f1031b1dee3ff00f1027fcd098ffc82120f43041fe67804b464bbd8a7"}, + {file = "greenlet-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:9b02e6039eafd75e029d8c58b7b1f3e450ca563ef1fe21c7e3e40b9936c8d03e"}, + {file = "greenlet-1.1.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:84488516639c3c5e5c0e52f311fff94ebc45b56788c2a3bfe9cf8e75670f4de3"}, + {file = "greenlet-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3f8fc59bc5d64fa41f58b0029794f474223693fd00016b29f4e176b3ee2cfd9f"}, + {file = "greenlet-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3e594015a2349ec6dcceda9aca29da8dc89e85b56825b7d1f138a3f6bb79dd4c"}, + {file = "greenlet-1.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e41f72f225192d5d4df81dad2974a8943b0f2d664a2a5cfccdf5a01506f5523c"}, + {file = "greenlet-1.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ff270fd05125dce3303e9216ccddc541a9e072d4fc764a9276d44dee87242b"}, + {file = "greenlet-1.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cde7ee190196cbdc078511f4df0be367af85636b84d8be32230f4871b960687"}, + {file = "greenlet-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:f253dad38605486a4590f9368ecbace95865fea0f2b66615d121ac91fd1a1563"}, + {file = "greenlet-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a91ee268f059583176c2c8b012a9fce7e49ca6b333a12bbc2dd01fc1a9783885"}, + {file = "greenlet-1.1.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:34e6675167a238bede724ee60fe0550709e95adaff6a36bcc97006c365290384"}, + {file = "greenlet-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bf3725d79b1ceb19e83fb1aed44095518c0fcff88fba06a76c0891cfd1f36837"}, + {file = "greenlet-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:5c3b735ccf8fc8048664ee415f8af5a3a018cc92010a0d7195395059b4b39b7d"}, + {file = "greenlet-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2002a59453858c7f3404690ae80f10c924a39f45f6095f18a985a1234c37334"}, + {file = "greenlet-1.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04e1849c88aa56584d4a0a6e36af5ec7cc37993fdc1fda72b56aa1394a92ded3"}, + {file = "greenlet-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8d4ed48eed7414ccb2aaaecbc733ed2a84c299714eae3f0f48db085342d5629"}, + {file = "greenlet-1.1.1-cp38-cp38-win32.whl", hash = "sha256:2f89d74b4f423e756a018832cd7a0a571e0a31b9ca59323b77ce5f15a437629b"}, + {file = "greenlet-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:d15cb6f8706678dc47fb4e4f8b339937b04eda48a0af1cca95f180db552e7663"}, + {file = "greenlet-1.1.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b050dbb96216db273b56f0e5960959c2b4cb679fe1e58a0c3906fa0a60c00662"}, + {file = "greenlet-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e0696525500bc8aa12eae654095d2260db4dc95d5c35af2b486eae1bf914ccd"}, + {file = "greenlet-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:07e6d88242e09b399682b39f8dfa1e7e6eca66b305de1ff74ed9eb1a7d8e539c"}, + {file = "greenlet-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98b491976ed656be9445b79bc57ed21decf08a01aaaf5fdabf07c98c108111f6"}, + {file = "greenlet-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e72db813c28906cdc59bd0da7c325d9b82aa0b0543014059c34c8c4ad20e16"}, + {file = "greenlet-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:090126004c8ab9cd0787e2acf63d79e80ab41a18f57d6448225bbfcba475034f"}, + {file = "greenlet-1.1.1-cp39-cp39-win32.whl", hash = "sha256:1796f2c283faab2b71c67e9b9aefb3f201fdfbee5cb55001f5ffce9125f63a45"}, + {file = "greenlet-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:4adaf53ace289ced90797d92d767d37e7cdc29f13bd3830c3f0a561277a4ae83"}, + {file = "greenlet-1.1.1.tar.gz", hash = "sha256:c0f22774cd8294078bdf7392ac73cf00bfa1e5e0ed644bd064fdabc5f2a2f481"}, +] humanfriendly = [ {file = "humanfriendly-9.2-py2.py3-none-any.whl", hash = "sha256:332da98c24cc150efcc91b5508b19115209272bfdf4b0764a56795932f854271"}, {file = "humanfriendly-9.2.tar.gz", hash = "sha256:f7dba53ac7935fd0b4a2fc9a29e316ddd9ea135fb3052d3d0279d10c18ff9c48"}, @@ -1570,22 +1669,12 @@ markdown = [ {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, ] markupsafe = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, @@ -1594,21 +1683,14 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, @@ -1618,9 +1700,6 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, @@ -1975,6 +2054,38 @@ snowballstemmer = [ {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] +sqlalchemy = [ + {file = "SQLAlchemy-1.4.22-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:488608953385d6c127d2dcbc4b11f8d7f2f30b89f6bd27c01b042253d985cc2f"}, + {file = "SQLAlchemy-1.4.22-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5d856cc50fd26fc8dd04892ed5a5a3d7eeb914fea2c2e484183e2d84c14926e0"}, + {file = "SQLAlchemy-1.4.22-cp27-cp27m-win32.whl", hash = "sha256:a00d9c6d3a8afe1d1681cd8a5266d2f0ed684b0b44bada2ca82403b9e8b25d39"}, + {file = "SQLAlchemy-1.4.22-cp27-cp27m-win_amd64.whl", hash = "sha256:5908ea6c652a050d768580d01219c98c071e71910ab8e7b42c02af4010608397"}, + {file = "SQLAlchemy-1.4.22-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b7fb937c720847879c7402fe300cfdb2aeff22349fa4ea3651bca4e2d6555939"}, + {file = "SQLAlchemy-1.4.22-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:9bfe882d5a1bbde0245dca0bd48da0976bd6634cf2041d2fdf0417c5463e40e5"}, + {file = "SQLAlchemy-1.4.22-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eedd76f135461cf237534a6dc0d1e0f6bb88a1dc193678fab48a11d223462da5"}, + {file = "SQLAlchemy-1.4.22-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6a16c7c4452293da5143afa3056680db2d187b380b3ef4d470d4e29885720de3"}, + {file = "SQLAlchemy-1.4.22-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d23ea797a5e0be71bc5454b9ae99158ea0edc79e2393c6e9a2354de88329c0"}, + {file = "SQLAlchemy-1.4.22-cp36-cp36m-win32.whl", hash = "sha256:a5e14cb0c0a4ac095395f24575a0e7ab5d1be27f5f9347f1762f21505e3ba9f1"}, + {file = "SQLAlchemy-1.4.22-cp36-cp36m-win_amd64.whl", hash = "sha256:bc34a007e604091ca3a4a057525efc4cefd2b7fe970f44d20b9cfa109ab1bddb"}, + {file = "SQLAlchemy-1.4.22-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:756f5d2f5b92d27450167247fb574b09c4cd192a3f8c2e493b3e518a204ee543"}, + {file = "SQLAlchemy-1.4.22-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fcbb4b4756b250ed19adc5e28c005b8ed56fdb5c21efa24c6822c0575b4964d"}, + {file = "SQLAlchemy-1.4.22-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:09dbb4bc01a734ccddbf188deb2a69aede4b3c153a72b6d5c6900be7fb2945b1"}, + {file = "SQLAlchemy-1.4.22-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f028ef6a1d828bc754852a022b2160e036202ac8658a6c7d34875aafd14a9a15"}, + {file = "SQLAlchemy-1.4.22-cp37-cp37m-win32.whl", hash = "sha256:68393d3fd31469845b6ba11f5b4209edbea0b58506be0e077aafbf9aa2e21e11"}, + {file = "SQLAlchemy-1.4.22-cp37-cp37m-win_amd64.whl", hash = "sha256:891927a49b2363a4199763a9d436d97b0b42c65922a4ea09025600b81a00d17e"}, + {file = "SQLAlchemy-1.4.22-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:fd2102a8f8a659522719ed73865dff3d3cc76eb0833039dc473e0ad3041d04be"}, + {file = "SQLAlchemy-1.4.22-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4014978de28163cd8027434916a92d0f5bb1a3a38dff5e8bf8bff4d9372a9117"}, + {file = "SQLAlchemy-1.4.22-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f814d80844969b0d22ea63663da4de5ca1c434cfbae226188901e5d368792c17"}, + {file = "SQLAlchemy-1.4.22-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d09a760b0a045b4d799102ae7965b5491ccf102123f14b2a8cc6c01d1021a2d9"}, + {file = "SQLAlchemy-1.4.22-cp38-cp38-win32.whl", hash = "sha256:26daa429f039e29b1e523bf763bfab17490556b974c77b5ca7acb545b9230e9a"}, + {file = "SQLAlchemy-1.4.22-cp38-cp38-win_amd64.whl", hash = "sha256:12bac5fa1a6ea870bdccb96fe01610641dd44ebe001ed91ef7fcd980e9702db5"}, + {file = "SQLAlchemy-1.4.22-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:39b5d36ab71f73c068cdcf70c38075511de73616e6c7fdd112d6268c2704d9f5"}, + {file = "SQLAlchemy-1.4.22-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5102b9face693e8b2db3b2539c7e1a5d9a5b4dc0d79967670626ffd2f710d6e6"}, + {file = "SQLAlchemy-1.4.22-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9373ef67a127799027091fa53449125351a8c943ddaa97bec4e99271dbb21f4"}, + {file = "SQLAlchemy-1.4.22-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36a089dc604032d41343d86290ce85d4e6886012eea73faa88001260abf5ff81"}, + {file = "SQLAlchemy-1.4.22-cp39-cp39-win32.whl", hash = "sha256:b48148ceedfb55f764562e04c00539bb9ea72bf07820ca15a594a9a049ff6b0e"}, + {file = "SQLAlchemy-1.4.22-cp39-cp39-win_amd64.whl", hash = "sha256:1fdae7d980a2fa617d119d0dc13ecb5c23cc63a8b04ffcb5298f2c59d86851e9"}, + {file = "SQLAlchemy-1.4.22.tar.gz", hash = "sha256:ec1be26cdccd60d180359a527d5980d959a26269a2c7b1b327a1eea0cab37ed8"}, +] stevedore = [ {file = "stevedore-3.3.0-py3-none-any.whl", hash = "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"}, {file = "stevedore-3.3.0.tar.gz", hash = "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee"}, diff --git a/pyproject.toml b/pyproject.toml index f37252c6..c2a3d14b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ toml = "^0.10.2" # HACK: Poetry uses requests to install git dependencies, which are not explicitly installed when `--no-dev` # is set in docker, and hence is needed to successfully install discord.py from git requests = ">=2.7.9" +SQLAlchemy = ">=1.4,<1.4.23" [tool.poetry.extras] From e4a37730c4bad877c60b939cab1d5c1e9606d4d8 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 17:22:17 +0530 Subject: [PATCH 03/23] deps: Add psycopg2-binary for SQLAlchemy --- poetry.lock | 41 ++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 99bee637..56511584 100644 --- a/poetry.lock +++ b/poetry.lock @@ -772,6 +772,14 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] +[[package]] +name = "psycopg2-binary" +version = "2.9.1" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "py" version = "1.10.0" @@ -1240,7 +1248,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "c86844053fa186b247e6c05c8a663621e84a7012ce792ec30ae75cb6adbcda74" +content-hash = "debe84c1d3caa4a7665f3bb9277b61dfc2ae6a01598e1c508be28dbddf8965bd" [metadata.files] aiodns = [ @@ -1837,6 +1845,37 @@ psutil = [ {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"}, {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"}, ] +psycopg2-binary = [ + {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"}, + {file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"}, + {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698"}, + {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616"}, + {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7"}, + {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e"}, + {file = "psycopg2_binary-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d"}, + {file = "psycopg2_binary-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce"}, + {file = "psycopg2_binary-2.9.1-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a"}, + {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a"}, + {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0"}, + {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed"}, + {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f"}, + {file = "psycopg2_binary-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e"}, + {file = "psycopg2_binary-2.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2"}, + {file = "psycopg2_binary-2.9.1-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140"}, + {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917"}, + {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90"}, + {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72"}, + {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34"}, + {file = "psycopg2_binary-2.9.1-cp38-cp38-win32.whl", hash = "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32"}, + {file = "psycopg2_binary-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf"}, + {file = "psycopg2_binary-2.9.1-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a"}, + {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4"}, + {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31"}, + {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd"}, + {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a"}, + {file = "psycopg2_binary-2.9.1-cp39-cp39-win32.whl", hash = "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975"}, + {file = "psycopg2_binary-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68"}, +] py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, diff --git a/pyproject.toml b/pyproject.toml index c2a3d14b..464eb96b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ toml = "^0.10.2" # is set in docker, and hence is needed to successfully install discord.py from git requests = ">=2.7.9" SQLAlchemy = ">=1.4,<1.4.23" +psycopg2-binary = "^2.9.1" [tool.poetry.extras] From 1253853db58ead7d9eb7ae6790f90ef7cfb4fbc4 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 17:24:59 +0530 Subject: [PATCH 04/23] feat(db): Add init_db function to initalize the DB This function is ran before the discord modmail bot is ran so that the bot is not ran unnecessarily even if the DB is not responding. It creates a local session which can be used throughout the bot and then tests if the db is responding. If the DB is not responding it would log the error with the exception and then exit, it doesn't raise the exception as the the sqlalchemy connection errors are usually really long and maybe "spammy", to reduce the logs down it just logs down the error and then exists silently. --- modmail/__main__.py | 4 ++++ modmail/bot.py | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modmail/__main__.py b/modmail/__main__.py index 39599e20..0e9ba22e 100644 --- a/modmail/__main__.py +++ b/modmail/__main__.py @@ -1,3 +1,4 @@ +import asyncio import logging from modmail.bot import ModmailBot @@ -18,6 +19,9 @@ def main() -> None: """Run the bot.""" bot = ModmailBot() + # Check if the database is alive before running the bot + loop = asyncio.get_event_loop() + loop.run_until_complete(bot.init_db()) bot.run(bot.config.bot.token) diff --git a/modmail/bot.py b/modmail/bot.py index 1ba67b52..f96d1fba 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -9,6 +9,8 @@ from discord import Activity, AllowedMentions, Intents from discord.client import _cleanup_loop from discord.ext import commands +from sqlalchemy import create_engine, engine +from sqlalchemy.orm import Session, sessionmaker from modmail.config import CONFIG from modmail.log import ModmailLogger @@ -38,6 +40,8 @@ def __init__(self, **kwargs): self.config = CONFIG self.start_time: t.Optional[arrow.Arrow] = None # arrow.utcnow() self.http_session: t.Optional[ClientSession] = None + self._db_engine: t.Optional[engine.Engine] = None + self.db: t.Optional[Session] = None status = discord.Status.online activity = Activity(type=discord.ActivityType.listening, name="users dming me!") @@ -47,6 +51,7 @@ def __init__(self, **kwargs): # allow only user mentions by default. # ! NOTE: This may change in the future to allow roles as well allowed_mentions = AllowedMentions(everyone=False, users=True, roles=False, replied_user=True) + super().__init__( case_insensitive=True, description="Modmail bot by discord-modmail.", @@ -58,12 +63,25 @@ def __init__(self, **kwargs): **kwargs, ) + async def init_db(self) -> None: + """Initiate the bot DB session and check if the DB is alive.""" + self._db_engine = create_engine(CONFIG.bot.sqlalchemy_database_uri, pool_pre_ping=True) + self.db = sessionmaker(autocommit=False, autoflush=False, bind=self._db_engine) + try: + self._db_engine.execute("SELECT 1") + self.logger.notice(f"DB Connection pool established at {self._db_engine.url}") + except Exception as e: + self.logger.error( + f"DB connection at {CONFIG.bot.sqlalchemy_database_uri} not successful, raised\n{e}" + ) + exit() + async def start(self, token: str, reconnect: bool = True) -> None: """ Start the bot. This function is called by the run method, and finishes the set up of the bot that needs an - asyncrhonous event loop running, before connecting the bot to discord. + asynchronous event loop running, before connecting the bot to discord. """ try: # create the aiohttp session From 1211746237474fdaf7a95aba40b60b75285f023d Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 17:54:42 +0530 Subject: [PATCH 05/23] feat(db): Add DB base class via as_declarative --- modmail/models/__init__.py | 1 + modmail/models/base.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 modmail/models/__init__.py create mode 100644 modmail/models/base.py diff --git a/modmail/models/__init__.py b/modmail/models/__init__.py new file mode 100644 index 00000000..555f32be --- /dev/null +++ b/modmail/models/__init__.py @@ -0,0 +1 @@ +from .ticket import Ticket # noqa: F401 diff --git a/modmail/models/base.py b/modmail/models/base.py new file mode 100644 index 00000000..dd00e200 --- /dev/null +++ b/modmail/models/base.py @@ -0,0 +1,32 @@ +from operator import itemgetter +from typing import Any + +from sqlalchemy.ext.declarative import as_declarative, declared_attr + + +@as_declarative() +class Base: + """ + Base model class. + + Provides functionality + - automatically create table name + - adds `__repr__()` to display model class name and initialisation parameters + """ + + id: Any + __name__: str + + @declared_attr + def __tablename__(cls) -> str: # noqa: N805 + """Generate __tablename__ automatically for a DB model.""" + return cls.__name__.lower() + + def __repr__(self) -> str: + """Returns the current model class name and initialisation parameters.""" + attributes = " ".join( + f"{attribute}={value!r}" + for attribute, value in sorted(self.__dict__.items(), key=itemgetter(0)) + if not attribute.startswith("_") + ) + return f"<{self.__class__.__name__}({attributes})>" From 73ec5d539e1893676adf3acf9ebe945cdcd7e86c Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 17:59:54 +0530 Subject: [PATCH 06/23] feat(db): Add discord modmail ticket model --- modmail/models/__init__.py | 2 +- modmail/models/tickets.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 modmail/models/tickets.py diff --git a/modmail/models/__init__.py b/modmail/models/__init__.py index 555f32be..a1014d08 100644 --- a/modmail/models/__init__.py +++ b/modmail/models/__init__.py @@ -1 +1 @@ -from .ticket import Ticket # noqa: F401 +from .tickets import Tickets # noqa: F401 diff --git a/modmail/models/tickets.py b/modmail/models/tickets.py new file mode 100644 index 00000000..1fe01d63 --- /dev/null +++ b/modmail/models/tickets.py @@ -0,0 +1,14 @@ +from sqlalchemy import Column, Integer + +from modmail.models.base import Base + + +class Tickets(Base): + """An discord modmail ticket for a Discord user with id `creater_id`.""" + + id = Column(Integer, primary_key=True, unique=True) + server_id = Column(Integer) + thread_id = Column(Integer) + creater_id = Column(Integer) + creating_message_id = Column(Integer) + creating_channel_id = Column(Integer) From 7138f1c19d8634fcf1c0a8383c18779ab893b375 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 18:11:33 +0530 Subject: [PATCH 07/23] feat(db): Add DB model representing message in ticket --- modmail/models/__init__.py | 1 + modmail/models/messages.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 modmail/models/messages.py diff --git a/modmail/models/__init__.py b/modmail/models/__init__.py index a1014d08..8645f9bf 100644 --- a/modmail/models/__init__.py +++ b/modmail/models/__init__.py @@ -1 +1,2 @@ +from .messages import Messages # noqa: F401 from .tickets import Tickets # noqa: F401 diff --git a/modmail/models/messages.py b/modmail/models/messages.py new file mode 100644 index 00000000..76e2d21a --- /dev/null +++ b/modmail/models/messages.py @@ -0,0 +1,14 @@ +from sqlalchemy import Boolean, Column, ForeignKey, Integer, String + +from modmail.models.base import Base + + +class Messages(Base): + """Database model representing a message sent in a modmail ticket.""" + + id = Column(Integer, primary_key=True) + ticket_id = Column(Integer, ForeignKey("ticket.id")) + mirrored_id = Column(Integer) + author_id = Column(Integer) + content = Column(String) + internal = Column(Boolean, default=False) From 34a5a905f77dbf9ce5c46fe22df6f6e93c916635 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 18:29:59 +0530 Subject: [PATCH 08/23] feat(db): Add DB model representing attachment in a message --- modmail/models/attachments.py | 11 +++++++++++ modmail/models/messages.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 modmail/models/attachments.py diff --git a/modmail/models/attachments.py b/modmail/models/attachments.py new file mode 100644 index 00000000..eef92341 --- /dev/null +++ b/modmail/models/attachments.py @@ -0,0 +1,11 @@ +from sqlalchemy import Column, ForeignKey, Integer, String + +from modmail.models.base import Base + + +class Attachments(Base): + """Database model representing a message attachment sent in a modmail ticket.""" + + internal_id = Column(Integer, primary_key=True) + message_id = Column(Integer, ForeignKey("messages.id")) + link = Column(String) diff --git a/modmail/models/messages.py b/modmail/models/messages.py index 76e2d21a..421bd01e 100644 --- a/modmail/models/messages.py +++ b/modmail/models/messages.py @@ -7,7 +7,7 @@ class Messages(Base): """Database model representing a message sent in a modmail ticket.""" id = Column(Integer, primary_key=True) - ticket_id = Column(Integer, ForeignKey("ticket.id")) + ticket_id = Column(Integer, ForeignKey("tickets.id")) mirrored_id = Column(Integer) author_id = Column(Integer) content = Column(String) From 4bcd399b65975fb7edb20c8421fb48559f9bb425 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 20:00:57 +0530 Subject: [PATCH 09/23] feat(db): Add DB model representing embed in a message --- modmail/models/embeds.py | 155 +++++++++++++++++++++++++++++++++++++++ tox.ini | 2 + 2 files changed, 157 insertions(+) create mode 100644 modmail/models/embeds.py diff --git a/modmail/models/embeds.py b/modmail/models/embeds.py new file mode 100644 index 00000000..fe78ebf6 --- /dev/null +++ b/modmail/models/embeds.py @@ -0,0 +1,155 @@ +from typing import Any, Dict, Mapping + +from sqlalchemy import JSON, Column, ForeignKey, Integer +from sqlalchemy.orm import validates + +from modmail.models.base import Base + + +def validate_embed_fields(fields: dict) -> None: + """Raises a ValueError if any of the given embed fields is invalid.""" + field_validators = { + "name": (lambda name: len(name) < 256, "Field `name` must be less than 256 characters long"), + "value": ( + lambda field_value: len(field_value) < 1024, + "`Field value` must be less than 1024 characters long", + ), + "inline": (lambda inline: isinstance(inline, bool), "Field `inline` must be of type bool."), + } + + required_fields = ("name", "value") + + assert len(fields) < 25, "Discord limits up to 25 field objects" + + for field in fields: + if not isinstance(field, Mapping): + raise ValueError("Embed fields must be a mapping.") + + if not all(required_field in field for required_field in required_fields): + raise ValueError(f"Embed fields must contain the following fields: {', '.join(required_fields)}.") + + for field_name, value in field.items(): + if field_name not in field_validators: + raise ValueError(f"Unknown embed field field: {field_name!r}.") + + validation, assertion_msg = field_validators[field_name] + assert validation(value), assertion_msg + + +def validate_embed_footer(footer: Dict[str, str]) -> None: + """Raises a ValueError if the given footer is invalid.""" + field_validators = { + "text": ( + lambda text: 1 < len(text) < 2048, + "Footer `text` must not be empty or be more than 2048 characters.", + ), + "icon_url": (lambda: True, ""), + "proxy_icon_url": (lambda: True, ""), + } + + if not isinstance(footer, Mapping): + raise ValueError("Embed footer must be a mapping.") + + for field_name, value in footer.items(): + if field_name not in field_validators: + raise ValueError(f"Unknown embed footer field: {field_name!r}.") + + validation, assertion_msg = field_validators[field_name] + assert validation(value), assertion_msg + + +def validate_embed_author(author: Any) -> None: + """Raises a ValueError if the given author is invalid.""" + field_validators = { + "name": ( + lambda name: 1 < len(name) < 256, + "Embed `author` name must not be empty or be more than 256 characters.", + ), + "url": (lambda: True, ""), + "icon_url": (lambda: True, ""), + "proxy_icon_url": (lambda: True, ""), + } + + if not isinstance(author, Mapping): + raise ValueError("Embed author must be a mapping.") + + for field_name, value in author.items(): + if field_name not in field_validators: + raise ValueError(f"Unknown embed author field: {field_name!r}.") + + validation, assertion_msg = field_validators[field_name] + assert validation(value), assertion_msg + + +def validate_embed_title(title: str) -> None: + """Raises a ValueError if the given title is invalid.""" + assert 1 < len(title) < 256, "Embed `title` must not be empty or be more than 256 characters." + + +def validate_embed_description(description: str) -> None: + """Raises a ValueError if the given title is invalid.""" + assert len(description) < 2048, "Embed `description` must not be more than 2048 characters." + + +class Embeds(Base): + """Database model representing a discord embed.""" + + internal_id = Column(Integer, primary_key=True) + message_id = Column(Integer, ForeignKey("messages.id")) + json_content = Column(JSON) + + @validates("json_content") + def validate_json_content(self, _: str, embed: Any) -> None: + """ + Validate a JSON document containing an embed as possible to send on Discord. + + This attempts to rebuild the validation used by Discord + as well as possible by checking for various embed limits so we can + ensure that any embed we store here will also be accepted as a + valid embed by the Discord API. + + Raises ValueError, if the given embed is deemed invalid. + """ + all_keys = { + "title", + "type", + "description", + "url", + "timestamp", + "color", + "footer", + "image", + "thumbnail", + "video", + "provider", + "author", + "fields", + } + one_required_of = {"description", "fields", "image", "title", "video"} + field_validators = { + "title": validate_embed_title, + "description": validate_embed_description, + "fields": validate_embed_fields, + "footer": validate_embed_footer, + "author": validate_embed_author, + } + + if not embed: + raise ValueError("Embed must not be empty.") + + elif not isinstance(embed, Mapping): + raise ValueError("Embed must be a mapping.") + + elif not any(field in embed for field in one_required_of): + raise ValueError(f"Embed must contain one of the fields {one_required_of}.") + + for required_key in one_required_of: + if required_key in embed and not embed[required_key]: + raise ValueError(f"Key {required_key!r} must not be empty.") + + for field_name, value in embed.items(): + if field_name not in all_keys: + raise ValueError(f"Unknown field name: {field_name!r}") + + if field_name in field_validators: + field_validators[field_name](value) diff --git a/tox.ini b/tox.ini index d06c9a68..14a14dde 100644 --- a/tox.ini +++ b/tox.ini @@ -24,6 +24,8 @@ ignore= ANN002,ANN003,ANN101,ANN102,ANN204,ANN206 # Whitespace Before E203 + # Assert statements are removed when running in optimized mode, but we don't + S101 per-file-ignores= tests/*:,ANN,S101,F401 docs.py:B008 From 8082820147422029529e8da6e56b0322c8972172 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 22:18:20 +0530 Subject: [PATCH 10/23] feat(db): Shift to asyncpg and drop psycopg --- modmail/__init__.py | 1 + modmail/bot.py | 25 ++++++++----- modmail/config-default.toml | 2 +- modmail/config.py | 3 +- poetry.lock | 73 ++++++++++++++++--------------------- pyproject.toml | 2 +- 6 files changed, 51 insertions(+), 55 deletions(-) diff --git a/modmail/__init__.py b/modmail/__init__.py index 14cd73a5..0c6d0f67 100644 --- a/modmail/__init__.py +++ b/modmail/__init__.py @@ -57,3 +57,4 @@ logging.getLogger("websockets").setLevel(logging.ERROR) # Set asyncio logging back to the default of INFO even if asyncio's debug mode is enabled. logging.getLogger("asyncio").setLevel(logging.INFO) +logging.getLogger("sqlalchemy").setLevel(logging.INFO) diff --git a/modmail/bot.py b/modmail/bot.py index f96d1fba..23158b57 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -9,8 +9,8 @@ from discord import Activity, AllowedMentions, Intents from discord.client import _cleanup_loop from discord.ext import commands -from sqlalchemy import create_engine, engine -from sqlalchemy.orm import Session, sessionmaker +from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine +from sqlalchemy.orm import Session from modmail.config import CONFIG from modmail.log import ModmailLogger @@ -40,7 +40,7 @@ def __init__(self, **kwargs): self.config = CONFIG self.start_time: t.Optional[arrow.Arrow] = None # arrow.utcnow() self.http_session: t.Optional[ClientSession] = None - self._db_engine: t.Optional[engine.Engine] = None + self._db_engine: t.Optional[AsyncEngine] = None self.db: t.Optional[Session] = None status = discord.Status.online @@ -65,10 +65,11 @@ def __init__(self, **kwargs): async def init_db(self) -> None: """Initiate the bot DB session and check if the DB is alive.""" - self._db_engine = create_engine(CONFIG.bot.sqlalchemy_database_uri, pool_pre_ping=True) - self.db = sessionmaker(autocommit=False, autoflush=False, bind=self._db_engine) + self._db_engine = create_async_engine(CONFIG.bot.sqlalchemy_database_uri, echo=True) + self.db = AsyncSession(self._db_engine) try: - self._db_engine.execute("SELECT 1") + # Try to check if DB is awake + await self.db.execute("SELECT 1") self.logger.notice(f"DB Connection pool established at {self._db_engine.url}") except Exception as e: self.logger.error( @@ -161,23 +162,29 @@ async def close(self) -> None: try: self.unload_extension(plug) except Exception: - self.logger.error(f"Exception occured while unloading plugin {plug.name}", exc_info=True) + self.logger.error(f"Exception occurred while unloading plugin {plug.name}", exc_info=True) for ext in list(self.extensions): try: self.unload_extension(ext) except Exception: - self.logger.error(f"Exception occured while unloading {ext.name}", exc_info=True) + self.logger.error(f"Exception occurred while unloading {ext.name}", exc_info=True) for cog in list(self.cogs): try: self.remove_cog(cog) except Exception: - self.logger.error(f"Exception occured while removing cog {cog.name}", exc_info=True) + self.logger.error(f"Exception occurred while removing cog {cog.name}", exc_info=True) if self.http_session: await self.http_session.close() + if self._db_engine: + await self._db_engine.dispose() + + if self.db: + await self.db.close() + await super().close() def load_extensions(self) -> None: diff --git a/modmail/config-default.toml b/modmail/config-default.toml index bd19b018..93f8a3e8 100644 --- a/modmail/config-default.toml +++ b/modmail/config-default.toml @@ -1,6 +1,6 @@ [bot] prefix = "?" -sqlalchemy_database_uri = "postgres://jasonthedestroyer:ihavemnesia33@localhost/modmail" +sqlalchemy_database_uri = "postgresql+asyncpg://jasonthedestroyer:ihavemnesia33@localhost/modmail" [dev] log_level = 25 # "NOTICE" diff --git a/modmail/config.py b/modmail/config.py index 7c0ce06e..5facd5aa 100644 --- a/modmail/config.py +++ b/modmail/config.py @@ -6,7 +6,6 @@ import toml from pydantic import BaseSettings as PydanticBaseSettings -from pydantic import PostgresDsn from pydantic.env_settings import SettingsSourceCallable from pydantic.types import conint @@ -88,7 +87,7 @@ def customise_sources( class BotConfig(BaseSettings): prefix: Optional[str] = "?" token: str - sqlalchemy_database_uri: Optional[PostgresDsn] = None + sqlalchemy_database_uri: Optional[str] = None class Config: # env_prefix = "bot." diff --git a/poetry.lock b/poetry.lock index 56511584..9c4f1e11 100644 --- a/poetry.lock +++ b/poetry.lock @@ -58,6 +58,19 @@ category = "main" optional = false python-versions = ">=3.5.3" +[[package]] +name = "asyncpg" +version = "0.24.0" +description = "An asyncio PostgreSQL driver" +category = "main" +optional = false +python-versions = ">=3.6.0" + +[package.extras] +dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"] +test = ["pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"] + [[package]] name = "atomicwrites" version = "1.4.0" @@ -252,7 +265,7 @@ toml = ["toml"] [[package]] name = "discord.py" -version = "2.0.0a3559+gefec816d" +version = "2.0.0a3563+ge7821be4" description = "A Python wrapper for the Discord API" category = "main" optional = false @@ -271,7 +284,7 @@ voice = ["PyNaCl (>=1.3.0,<1.5)"] type = "git" url = "https://github.com/Rapptz/discord.py.git" reference = "master" -resolved_reference = "efec816de2a9783d98f07aaf02ad7d32cba2b7b9" +resolved_reference = "e7821be4aac7ab68be37a991a68796f01ffc1ece" [[package]] name = "distlib" @@ -772,14 +785,6 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] -[[package]] -name = "psycopg2-binary" -version = "2.9.1" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" -optional = false -python-versions = ">=3.6" - [[package]] name = "py" version = "1.10.0" @@ -1248,7 +1253,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "debe84c1d3caa4a7665f3bb9277b61dfc2ae6a01598e1c508be28dbddf8965bd" +content-hash = "abae541c12fbb5cb3e1a8da34527196978d1877f118840560a20220bbc391cb8" [metadata.files] aiodns = [ @@ -1306,6 +1311,21 @@ async-timeout = [ {file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"}, {file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"}, ] +asyncpg = [ + {file = "asyncpg-0.24.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c4fc0205fe4ddd5aeb3dfdc0f7bafd43411181e1f5650189608e5971cceacff1"}, + {file = "asyncpg-0.24.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a7095890c96ba36f9f668eb552bb020dddb44f8e73e932f8573efc613ee83843"}, + {file = "asyncpg-0.24.0-cp310-cp310-win_amd64.whl", hash = "sha256:8ff5073d4b654e34bd5eaadc01dc4d68b8a9609084d835acd364cd934190a08d"}, + {file = "asyncpg-0.24.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e36c6806883786b19551bb70a4882561f31135dc8105a59662e0376cf5b2cbc5"}, + {file = "asyncpg-0.24.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ddffcb85227bf39cd1bedd4603e0082b243cf3b14ced64dce506a15b05232b83"}, + {file = "asyncpg-0.24.0-cp37-cp37m-win_amd64.whl", hash = "sha256:41704c561d354bef01353835a7846e5606faabbeb846214dfcf666cf53319f18"}, + {file = "asyncpg-0.24.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ef6ae0a617fc13cc2ac5dc8e9b367bb83cba220614b437af9b67766f4b6b20"}, + {file = "asyncpg-0.24.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eed43abc6ccf1dc02e0d0efc06ce46a411362f3358847c6b0ec9a43426f91ece"}, + {file = "asyncpg-0.24.0-cp38-cp38-win_amd64.whl", hash = "sha256:129d501f3d30616afd51eb8d3142ef51ba05374256bd5834cec3ef4956a9b317"}, + {file = "asyncpg-0.24.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a458fc69051fbb67d995fdda46d75a012b5d6200f91e17d23d4751482640ed4c"}, + {file = "asyncpg-0.24.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:556b0e92e2b75dc028b3c4bc9bd5162ddf0053b856437cf1f04c97f9c6837d03"}, + {file = "asyncpg-0.24.0-cp39-cp39-win_amd64.whl", hash = "sha256:a738f4807c853623d3f93f0fea11f61be6b0e5ca16ea8aeb42c2c7ee742aa853"}, + {file = "asyncpg-0.24.0.tar.gz", hash = "sha256:dd2fa063c3344823487d9ddccb40802f02622ddf8bf8a6cc53885ee7a2c1c0c6"}, +] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, @@ -1845,37 +1865,6 @@ psutil = [ {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"}, {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"}, ] -psycopg2-binary = [ - {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"}, - {file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"}, - {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698"}, - {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616"}, - {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7"}, - {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e"}, - {file = "psycopg2_binary-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d"}, - {file = "psycopg2_binary-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce"}, - {file = "psycopg2_binary-2.9.1-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a"}, - {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a"}, - {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0"}, - {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed"}, - {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f"}, - {file = "psycopg2_binary-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e"}, - {file = "psycopg2_binary-2.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2"}, - {file = "psycopg2_binary-2.9.1-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140"}, - {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917"}, - {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90"}, - {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72"}, - {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34"}, - {file = "psycopg2_binary-2.9.1-cp38-cp38-win32.whl", hash = "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32"}, - {file = "psycopg2_binary-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf"}, - {file = "psycopg2_binary-2.9.1-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a"}, - {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4"}, - {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31"}, - {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd"}, - {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a"}, - {file = "psycopg2_binary-2.9.1-cp39-cp39-win32.whl", hash = "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975"}, - {file = "psycopg2_binary-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68"}, -] py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, diff --git a/pyproject.toml b/pyproject.toml index 464eb96b..8f7f4d3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ toml = "^0.10.2" # is set in docker, and hence is needed to successfully install discord.py from git requests = ">=2.7.9" SQLAlchemy = ">=1.4,<1.4.23" -psycopg2-binary = "^2.9.1" +asyncpg = "^0.24.0" [tool.poetry.extras] From 7bf651a7979cfc7f460f8e8a22b7aacef3fdf63f Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 26 Aug 2021 22:18:20 +0530 Subject: [PATCH 11/23] feat(db): Add Emojis and Modmail Configurations Model --- modmail/models/__init__.py | 4 ++++ modmail/models/configurations.py | 12 ++++++++++++ modmail/models/emojis.py | 12 ++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 modmail/models/configurations.py create mode 100644 modmail/models/emojis.py diff --git a/modmail/models/__init__.py b/modmail/models/__init__.py index 8645f9bf..83068007 100644 --- a/modmail/models/__init__.py +++ b/modmail/models/__init__.py @@ -1,2 +1,6 @@ +from .attachments import Attachments # noqa: F401 +from .configurations import Configurations # noqa: F401 +from .embeds import Embeds # noqa: F401 +from .emojis import Emojis # noqa: F401 from .messages import Messages # noqa: F401 from .tickets import Tickets # noqa: F401 diff --git a/modmail/models/configurations.py b/modmail/models/configurations.py new file mode 100644 index 00000000..8e4668d7 --- /dev/null +++ b/modmail/models/configurations.py @@ -0,0 +1,12 @@ +from sqlalchemy import Column, Integer, String + +from modmail.models.base import Base + + +class Configurations(Base): + """Database model representing a discord modmail bot configurations..""" + + server_id = Column(Integer) + thread_id = Column(Integer, nullable=True) + config_key = Column(String) + config_value = Column(String) diff --git a/modmail/models/emojis.py b/modmail/models/emojis.py new file mode 100644 index 00000000..c4e886d6 --- /dev/null +++ b/modmail/models/emojis.py @@ -0,0 +1,12 @@ +from sqlalchemy import Boolean, Column, Integer, String + +from modmail.models.base import Base + + +class Emojis(Base): + """Database model representing a discord emoji.""" + + id = Column(Integer, primary_key=True) + name = Column(String) + url = Column(String) + animated = Column(Boolean, default=False) From b2abd9f04e1946296fd3935d7a5d35255f1f86d9 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 28 Aug 2021 05:17:05 +0530 Subject: [PATCH 12/23] revert(db): Remove Embed model json validation Since we are getting the embed json content directly via discord.py's embed and not generating or touching it, we are safe and validation shouldn't be needed. I have made an extra commit here, in case we ever need it again in future we won't need to rewrite it all over again. --- modmail/models/embeds.py | 144 --------------------------------------- 1 file changed, 144 deletions(-) diff --git a/modmail/models/embeds.py b/modmail/models/embeds.py index fe78ebf6..85af89a2 100644 --- a/modmail/models/embeds.py +++ b/modmail/models/embeds.py @@ -1,155 +1,11 @@ -from typing import Any, Dict, Mapping - from sqlalchemy import JSON, Column, ForeignKey, Integer -from sqlalchemy.orm import validates from modmail.models.base import Base -def validate_embed_fields(fields: dict) -> None: - """Raises a ValueError if any of the given embed fields is invalid.""" - field_validators = { - "name": (lambda name: len(name) < 256, "Field `name` must be less than 256 characters long"), - "value": ( - lambda field_value: len(field_value) < 1024, - "`Field value` must be less than 1024 characters long", - ), - "inline": (lambda inline: isinstance(inline, bool), "Field `inline` must be of type bool."), - } - - required_fields = ("name", "value") - - assert len(fields) < 25, "Discord limits up to 25 field objects" - - for field in fields: - if not isinstance(field, Mapping): - raise ValueError("Embed fields must be a mapping.") - - if not all(required_field in field for required_field in required_fields): - raise ValueError(f"Embed fields must contain the following fields: {', '.join(required_fields)}.") - - for field_name, value in field.items(): - if field_name not in field_validators: - raise ValueError(f"Unknown embed field field: {field_name!r}.") - - validation, assertion_msg = field_validators[field_name] - assert validation(value), assertion_msg - - -def validate_embed_footer(footer: Dict[str, str]) -> None: - """Raises a ValueError if the given footer is invalid.""" - field_validators = { - "text": ( - lambda text: 1 < len(text) < 2048, - "Footer `text` must not be empty or be more than 2048 characters.", - ), - "icon_url": (lambda: True, ""), - "proxy_icon_url": (lambda: True, ""), - } - - if not isinstance(footer, Mapping): - raise ValueError("Embed footer must be a mapping.") - - for field_name, value in footer.items(): - if field_name not in field_validators: - raise ValueError(f"Unknown embed footer field: {field_name!r}.") - - validation, assertion_msg = field_validators[field_name] - assert validation(value), assertion_msg - - -def validate_embed_author(author: Any) -> None: - """Raises a ValueError if the given author is invalid.""" - field_validators = { - "name": ( - lambda name: 1 < len(name) < 256, - "Embed `author` name must not be empty or be more than 256 characters.", - ), - "url": (lambda: True, ""), - "icon_url": (lambda: True, ""), - "proxy_icon_url": (lambda: True, ""), - } - - if not isinstance(author, Mapping): - raise ValueError("Embed author must be a mapping.") - - for field_name, value in author.items(): - if field_name not in field_validators: - raise ValueError(f"Unknown embed author field: {field_name!r}.") - - validation, assertion_msg = field_validators[field_name] - assert validation(value), assertion_msg - - -def validate_embed_title(title: str) -> None: - """Raises a ValueError if the given title is invalid.""" - assert 1 < len(title) < 256, "Embed `title` must not be empty or be more than 256 characters." - - -def validate_embed_description(description: str) -> None: - """Raises a ValueError if the given title is invalid.""" - assert len(description) < 2048, "Embed `description` must not be more than 2048 characters." - - class Embeds(Base): """Database model representing a discord embed.""" internal_id = Column(Integer, primary_key=True) message_id = Column(Integer, ForeignKey("messages.id")) json_content = Column(JSON) - - @validates("json_content") - def validate_json_content(self, _: str, embed: Any) -> None: - """ - Validate a JSON document containing an embed as possible to send on Discord. - - This attempts to rebuild the validation used by Discord - as well as possible by checking for various embed limits so we can - ensure that any embed we store here will also be accepted as a - valid embed by the Discord API. - - Raises ValueError, if the given embed is deemed invalid. - """ - all_keys = { - "title", - "type", - "description", - "url", - "timestamp", - "color", - "footer", - "image", - "thumbnail", - "video", - "provider", - "author", - "fields", - } - one_required_of = {"description", "fields", "image", "title", "video"} - field_validators = { - "title": validate_embed_title, - "description": validate_embed_description, - "fields": validate_embed_fields, - "footer": validate_embed_footer, - "author": validate_embed_author, - } - - if not embed: - raise ValueError("Embed must not be empty.") - - elif not isinstance(embed, Mapping): - raise ValueError("Embed must be a mapping.") - - elif not any(field in embed for field in one_required_of): - raise ValueError(f"Embed must contain one of the fields {one_required_of}.") - - for required_key in one_required_of: - if required_key in embed and not embed[required_key]: - raise ValueError(f"Key {required_key!r} must not be empty.") - - for field_name, value in embed.items(): - if field_name not in all_keys: - raise ValueError(f"Unknown field name: {field_name!r}") - - if field_name in field_validators: - field_validators[field_name](value) From b48b47060437a01388ef74a5fef0c22c02e6b46c Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 28 Aug 2021 17:06:16 +0530 Subject: [PATCH 13/23] fix(db): Don't map messages <-> tickets foreignkey Because it stores things not in the messages/tickets, and it's just use to refer to the message the thread starts from in the ticket logging channel. https://discord.com/channels/798235512208490526/845824657306288158/881136355348992060 --- modmail/models/configurations.py | 2 +- modmail/models/messages.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modmail/models/configurations.py b/modmail/models/configurations.py index 8e4668d7..a14d3d3b 100644 --- a/modmail/models/configurations.py +++ b/modmail/models/configurations.py @@ -6,7 +6,7 @@ class Configurations(Base): """Database model representing a discord modmail bot configurations..""" - server_id = Column(Integer) + server_id = Column(Integer, primary_key=True) thread_id = Column(Integer, nullable=True) config_key = Column(String) config_value = Column(String) diff --git a/modmail/models/messages.py b/modmail/models/messages.py index 421bd01e..caa191b4 100644 --- a/modmail/models/messages.py +++ b/modmail/models/messages.py @@ -1,4 +1,4 @@ -from sqlalchemy import Boolean, Column, ForeignKey, Integer, String +from sqlalchemy import Boolean, Column, Integer, String from modmail.models.base import Base @@ -7,7 +7,7 @@ class Messages(Base): """Database model representing a message sent in a modmail ticket.""" id = Column(Integer, primary_key=True) - ticket_id = Column(Integer, ForeignKey("tickets.id")) + ticket_id = Column(Integer) mirrored_id = Column(Integer) author_id = Column(Integer) content = Column(String) From c87f8c8dfb68ec72b9f48053f7474c1ae5644942 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 28 Aug 2021 17:08:14 +0530 Subject: [PATCH 14/23] feat(db): Add alembic for migrations managing --- poetry.lock | 60 ++++++++++++++++++++++++++++++++++++++++++++++---- pyproject.toml | 1 + 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9c4f1e11..d9e4cee6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -31,6 +31,20 @@ yarl = ">=1.0,<2.0" [package.extras] speedups = ["aiodns", "brotlipy", "cchardet"] +[[package]] +name = "alembic" +version = "1.6.5" +description = "A database migration tool for SQLAlchemy." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" + +[package.dependencies] +Mako = "*" +python-dateutil = "*" +python-editor = ">=0.3" +SQLAlchemy = ">=1.3.0" + [[package]] name = "appdirs" version = "1.4.4" @@ -265,7 +279,7 @@ toml = ["toml"] [[package]] name = "discord.py" -version = "2.0.0a3563+ge7821be4" +version = "2.0.0a3572+g12dcc7c4" description = "A Python wrapper for the Discord API" category = "main" optional = false @@ -284,7 +298,7 @@ voice = ["PyNaCl (>=1.3.0,<1.5)"] type = "git" url = "https://github.com/Rapptz/discord.py.git" reference = "master" -resolved_reference = "e7821be4aac7ab68be37a991a68796f01ffc1ece" +resolved_reference = "12dcc7c44b1f4401eb01a71fe5d430b6d4f6d45e" [[package]] name = "distlib" @@ -568,6 +582,21 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "mako" +version = "1.1.5" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["babel"] +lingua = ["lingua"] + [[package]] name = "markdown" version = "3.3.4" @@ -583,7 +612,7 @@ testing = ["coverage", "pyyaml"] name = "markupsafe" version = "2.0.1" description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" +category = "main" optional = false python-versions = ">=3.6" @@ -1021,6 +1050,14 @@ python-versions = ">=3.5" [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "python-editor" +version = "1.0.4" +description = "Programmatically open an editor, capture the result." +category = "main" +optional = false +python-versions = "*" + [[package]] name = "pyyaml" version = "5.4.1" @@ -1253,7 +1290,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "abae541c12fbb5cb3e1a8da34527196978d1877f118840560a20220bbc391cb8" +content-hash = "117e721410182515ca22e3da2917c8707ec8a853badb7a5a1492b9233840bf1e" [metadata.files] aiodns = [ @@ -1299,6 +1336,10 @@ aiohttp = [ {file = "aiohttp-3.7.4.post0-cp39-cp39-win_amd64.whl", hash = "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe"}, {file = "aiohttp-3.7.4.post0.tar.gz", hash = "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf"}, ] +alembic = [ + {file = "alembic-1.6.5-py2.py3-none-any.whl", hash = "sha256:e78be5b919f5bb184e3e0e2dd1ca986f2362e29a2bc933c446fe89f39dbe4e9c"}, + {file = "alembic-1.6.5.tar.gz", hash = "sha256:a21fedebb3fb8f6bbbba51a11114f08c78709377051384c9c5ead5705ee93a51"}, +] appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, @@ -1692,6 +1733,10 @@ jinja2 = [ {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"}, ] +mako = [ + {file = "Mako-1.1.5-py2.py3-none-any.whl", hash = "sha256:6804ee66a7f6a6416910463b00d76a7b25194cd27f1918500c5bd7be2a088a23"}, + {file = "Mako-1.1.5.tar.gz", hash = "sha256:169fa52af22a91900d852e937400e79f535496191c63712e3b9fda5a9bed6fc3"}, +] markdown = [ {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, @@ -1996,6 +2041,13 @@ python-dotenv = [ {file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"}, {file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"}, ] +python-editor = [ + {file = "python-editor-1.0.4.tar.gz", hash = "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b"}, + {file = "python_editor-1.0.4-py2-none-any.whl", hash = "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8"}, + {file = "python_editor-1.0.4-py2.7.egg", hash = "sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522"}, + {file = "python_editor-1.0.4-py3-none-any.whl", hash = "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d"}, + {file = "python_editor-1.0.4-py3.5.egg", hash = "sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77"}, +] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, diff --git a/pyproject.toml b/pyproject.toml index 8f7f4d3e..077fd79c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ toml = "^0.10.2" requests = ">=2.7.9" SQLAlchemy = ">=1.4,<1.4.23" asyncpg = "^0.24.0" +alembic = "^1.6.5" [tool.poetry.extras] From 6bb69dab5fe187053fc6a9b7f61528600d65414f Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 28 Aug 2021 17:08:41 +0530 Subject: [PATCH 15/23] chore(flake8): Ignore S101 Assert statements are removed when running in optimized mode, but we don't. --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 14a14dde..bbb93c7c 100644 --- a/tox.ini +++ b/tox.ini @@ -5,10 +5,10 @@ docstring-convention=all exclude=modmail/config.py, __pycache__, .cache,.git, - .md,.svg,.png + .md,.svg,.png, venv,.venv, - .json, - docs/ + .json, docs/, + modmail/alembic/versions ignore= B311,W503,E226,S311,T000 From 0220febcbde3383ae505dea0179451f51b36fd5d Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Sat, 28 Aug 2021 17:12:56 +0530 Subject: [PATCH 16/23] feat(db): Add alembic migrations --- modmail/alembic.ini | 81 ++++++++++++++ modmail/alembic/README | 1 + modmail/alembic/env.py | 101 ++++++++++++++++++ modmail/alembic/script.py.mako | 24 +++++ .../500f4263ffe9_add_tickets_table.py | 32 ++++++ .../72459556f7b9_add_configurations_table.py | 30 ++++++ .../versions/7b384834cca4_add_emojis_table.py | 30 ++++++ .../8d52443a026e_add_attachments_table.py | 33 ++++++ .../versions/9dacee669c96_add_embeds_table.py | 33 ++++++ .../e6b20ed26bc2_add_messages_table.py | 32 ++++++ tox.ini | 2 +- 11 files changed, 398 insertions(+), 1 deletion(-) create mode 100644 modmail/alembic.ini create mode 100644 modmail/alembic/README create mode 100644 modmail/alembic/env.py create mode 100644 modmail/alembic/script.py.mako create mode 100644 modmail/alembic/versions/500f4263ffe9_add_tickets_table.py create mode 100644 modmail/alembic/versions/72459556f7b9_add_configurations_table.py create mode 100644 modmail/alembic/versions/7b384834cca4_add_emojis_table.py create mode 100644 modmail/alembic/versions/8d52443a026e_add_attachments_table.py create mode 100644 modmail/alembic/versions/9dacee669c96_add_embeds_table.py create mode 100644 modmail/alembic/versions/e6b20ed26bc2_add_messages_table.py diff --git a/modmail/alembic.ini b/modmail/alembic.ini new file mode 100644 index 00000000..9d0aa65c --- /dev/null +++ b/modmail/alembic.ini @@ -0,0 +1,81 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = alembic + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat alembic/versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +[post_write_hooks] +hooks = black +black.type = console_scripts +black.entrypoint = black +black.options = -l 110 REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/modmail/alembic/README b/modmail/alembic/README new file mode 100644 index 00000000..a23d4fb5 --- /dev/null +++ b/modmail/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. diff --git a/modmail/alembic/env.py b/modmail/alembic/env.py new file mode 100644 index 00000000..4befc1b5 --- /dev/null +++ b/modmail/alembic/env.py @@ -0,0 +1,101 @@ +import asyncio +import inspect +import os +import sys +from logging.config import fileConfig + +from alembic import context +from sqlalchemy import engine_from_config, pool +from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine + +current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +parent_parent_dir = os.path.dirname(os.path.dirname(current_dir)) +sys.path.insert(0, parent_parent_dir) + +from modmail.config import CONFIG # noqa: I201, E402 +from modmail.models.base import Base # noqa: I201, E402 + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def get_url() -> str: + """Get database connection url.""" + return CONFIG.bot.sqlalchemy_database_uri + + +def run_migrations_offline() -> None: + """ + Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + """ + url = get_url() + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: AsyncConnection) -> None: + """Run migrations.""" + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_migrations_online() -> None: + """ + Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + """ + configuration = config.get_section(config.config_ini_section) + configuration["sqlalchemy.url"] = get_url() + connectable = AsyncEngine( + engine_from_config( + configuration, + prefix="sqlalchemy.", + poolclass=pool.NullPool, + future=True, + ) + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + asyncio.run(run_migrations_online()) diff --git a/modmail/alembic/script.py.mako b/modmail/alembic/script.py.mako new file mode 100644 index 00000000..2c015630 --- /dev/null +++ b/modmail/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/modmail/alembic/versions/500f4263ffe9_add_tickets_table.py b/modmail/alembic/versions/500f4263ffe9_add_tickets_table.py new file mode 100644 index 00000000..d115de96 --- /dev/null +++ b/modmail/alembic/versions/500f4263ffe9_add_tickets_table.py @@ -0,0 +1,32 @@ +"""Add tickets table + +Revision ID: 500f4263ffe9 +Revises: e6b20ed26bc2 +Create Date: 2021-08-28 17:01:39.611060 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "500f4263ffe9" +down_revision = "e6b20ed26bc2" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "tickets", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("server_id", sa.BigInteger(), nullable=False), + sa.Column("thread_id", sa.BigInteger(), nullable=False), + sa.Column("creater_id", sa.BigInteger(), nullable=False), + sa.Column("creating_message_id", sa.BigInteger(), nullable=False), + sa.Column("creating_channel_id", sa.BigInteger(), nullable=False), + sa.PrimaryKeyConstraint("id"), + ) + + +def downgrade(): + op.drop_table("tickets") diff --git a/modmail/alembic/versions/72459556f7b9_add_configurations_table.py b/modmail/alembic/versions/72459556f7b9_add_configurations_table.py new file mode 100644 index 00000000..f134daf8 --- /dev/null +++ b/modmail/alembic/versions/72459556f7b9_add_configurations_table.py @@ -0,0 +1,30 @@ +"""Add configurations table + +Revision ID: 72459556f7b9 +Revises: 7b384834cca4 +Create Date: 2021-08-28 17:02:36.726948 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "72459556f7b9" +down_revision = "7b384834cca4" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "configurations", + sa.Column("server_id", sa.BigInteger(), nullable=False), + sa.Column("thread_id", sa.BigInteger(), nullable=True), + sa.Column("config_key", sa.String(), nullable=False), + sa.Column("config_value", sa.String(), nullable=False), + sa.PrimaryKeyConstraint("server_id"), + ) + + +def downgrade(): + op.drop_table("configurations") diff --git a/modmail/alembic/versions/7b384834cca4_add_emojis_table.py b/modmail/alembic/versions/7b384834cca4_add_emojis_table.py new file mode 100644 index 00000000..5eecf5e7 --- /dev/null +++ b/modmail/alembic/versions/7b384834cca4_add_emojis_table.py @@ -0,0 +1,30 @@ +"""Add emojis table + +Revision ID: 7b384834cca4 +Revises: 8d52443a026e +Create Date: 2021-08-28 17:02:30.226745 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "7b384834cca4" +down_revision = "8d52443a026e" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "emojis", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.Column("url", sa.String(), nullable=False), + sa.Column("animated", sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint("id"), + ) + + +def downgrade(): + op.drop_table("emojis") diff --git a/modmail/alembic/versions/8d52443a026e_add_attachments_table.py b/modmail/alembic/versions/8d52443a026e_add_attachments_table.py new file mode 100644 index 00000000..b6956c86 --- /dev/null +++ b/modmail/alembic/versions/8d52443a026e_add_attachments_table.py @@ -0,0 +1,33 @@ +"""Add attachments table + +Revision ID: 8d52443a026e +Revises: 9dacee669c96 +Create Date: 2021-08-28 17:02:15.788101 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "8d52443a026e" +down_revision = "9dacee669c96" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "attachments", + sa.Column("internal_id", sa.BigInteger(), nullable=False), + sa.Column("message_id", sa.BigInteger(), nullable=False), + sa.Column("link", sa.BigInteger(), nullable=False), + sa.ForeignKeyConstraint( + ["message_id"], + ["messages.id"], + ), + sa.PrimaryKeyConstraint("internal_id"), + ) + + +def downgrade(): + op.drop_table("attachments") diff --git a/modmail/alembic/versions/9dacee669c96_add_embeds_table.py b/modmail/alembic/versions/9dacee669c96_add_embeds_table.py new file mode 100644 index 00000000..cc704ccb --- /dev/null +++ b/modmail/alembic/versions/9dacee669c96_add_embeds_table.py @@ -0,0 +1,33 @@ +"""Add embeds table + +Revision ID: 9dacee669c96 +Revises: 500f4263ffe9 +Create Date: 2021-08-28 17:02:06.180093 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "9dacee669c96" +down_revision = "500f4263ffe9" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "embeds", + sa.Column("internal_id", sa.BigInteger(), nullable=False), + sa.Column("message_id", sa.BigInteger(), nullable=False), + sa.Column("json_content", sa.JSON(), nullable=False), + sa.ForeignKeyConstraint( + ["message_id"], + ["messages.id"], + ), + sa.PrimaryKeyConstraint("internal_id"), + ) + + +def downgrade(): + op.drop_table("embeds") diff --git a/modmail/alembic/versions/e6b20ed26bc2_add_messages_table.py b/modmail/alembic/versions/e6b20ed26bc2_add_messages_table.py new file mode 100644 index 00000000..841c6d0a --- /dev/null +++ b/modmail/alembic/versions/e6b20ed26bc2_add_messages_table.py @@ -0,0 +1,32 @@ +"""Add messages table + +Revision ID: e6b20ed26bc2 +Revises: +Create Date: 2021-08-28 17:01:24.449928 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "e6b20ed26bc2" +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "messages", + sa.Column("id", sa.BigInteger(), nullable=False), + sa.Column("ticket_id", sa.BigInteger(), nullable=False), + sa.Column("mirrored_id", sa.BigInteger(), nullable=False), + sa.Column("author_id", sa.BigInteger(), nullable=False), + sa.Column("content", sa.String(), nullable=False), + sa.Column("internal", sa.Boolean()), + sa.PrimaryKeyConstraint("id"), + ) + + +def downgrade(): + op.drop_table("messages") diff --git a/tox.ini b/tox.ini index bbb93c7c..adcf576c 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,7 @@ exclude=modmail/config.py, .md,.svg,.png, venv,.venv, .json, docs/, - modmail/alembic/versions + modmail/alembic/versions/* ignore= B311,W503,E226,S311,T000 From 0ca1ecc1bff0e927719746d732b87172b00f5ad8 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 2 Sep 2021 11:40:22 +0530 Subject: [PATCH 17/23] docs(models): Document all model columns --- .../72459556f7b9_add_configurations_table.py | 5 ++--- .../versions/e6b20ed26bc2_add_messages_table.py | 1 - modmail/models/attachments.py | 8 +++++++- modmail/models/configurations.py | 15 ++++++++++++--- modmail/models/embeds.py | 8 +++++++- modmail/models/emojis.py | 9 ++++++++- modmail/models/messages.py | 16 +++++++++++++--- modmail/models/tickets.py | 17 +++++++++++++++-- 8 files changed, 64 insertions(+), 15 deletions(-) diff --git a/modmail/alembic/versions/72459556f7b9_add_configurations_table.py b/modmail/alembic/versions/72459556f7b9_add_configurations_table.py index f134daf8..edfdf3fb 100644 --- a/modmail/alembic/versions/72459556f7b9_add_configurations_table.py +++ b/modmail/alembic/versions/72459556f7b9_add_configurations_table.py @@ -18,11 +18,10 @@ def upgrade(): op.create_table( "configurations", - sa.Column("server_id", sa.BigInteger(), nullable=False), - sa.Column("thread_id", sa.BigInteger(), nullable=True), + sa.Column("target_id", sa.BigInteger(), nullable=False), sa.Column("config_key", sa.String(), nullable=False), sa.Column("config_value", sa.String(), nullable=False), - sa.PrimaryKeyConstraint("server_id"), + sa.PrimaryKeyConstraint("target_id"), ) diff --git a/modmail/alembic/versions/e6b20ed26bc2_add_messages_table.py b/modmail/alembic/versions/e6b20ed26bc2_add_messages_table.py index 841c6d0a..592b4192 100644 --- a/modmail/alembic/versions/e6b20ed26bc2_add_messages_table.py +++ b/modmail/alembic/versions/e6b20ed26bc2_add_messages_table.py @@ -23,7 +23,6 @@ def upgrade(): sa.Column("mirrored_id", sa.BigInteger(), nullable=False), sa.Column("author_id", sa.BigInteger(), nullable=False), sa.Column("content", sa.String(), nullable=False), - sa.Column("internal", sa.Boolean()), sa.PrimaryKeyConstraint("id"), ) diff --git a/modmail/models/attachments.py b/modmail/models/attachments.py index eef92341..40b385ce 100644 --- a/modmail/models/attachments.py +++ b/modmail/models/attachments.py @@ -4,7 +4,13 @@ class Attachments(Base): - """Database model representing a message attachment sent in a modmail ticket.""" + """ + Database model representing a message attachment sent in a modmail ticket. + + * : Internal ID for the attachment + * : Message ID containing this attachment + * : Discord attachment URL + """ internal_id = Column(Integer, primary_key=True) message_id = Column(Integer, ForeignKey("messages.id")) diff --git a/modmail/models/configurations.py b/modmail/models/configurations.py index a14d3d3b..f9722b03 100644 --- a/modmail/models/configurations.py +++ b/modmail/models/configurations.py @@ -4,9 +4,18 @@ class Configurations(Base): - """Database model representing a discord modmail bot configurations..""" + """ + Database model representing a discord modmail bot configurations. - server_id = Column(Integer, primary_key=True) - thread_id = Column(Integer, nullable=True) + * : target ID according to the level of the configuration. It can + either be the bot ID or the server ID or a thread ID. If it is a + bot ID: global level, across all its servers. + server ID: server level, acorss all its threads. + thread ID: thread level, for that specific server thread. + * : The configuration name + * : The configuration value + """ + + target_id = Column(Integer, primary_key=True, nullable=False) config_key = Column(String) config_value = Column(String) diff --git a/modmail/models/embeds.py b/modmail/models/embeds.py index 85af89a2..966d7c19 100644 --- a/modmail/models/embeds.py +++ b/modmail/models/embeds.py @@ -4,7 +4,13 @@ class Embeds(Base): - """Database model representing a discord embed.""" + """ + Database model representing a discord embed. + + * : Internal ID for the embed + * : Message ID containing this embed + * : Embed represented as JSON data + """ internal_id = Column(Integer, primary_key=True) message_id = Column(Integer, ForeignKey("messages.id")) diff --git a/modmail/models/emojis.py b/modmail/models/emojis.py index c4e886d6..a9292a8f 100644 --- a/modmail/models/emojis.py +++ b/modmail/models/emojis.py @@ -4,7 +4,14 @@ class Emojis(Base): - """Database model representing a discord emoji.""" + """ + Database model representing a custom discord emoji. + + * : emoji ID for server emojis + * : emoji name + * : discord emoji URL + * : whether the emojis is animated or not + """ id = Column(Integer, primary_key=True) name = Column(String) diff --git a/modmail/models/messages.py b/modmail/models/messages.py index caa191b4..120a1bd1 100644 --- a/modmail/models/messages.py +++ b/modmail/models/messages.py @@ -1,14 +1,24 @@ -from sqlalchemy import Boolean, Column, Integer, String +from sqlalchemy import Column, Integer, String from modmail.models.base import Base class Messages(Base): - """Database model representing a message sent in a modmail ticket.""" + """ + Database model representing a message sent in a modmail ticket. + + * : message ID, message IDs are unique + * : modmail ticket ID, the thread created in the guild, + in which this message was sent. + * : mirrored message ID, sent in the guild thread. + * : author ID, would either be the moderator + who sent message in the channel, or the user who has this ticket + opened. + * : message content. + """ id = Column(Integer, primary_key=True) ticket_id = Column(Integer) mirrored_id = Column(Integer) author_id = Column(Integer) content = Column(String) - internal = Column(Boolean, default=False) diff --git a/modmail/models/tickets.py b/modmail/models/tickets.py index 1fe01d63..3c0847ff 100644 --- a/modmail/models/tickets.py +++ b/modmail/models/tickets.py @@ -4,11 +4,24 @@ class Tickets(Base): - """An discord modmail ticket for a Discord user with id `creater_id`.""" + """ + An discord modmail ticket for a Discord user with id `creater_id`. + + * : internal ticket ID for modmail, not for users. + * : ticket creation guild. + * : thread created for this ticket. Since threads won't + have the same ID even across guilds, they would be unique so that two + tickets aren't created. + * : ticket opener's discord user ID. + * : message ID which created this ticket, if + it was opened with DMing the bot, it would be that of the DM channel, + if it was opened with running a command on the guild, it would be that. + * : channel ID the thread was created in. + """ id = Column(Integer, primary_key=True, unique=True) server_id = Column(Integer) - thread_id = Column(Integer) + thread_id = Column(Integer, unique=True) creater_id = Column(Integer) creating_message_id = Column(Integer) creating_channel_id = Column(Integer) From b73ace54501aa647ce2ab83d0e8f3fe5015871ae Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 2 Sep 2021 11:48:28 +0530 Subject: [PATCH 18/23] feat(db): CRUD functions for models.Messages --- modmail/crud/messages.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 modmail/crud/messages.py diff --git a/modmail/crud/messages.py b/modmail/crud/messages.py new file mode 100644 index 00000000..c0bf027c --- /dev/null +++ b/modmail/crud/messages.py @@ -0,0 +1,25 @@ +from typing import Any + +from sqlalchemy.orm import Session + +from modmail.models import Messages + + +class CRUDMessages: + """View providing CRUD operations on Messages.""" + + def get_by_id(self, db: Session, id: Any) -> Messages: + """Get `Messages` object by object ID.""" + return db.query(Messages).filter(Messages.id == id).first() + + def get_by_mirrored_id(self, db: Session, *, mirrored_id: Any) -> Messages: + """Get `Messages` object by mirrored_id, ID of message in the server.""" + return db.query(Messages).filter(Messages.mirrored_id == mirrored_id).first() + + def create(self, db: Session, **kwargs) -> Messages: + """Create a new `Messages` object.""" + db_obj = Messages(**kwargs) + db.add(db_obj) + db.commit() + db.refresh(db_obj) + return db_obj From 4e7379380e7a65741443e2958f180e649ea9c381 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 2 Sep 2021 11:49:15 +0530 Subject: [PATCH 19/23] feat(db): CRUD functions for models.Threads --- modmail/crud/threads.py | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 modmail/crud/threads.py diff --git a/modmail/crud/threads.py b/modmail/crud/threads.py new file mode 100644 index 00000000..906744d9 --- /dev/null +++ b/modmail/crud/threads.py @@ -0,0 +1,49 @@ +from typing import Any, List + +from sqlalchemy.orm import Session + +from modmail.models import Tickets + + +class CRUDTickets: + """View providing CRUD operations on Tickets.""" + + def get_by_id(self, db: Session, id: Any) -> Tickets: + """Get `Tickets` object by object ID.""" + return db.query(Tickets).filter(Tickets.id == id).first() + + def get_multi_by_creator( + self, db: Session, *, creater_id: int, server_id: int, skip: int = 0, limit: int = 100 + ) -> List[Tickets]: + """Get all `Tickets` of a user from server with `server_id`.""" + return ( + db.query(Tickets) + .filter(Tickets.creater_id == creater_id) + .filter(Tickets.server_id == server_id) + .offset(skip) + .limit(limit) + .all() + ) + + def get_multi_by_server( + self, db: Session, *, server_id: int, skip: int = 0, limit: int = 100 + ) -> List[Tickets]: + """Get all `Tickets` for a specific server.""" + return db.query(Tickets).filter(Tickets.server_id == server_id).offset(skip).limit(limit).all() + + def get_by_thread_id(self, db: Session, *, server_id: int, thread_id: int) -> Tickets: + """Get `Tickets` object by thread ID.""" + return ( + db.query(Tickets) + .filter(Tickets.server_id == server_id) + .filter(Tickets.thread_id == thread_id) + .first() + ) + + def create(self, db: Session, **kwargs) -> Tickets: + """Create a new `Tickets` object.""" + db_obj = Tickets(**kwargs) + db.add(db_obj) + db.commit() + db.refresh(db_obj) + return db_obj From 945af5d4258cdc9195800817c280eb31d5b5222f Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 2 Sep 2021 11:51:31 +0530 Subject: [PATCH 20/23] feat(db): CRUD functions for models.Configurations --- modmail/crud/__init__.py | 0 modmail/crud/configurations.py | 54 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 modmail/crud/__init__.py create mode 100644 modmail/crud/configurations.py diff --git a/modmail/crud/__init__.py b/modmail/crud/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modmail/crud/configurations.py b/modmail/crud/configurations.py new file mode 100644 index 00000000..d6dc0a9d --- /dev/null +++ b/modmail/crud/configurations.py @@ -0,0 +1,54 @@ +from typing import Any, Dict, List, Optional + +from sqlalchemy.orm import Session + +from modmail.models import Configurations + + +class CRUDConfigurations: + """View providing CRUD operations on Configurations.""" + + def get_by_config_key(self, db: Session, *, target_id: int, config_key: str) -> Optional[Configurations]: + """Get `Configurations` object by config key for `target_id`.""" + return ( + db.query(Configurations) + .filter(Configurations.target_id == target_id) + .filter(Configurations.config_key == config_key) + .first() + ) + + def get_multi_by_target( + self, db: Session, *, target_id: int, skip: int = 0, limit: int = 100 + ) -> List[Configurations]: + """Get all `Configurations` for `target_id`.""" + return ( + db.query(Configurations) + .filter(Configurations.target_id == target_id) + .offset(skip) + .limit(limit) + .all() + ) + + def create(self, db: Session, **kwargs) -> Configurations: + """Create a new `Configurations` object.""" + db_obj = Configurations(**kwargs) + db.add(db_obj) + db.commit() + db.refresh(db_obj) + return db_obj + + def update(self, db: Session, *, db_obj: Configurations, obj_in: Dict[str, Any]) -> Configurations: + """Update a `Configurations` object.""" + for field in obj_in: + setattr(db_obj, field, obj_in[field]) + db.add(db_obj) + db.commit() + db.refresh(db_obj) + return db_obj + + def delete(self, db: Session, *, target_id: int, config_key: str) -> Configurations: + """Remove a `Configurations` object from the database.""" + obj = self.get_by_config_key(db, target_id=target_id, config_key=config_key) + db.delete(obj) + db.commit() + return obj From 076579ed7d05fb9e7d6445c88996de54116657ce Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 2 Sep 2021 12:09:52 +0530 Subject: [PATCH 21/23] docs(contrib): Add PostgreSQL setup --- docs/contributing.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/contributing.md b/docs/contributing.md index 3c0bad0b..600b367c 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -149,6 +149,37 @@ $ poetry install This runs our register pre-commit hooks on every commit to automatically point out issues in code such as missing semicolons, trailing whitespace, and debug statements. By pointing these issues out before code review, this allows a code reviewer to focus on the architecture of a change while not wasting time with trivial style nitpicks. +### PostgreSQL setup + +Install PostgreSQL according to its [documentation](https://www.postgresql.org/download/). + +Enter psql, a terminal-based front-end to PostgreSQL: + +
+ +```console +$ psql -qd postgres +``` + +Run the following queries to create the user and database: + +```psql +CREATE USER voting WITH SUPERUSER PASSWORD 'modmail'; +CREATE DATABASE modmail WITH OWNER modmail; +``` + +Finally, enter `/q` to exit psql. + +Once the Database is started, you need run migrations to init tables and columns which can be ran through: + +
+ +```console +$ poetry run alembic upgrade heads + +---> 100% +``` + ### Set up modmail config @@ -182,7 +213,12 @@ $ poetry install !!!note The entire file name is literally `.env` -5. Open the file with any text editor and write the bot token to the files in this format: `TOKEN="my_token"`. +5. Open the file with any text editor and write the bot token and the database URL to the files in this format: + * `TOKEN="my_token"`. + * `SQLALCHEMY_DATABASE_URI=postgresql+asyncpg://modmail:modmail@localhost:5432/modmail` + +!!! note + If you configured PostgreSQL in a different manner or you are not hosting it locally, then you will need to determine the correct host and port yourself. The user, password, and database name should all still be pysite unless you deviated from the setup instructions in the previous section. ### Run The Project From 0f32c2c6d9a575de3b99775dd08025060f8bd0d3 Mon Sep 17 00:00:00 2001 From: Shivansh-007 Date: Thu, 2 Sep 2021 12:16:28 +0530 Subject: [PATCH 22/23] chore: Lint fixes --- docs/contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing.md b/docs/contributing.md index 600b367c..00d59bb0 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -169,7 +169,7 @@ CREATE DATABASE modmail WITH OWNER modmail; ``` Finally, enter `/q` to exit psql. - + Once the Database is started, you need run migrations to init tables and columns which can be ran through:
@@ -213,7 +213,7 @@ $ poetry run alembic upgrade heads !!!note The entire file name is literally `.env` -5. Open the file with any text editor and write the bot token and the database URL to the files in this format: +5. Open the file with any text editor and write the bot token and the database URL to the files in this format: * `TOKEN="my_token"`. * `SQLALCHEMY_DATABASE_URI=postgresql+asyncpg://modmail:modmail@localhost:5432/modmail` From b2480f8f60d12a4eb0d88f169889d52452921ce4 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Tue, 7 Sep 2021 16:59:46 -0400 Subject: [PATCH 23/23] fix configuration so tests work again --- modmail/config-default.toml | 3 +-- modmail/config.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/modmail/config-default.toml b/modmail/config-default.toml index 8707efea..bc85fa80 100644 --- a/modmail/config-default.toml +++ b/modmail/config-default.toml @@ -1,11 +1,10 @@ [bot] prefix = "?" -sqlalchemy_database_uri = "postgresql+asyncpg://jasonthedestroyer:ihavemnesia33@localhost/modmail" [colors] [dev] -log_level = 25 # "NOTICE" +log_level = 25 # "NOTICE" [dev.mode] production = true diff --git a/modmail/config.py b/modmail/config.py index 81f06056..4a1c881c 100644 --- a/modmail/config.py +++ b/modmail/config.py @@ -87,8 +87,8 @@ def customise_sources( class BotConfig(BaseSettings): - prefix: Optional[str] = "?" - token: str + prefix: str = "?" + token: str = "" sqlalchemy_database_uri: Optional[str] = None class Config: