diff --git a/Dockerfile b/Dockerfile index 6083d266..6f8b880f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,4 +18,4 @@ COPY . . # install the package using pep 517 RUN pip install . --no-deps --use-feature=in-tree-build -CMD ["python", "-m", "modmail"] +CMD ["sh", "-c", "aerich upgrade && python -m modmail"] diff --git a/aerich.ini b/aerich.ini new file mode 100644 index 00000000..413704e8 --- /dev/null +++ b/aerich.ini @@ -0,0 +1,4 @@ +[aerich] +tortoise_orm = modmail.bot.TORTOISE_ORM +location = ./migrations +src_folder = ./. diff --git a/docker-compose.yaml b/docker-compose.yaml index 8a5ebbbd..29a20e59 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,18 +2,32 @@ version: "3.7" -x-restart-policy: &restart_policy - restart: unless-stopped - services: + postgres: + image: postgres:13-alpine + ports: + - "127.0.0.1:7777:5432" + environment: + POSTGRES_DB: modmail + POSTGRES_PASSWORD: modmail + POSTGRES_USER: modmail + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U modmail" ] + interval: 2s + timeout: 1s + retries: 5 + modmail: - << : *restart_policy - container_name: modmail build: context: . dockerfile: Dockerfile + depends_on: + postgres: + condition: service_healthy + tty: true volumes: - .:/modmail env_file: - .env - tty: true + environment: + database_uri: postgres://modmail:modmail@postgres:5432/modmail diff --git a/docs/contributing.md b/docs/contributing.md index 3c0bad0b..a4599b16 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -150,6 +150,39 @@ $ 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 modmail 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 1. Create a copy of `config-default.yml` named `config.yml` in the the `modmail/` directory. @@ -182,7 +215,14 @@ $ 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"`. + * `DATABASE_URI='postgres://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 `modmail` unless +you deviated from the setup instructions in the previous section. ### Run The Project @@ -262,6 +302,37 @@ your chapter weird. + +## Applying database migrations +Migrations are like a version control system for your database. Each migration defines a change to the database +and how to undo it. By modifying your database through migrations, you create a consistent, testable, and shareable +way to evolve your databases over time. + +You can easily create migrations through aerich's CLI. But before you do that, you need to generate the database +schemas, this can simply be done by running the bot once (`task start`). Once that's done, you can use the CLI +to generate the migration raw SQL file: + +
+ +```console +$ aerich migrate + +---> 100% + +Success migrate 1_202029051520102929_drop_column.sql +``` + +
+ +Now, check your `migrations/models` folder. You should see your brand-new migration! Notice that it also +contains a timestamp. This allows `aerich` to run your migrations in the correct order. Format of migrate filename +is `{version_num}_{datetime}_{name|update}.sql`. If you are renaming a column, aerich would ask you for confirmation, +you can then choose `True` to rename the column without a column drop and `False` to drop the column then create. +Note that the latter may **lose** data. + + + + ## Changelog Requirement Modmail has CI that will check for an entry corresponding to your PR in `CHANGES.md`. diff --git a/migrations/models/0_20211031115120_init.sql b/migrations/models/0_20211031115120_init.sql new file mode 100644 index 00000000..f80ea37e --- /dev/null +++ b/migrations/models/0_20211031115120_init.sql @@ -0,0 +1,59 @@ +-- upgrade -- +CREATE TABLE IF NOT EXISTS "messages" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "ticket_id" BIGINT NOT NULL, + "mirrored_id" BIGINT NOT NULL, + "author_id" BIGINT NOT NULL, + "content" VARCHAR(4000) NOT NULL +); +COMMENT ON TABLE "messages" IS 'Database model representing a message sent in a modmail ticket.'; +CREATE TABLE IF NOT EXISTS "attachments" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "filename" VARCHAR(255) NOT NULL, + "file_url" TEXT NOT NULL, + "message_id_id" BIGINT NOT NULL REFERENCES "messages" ("id") ON DELETE CASCADE +); +COMMENT ON TABLE "attachments" IS 'Database model representing a message attachment sent in a modmail ticket.'; +CREATE TABLE IF NOT EXISTS "embeds" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "content" JSONB NOT NULL, + "message_id_id" BIGINT NOT NULL REFERENCES "messages" ("id") ON DELETE CASCADE +); +COMMENT ON TABLE "embeds" IS 'Database model representing a discord embed.'; +CREATE TABLE IF NOT EXISTS "emojis" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "name" VARCHAR(32) NOT NULL, + "url" TEXT NOT NULL, + "animated" BOOL NOT NULL DEFAULT False, + "message_id_id" BIGINT NOT NULL REFERENCES "messages" ("id") ON DELETE CASCADE +); +COMMENT ON TABLE "emojis" IS 'Database model representing a custom discord emoji.'; +CREATE TABLE IF NOT EXISTS "servers" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "name" VARCHAR(200) NOT NULL, + "icon_url" TEXT NOT NULL +); +COMMENT ON TABLE "servers" IS 'Database model representing a discord server.'; +CREATE TABLE IF NOT EXISTS "configurations" ( + "id" SERIAL NOT NULL PRIMARY KEY, + "target_bot_id" BIGINT, + "config_key" TEXT NOT NULL, + "config_value" TEXT NOT NULL, + "target_server_id_id" BIGINT REFERENCES "servers" ("id") ON DELETE CASCADE +); +COMMENT ON TABLE "configurations" IS 'Database model representing a discord modmail bot configurations.'; +CREATE TABLE IF NOT EXISTS "tickets" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "thread_id" BIGINT NOT NULL UNIQUE, + "creater_id" BIGINT NOT NULL, + "creating_message_id" BIGINT NOT NULL, + "creating_channel_id" BIGINT NOT NULL, + "server_id_id" BIGINT NOT NULL REFERENCES "servers" ("id") ON DELETE CASCADE +); +COMMENT ON TABLE "tickets" IS 'An discord modmail ticket for a Discord user with id `creator_id`.'; +CREATE TABLE IF NOT EXISTS "aerich" ( + "id" SERIAL NOT NULL PRIMARY KEY, + "version" VARCHAR(255) NOT NULL, + "app" VARCHAR(20) NOT NULL, + "content" JSONB NOT NULL +); diff --git a/migrations/models/1_20211101070158_update.sql b/migrations/models/1_20211101070158_update.sql new file mode 100644 index 00000000..22a34e81 --- /dev/null +++ b/migrations/models/1_20211101070158_update.sql @@ -0,0 +1,10 @@ +-- upgrade -- +CREATE TABLE IF NOT EXISTS "stickers" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "name" VARCHAR(32) NOT NULL, + "url" TEXT NOT NULL, + "message_id_id" BIGINT NOT NULL REFERENCES "messages" ("id") ON DELETE CASCADE +); +COMMENT ON TABLE "stickers" IS 'Database model representing a custom discord sticker.'; +-- downgrade -- +DROP TABLE IF EXISTS "stickers"; diff --git a/migrations/models/2_20211120132336_update.sql b/migrations/models/2_20211120132336_update.sql new file mode 100644 index 00000000..79dff696 --- /dev/null +++ b/migrations/models/2_20211120132336_update.sql @@ -0,0 +1,25 @@ +-- upgrade -- +ALTER TABLE "configurations" DROP CONSTRAINT "fk_configur_servers_471a90ee"; +ALTER TABLE "configurations" RENAME COLUMN "target_server_id_id" TO "target_guild_id_id"; +CREATE TABLE IF NOT EXISTS "guilds" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "name" VARCHAR(200) NOT NULL, + "icon_url" TEXT NOT NULL +); +COMMENT ON TABLE "guilds" IS 'Database model representing a discord guild.';; +ALTER TABLE "tickets" ADD "creating_message_id_id" BIGINT NOT NULL; +ALTER TABLE "tickets" RENAME COLUMN "creater_id" TO "author_id"; +ALTER TABLE "tickets" ADD "author_id" BIGINT NOT NULL; +ALTER TABLE "tickets" DROP COLUMN "creating_message_id"; +DROP TABLE IF EXISTS "servers"; +ALTER TABLE "configurations" ADD CONSTRAINT "fk_configur_guilds_942a92c3" FOREIGN KEY ("target_guild_id_id") REFERENCES "guilds" ("id") ON DELETE CASCADE; +ALTER TABLE "tickets" ADD CONSTRAINT "fk_tickets_messages_581a3e15" FOREIGN KEY ("creating_message_id_id") REFERENCES "messages" ("id") ON DELETE CASCADE; +-- downgrade -- +ALTER TABLE "configurations" DROP CONSTRAINT "fk_configur_guilds_942a92c3"; +ALTER TABLE "tickets" DROP CONSTRAINT "fk_tickets_messages_581a3e15"; +ALTER TABLE "tickets" RENAME COLUMN "author_id" TO "creater_id"; +ALTER TABLE "tickets" RENAME COLUMN "author_id" TO "creating_message_id"; +ALTER TABLE "tickets" DROP COLUMN "creating_message_id_id"; +ALTER TABLE "configurations" RENAME COLUMN "target_guild_id_id" TO "target_server_id_id"; +DROP TABLE IF EXISTS "guilds"; +ALTER TABLE "configurations" ADD CONSTRAINT "fk_configur_servers_471a90ee" FOREIGN KEY ("target_server_id_id") REFERENCES "servers" ("id") ON DELETE CASCADE; diff --git a/modmail/bot.py b/modmail/bot.py index de27ea08..eb830c2d 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -1,8 +1,8 @@ import asyncio import logging import signal +import sys import typing as t -from typing import Any import arrow import discord @@ -10,6 +10,7 @@ from discord import Activity, AllowedMentions, Intents from discord.client import _cleanup_loop from discord.ext import commands +from tortoise import BaseDBAsyncClient, Tortoise from modmail.config import CONFIG from modmail.dispatcher import Dispatcher @@ -27,6 +28,16 @@ emojis_and_stickers=True, ) +TORTOISE_ORM = { + "connections": {"default": CONFIG.bot.database_uri}, + "apps": { + "models": { + "models": ["modmail.database.models", "aerich.models"], + "default_connection": "default", + }, + }, +} + class ModmailBot(commands.Bot): """ @@ -65,6 +76,23 @@ def __init__(self, **kwargs): **kwargs, ) + @property + def db(self, name: t.Optional[str] = "default") -> BaseDBAsyncClient: + """Get the default tortoise-orm connection.""" + return Tortoise.get_connection(name) + + async def init_db(self) -> None: + """Initiate the bot DB connection and check if the DB is alive.""" + try: + self.logger.info("Initializing Tortoise...") + await Tortoise.init(TORTOISE_ORM) + + self.logger.info("Generating database schema via Tortoise...") + await Tortoise.generate_schemas() + except Exception as e: + self.logger.error(f"DB connection at {CONFIG.bot.database_uri} not successful, raised:\n{e}") + sys.exit(e) + async def start(self, token: str, reconnect: bool = True) -> None: """ Start the bot. @@ -73,6 +101,7 @@ async def start(self, token: str, reconnect: bool = True) -> None: asyncrhonous event loop running, before connecting the bot to discord. """ try: + await self.init_db() # create the aiohttp session self.http_session = ClientSession(loop=self.loop) self.logger.trace("Created ClientSession.") @@ -122,7 +151,7 @@ def run(self, *args, **kwargs) -> None: except NotImplementedError: pass - def stop_loop_on_completion(f: Any) -> None: + def stop_loop_on_completion(f: t.Any) -> None: loop.stop() future = asyncio.ensure_future(self.start(*args, **kwargs), loop=loop) @@ -167,6 +196,7 @@ async def close(self) -> None: if self.http_session: await self.http_session.close() + await Tortoise.close_connections() await super().close() def load_extensions(self) -> None: diff --git a/modmail/config.py b/modmail/config.py index dce1efeb..59aba490 100644 --- a/modmail/config.py +++ b/modmail/config.py @@ -97,6 +97,7 @@ def customise_sources( class BotConfig(BaseSettings): prefix: str = "?" token: str = None + database_uri: Optional[str] = None class Config: # env_prefix = "bot." diff --git a/modmail/database/__init__.py b/modmail/database/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modmail/database/models/__init__.py b/modmail/database/models/__init__.py new file mode 100644 index 00000000..2186f80c --- /dev/null +++ b/modmail/database/models/__init__.py @@ -0,0 +1,20 @@ +from .attachments import Attachments +from .configuration import Configurations +from .embeds import Embeds +from .emojis import Emojis +from .guilds import Guilds +from .messages import Messages +from .stickers import Stickers +from .tickets import Tickets + + +__all__ = ( + "Attachments", + "Configurations", + "Embeds", + "Emojis", + "Guilds", + "Messages", + "Stickers", + "Tickets", +) diff --git a/modmail/database/models/attachments.py b/modmail/database/models/attachments.py new file mode 100644 index 00000000..335f0a11 --- /dev/null +++ b/modmail/database/models/attachments.py @@ -0,0 +1,15 @@ +from tortoise import fields +from tortoise.models import Model + +from .messages import Messages + + +class Attachments(Model): + """Database model representing a message attachment sent in a modmail ticket.""" + + id = fields.BigIntField(pk=True) + message_id: fields.ForeignKeyRelation[Messages] = fields.ForeignKeyField( + "models.Messages", related_name="attachments", to_field="id" + ) + filename = fields.CharField(max_length=255) + file_url = fields.TextField() diff --git a/modmail/database/models/configuration.py b/modmail/database/models/configuration.py new file mode 100644 index 00000000..15919f7f --- /dev/null +++ b/modmail/database/models/configuration.py @@ -0,0 +1,21 @@ +from tortoise import fields +from tortoise.exceptions import ValidationError +from tortoise.models import Model + +from .guilds import Guilds + + +class Configurations(Model): + """Database model representing a discord modmail bot configurations.""" + + def __init__(self, **kwargs) -> None: + if kwargs.get("target_bot_id") and not kwargs.get("target_server_id"): + raise ValidationError("`target_bot_id` is mutually exclusive with `target_server_id`.") + super().__init__(**kwargs) + + target_bot_id = fields.BigIntField(null=True) + target_guild_id: fields.ForeignKeyRelation[Guilds] = fields.ForeignKeyField( + "models.Guilds", related_name="configurations", to_field="id", null=True + ) + config_key = fields.TextField() + config_value = fields.TextField() diff --git a/modmail/database/models/embeds.py b/modmail/database/models/embeds.py new file mode 100644 index 00000000..b0615dbd --- /dev/null +++ b/modmail/database/models/embeds.py @@ -0,0 +1,14 @@ +from tortoise import fields +from tortoise.models import Model + +from .messages import Messages + + +class Embeds(Model): + """Database model representing a discord embed.""" + + id = fields.BigIntField(pk=True) + message_id: fields.ForeignKeyRelation[Messages] = fields.ForeignKeyField( + "models.Messages", related_name="embeds", to_field="id" + ) + content = fields.JSONField() diff --git a/modmail/database/models/emojis.py b/modmail/database/models/emojis.py new file mode 100644 index 00000000..76b87cef --- /dev/null +++ b/modmail/database/models/emojis.py @@ -0,0 +1,16 @@ +from tortoise import fields +from tortoise.models import Model + +from .messages import Messages + + +class Emojis(Model): + """Database model representing a custom discord emoji.""" + + id = fields.BigIntField(pk=True) + name = fields.CharField(max_length=32) + url = fields.TextField() + animated = fields.BooleanField(default=False) + message_id: fields.ForeignKeyRelation[Messages] = fields.ForeignKeyField( + "models.Messages", related_name="emojis", to_field="id" + ) diff --git a/modmail/database/models/guilds.py b/modmail/database/models/guilds.py new file mode 100644 index 00000000..6d1bdaea --- /dev/null +++ b/modmail/database/models/guilds.py @@ -0,0 +1,20 @@ +from typing import TYPE_CHECKING + +from tortoise import fields +from tortoise.models import Model + + +if TYPE_CHECKING: + from .configuration import Configurations + from .tickets import Tickets + + +class Guilds(Model): + """Database model representing a discord guild.""" + + id = fields.BigIntField(pk=True, null=False) + name = fields.CharField(max_length=200) + icon_url = fields.TextField() + + configurations: fields.ReverseRelation["Configurations"] + tickets: fields.ReverseRelation["Tickets"] diff --git a/modmail/database/models/messages.py b/modmail/database/models/messages.py new file mode 100644 index 00000000..3d3b7947 --- /dev/null +++ b/modmail/database/models/messages.py @@ -0,0 +1,28 @@ +from typing import TYPE_CHECKING + +from tortoise import fields +from tortoise.models import Model + + +if TYPE_CHECKING: + from .attachments import Attachments + from .embeds import Embeds + from .emojis import Emojis + from .stickers import Stickers + from .tickets import Tickets + + +class Messages(Model): + """Database model representing a message sent in a modmail ticket.""" + + id = fields.BigIntField(pk=True) + ticket_id = fields.BigIntField() + mirrored_id = fields.BigIntField() + author_id = fields.BigIntField() + content = fields.CharField(max_length=4000) + + attachments: fields.ReverseRelation["Attachments"] + embeds: fields.ReverseRelation["Embeds"] + emojis: fields.ReverseRelation["Emojis"] + stickers: fields.ReverseRelation["Stickers"] + ticket_creations: fields.ReverseRelation["Tickets"] diff --git a/modmail/database/models/stickers.py b/modmail/database/models/stickers.py new file mode 100644 index 00000000..276b7654 --- /dev/null +++ b/modmail/database/models/stickers.py @@ -0,0 +1,15 @@ +from tortoise import fields +from tortoise.models import Model + +from .messages import Messages + + +class Stickers(Model): + """Database model representing a custom discord sticker.""" + + id = fields.BigIntField(pk=True) + name = fields.CharField(max_length=32) + url = fields.TextField() + message_id: fields.ForeignKeyRelation[Messages] = fields.ForeignKeyField( + "models.Messages", related_name="stickers", to_field="id" + ) diff --git a/modmail/database/models/tickets.py b/modmail/database/models/tickets.py new file mode 100644 index 00000000..f569c979 --- /dev/null +++ b/modmail/database/models/tickets.py @@ -0,0 +1,24 @@ +from tortoise import fields +from tortoise.models import Model + +from .guilds import Guilds +from .messages import Messages + + +class Tickets(Model): + """An discord modmail ticket for a Discord user with id `author_id`.""" + + id = fields.BigIntField(pk=True, unique=True) + server_id: fields.ForeignKeyRelation[Guilds] = fields.ForeignKeyField( + "models.Guilds", + related_name="tickets", + to_field="id", + ) + thread_id = fields.BigIntField(unique=True) + author_id = fields.BigIntField() + creating_message_id: fields.ForeignKeyRelation[Messages] = fields.ForeignKeyField( + "models.Messages", + related_name="ticket_creations", + to_field="id", + ) + creating_channel_id = fields.BigIntField() diff --git a/poetry.lock b/poetry.lock index 71294634..d24b6275 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,22 @@ +[[package]] +name = "aerich" +version = "0.5.8" +description = "A database migrations tool for Tortoise ORM." +category = "main" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +click = "*" +ddlparse = "*" +dictdiffer = "*" +pydantic = "*" +tortoise-orm = ">=0.17.7,<0.18.0" + +[package.extras] +aiomysql = ["aiomysql"] +asyncpg = ["asyncpg"] + [[package]] name = "aiodns" version = "3.0.0" @@ -31,6 +50,17 @@ yarl = ">=1.0,<2.0" [package.extras] speedups = ["aiodns", "brotlipy", "cchardet"] +[[package]] +name = "aiosqlite" +version = "0.17.0" +description = "asyncio bridge to the standard sqlite3 module" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing_extensions = ">=3.7.2" + [[package]] name = "arrow" version = "1.1.1" @@ -50,6 +80,19 @@ category = "main" optional = false python-versions = ">=3.5.3" +[[package]] +name = "asyncpg" +version = "0.25.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" @@ -195,7 +238,7 @@ unicode_backport = ["unicodedata2"] name = "click" version = "8.0.3" description = "Composable command line interface toolkit" -category = "dev" +category = "main" optional = false python-versions = ">=3.6" @@ -250,6 +293,31 @@ tomli = {version = "*", optional = true, markers = "extra == \"toml\""} [package.extras] toml = ["tomli"] +[[package]] +name = "ddlparse" +version = "1.10.0" +description = "DDL parase and Convert to BigQuery JSON schema" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pyparsing = "*" + +[[package]] +name = "dictdiffer" +version = "0.9.0" +description = "Dictdiffer is a library that helps you to diff and patch dictionaries." +category = "main" +optional = false +python-versions = "*" + +[package.extras] +all = ["Sphinx (>=3)", "sphinx-rtd-theme (>=0.2)", "check-manifest (>=0.42)", "mock (>=1.3.0)", "pytest-cov (>=2.10.1)", "pytest-isort (>=1.2.0)", "sphinx (>=3)", "tox (>=3.7.0)", "numpy (>=1.13.0)", "numpy (>=1.15.0)", "numpy (>=1.18.0)", "pytest (==5.4.3)", "pytest-pycodestyle (>=2)", "pytest-pydocstyle (>=2)", "pytest (>=6)", "pytest-pycodestyle (>=2.2.0)", "pytest-pydocstyle (>=2.2.0)", "numpy (>=1.20.0)"] +docs = ["Sphinx (>=3)", "sphinx-rtd-theme (>=0.2)"] +numpy = ["numpy (>=1.13.0)", "numpy (>=1.15.0)", "numpy (>=1.18.0)", "numpy (>=1.20.0)"] +tests = ["check-manifest (>=0.42)", "mock (>=1.3.0)", "pytest-cov (>=2.10.1)", "pytest-isort (>=1.2.0)", "sphinx (>=3)", "tox (>=3.7.0)", "pytest (==5.4.3)", "pytest-pycodestyle (>=2)", "pytest-pydocstyle (>=2)", "pytest (>=6)", "pytest-pycodestyle (>=2.2.0)", "pytest-pydocstyle (>=2.2.0)"] + [[package]] name = "discord.py" version = "2.0.0a0" @@ -434,7 +502,7 @@ python-versions = "*" python-dateutil = ">=2.8.1" [package.extras] -dev = ["twine", "markdown", "flake8"] +dev = ["twine", "markdown", "flake8", "wheel"] [[package]] name = "gitdb" @@ -513,6 +581,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "iso8601" +version = "0.1.16" +description = "Simple module to parse ISO 8601 dates" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "isort" version = "5.9.3" @@ -858,10 +934,18 @@ Markdown = ">=3.2" name = "pyparsing" version = "2.4.7" description = "Python parsing module" -category = "dev" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "pypika-tortoise" +version = "0.1.1" +description = "Forked from pypika and streamline just for tortoise-orm" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" + [[package]] name = "pyreadline3" version = "3.3" @@ -997,6 +1081,14 @@ python-versions = ">=3.5" [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "pytz" +version = "2021.3" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "pyyaml" version = "5.4.1" @@ -1128,6 +1220,27 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "tortoise-orm" +version = "0.17.8" +description = "Easy async ORM for python, built with relations in mind" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +aiosqlite = ">=0.16.0,<0.18.0" +asyncpg = {version = "*", optional = true, markers = "extra == \"asyncpg\""} +iso8601 = ">=0.1.13,<0.2.0" +pypika-tortoise = ">=0.1.1,<0.2.0" +pytz = "*" + +[package.extras] +aiomysql = ["aiomysql"] +asyncmy = ["asyncmy"] +asyncpg = ["asyncpg"] +accel = ["ciso8601 (>=2.1.2,<3.0.0)", "python-rapidjson", "uvloop (>=0.14.0,<0.15.0)"] + [[package]] name = "typing-extensions" version = "3.10.0.2" @@ -1206,9 +1319,13 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "b85bebf7a796b0b37e647fd7289589ea03a147c155839dfd1bcd561ead1fde92" +content-hash = "109b67721fccabddc7388bafe3cf446cbd91970ea571355597c25c0890eb1edb" [metadata.files] +aerich = [ + {file = "aerich-0.5.8-py3-none-any.whl", hash = "sha256:231ea9897f49e1a5e95884cd5fbed31df722dbd417a4778d301e9c46af7457a4"}, + {file = "aerich-0.5.8.tar.gz", hash = "sha256:1086d67788d805e273b828100fc66593ba96c1baeba7ff1198bf5be993975f4d"}, +] aiodns = [ {file = "aiodns-3.0.0-py3-none-any.whl", hash = "sha256:2b19bc5f97e5c936638d28e665923c093d8af2bf3aa88d35c43417fa25d136a2"}, {file = "aiodns-3.0.0.tar.gz", hash = "sha256:946bdfabe743fceeeb093c8a010f5d1645f708a241be849e17edfb0e49e08cd6"}, @@ -1252,6 +1369,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"}, ] +aiosqlite = [ + {file = "aiosqlite-0.17.0-py3-none-any.whl", hash = "sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231"}, + {file = "aiosqlite-0.17.0.tar.gz", hash = "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51"}, +] arrow = [ {file = "arrow-1.1.1-py3-none-any.whl", hash = "sha256:77a60a4db5766d900a2085ce9074c5c7b8e2c99afeaa98ad627637ff6f292510"}, {file = "arrow-1.1.1.tar.gz", hash = "sha256:dee7602f6c60e3ec510095b5e301441bc56288cb8f51def14dcb3079f623823a"}, @@ -1260,6 +1381,34 @@ 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.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf5e3408a14a17d480f36ebaf0401a12ff6ae5457fdf45e4e2775c51cc9517d3"}, + {file = "asyncpg-0.25.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bc197fc4aca2fd24f60241057998124012469d2e414aed3f992579db0c88e3a"}, + {file = "asyncpg-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a70783f6ffa34cc7dd2de20a873181414a34fd35a4a208a1f1a7f9f695e4ec4"}, + {file = "asyncpg-0.25.0-cp310-cp310-win32.whl", hash = "sha256:43cde84e996a3afe75f325a68300093425c2f47d340c0fc8912765cf24a1c095"}, + {file = "asyncpg-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:56d88d7ef4341412cd9c68efba323a4519c916979ba91b95d4c08799d2ff0c09"}, + {file = "asyncpg-0.25.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a84d30e6f850bac0876990bcd207362778e2208df0bee8be8da9f1558255e634"}, + {file = "asyncpg-0.25.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:beaecc52ad39614f6ca2e48c3ca15d56e24a2c15cbfdcb764a4320cc45f02fd5"}, + {file = "asyncpg-0.25.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6f8f5fc975246eda83da8031a14004b9197f510c41511018e7b1bedde6968e92"}, + {file = "asyncpg-0.25.0-cp36-cp36m-win32.whl", hash = "sha256:ddb4c3263a8d63dcde3d2c4ac1c25206bfeb31fa83bd70fd539e10f87739dee4"}, + {file = "asyncpg-0.25.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bf6dc9b55b9113f39eaa2057337ce3f9ef7de99a053b8a16360395ce588925cd"}, + {file = "asyncpg-0.25.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:acb311722352152936e58a8ee3c5b8e791b24e84cd7d777c414ff05b3530ca68"}, + {file = "asyncpg-0.25.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0a61fb196ce4dae2f2fa26eb20a778db21bbee484d2e798cb3cc988de13bdd1b"}, + {file = "asyncpg-0.25.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2633331cbc8429030b4f20f712f8d0fbba57fa8555ee9b2f45f981b81328b256"}, + {file = "asyncpg-0.25.0-cp37-cp37m-win32.whl", hash = "sha256:863d36eba4a7caa853fd7d83fad5fd5306f050cc2fe6e54fbe10cdb30420e5e9"}, + {file = "asyncpg-0.25.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fe471ccd915b739ca65e2e4dbd92a11b44a5b37f2e38f70827a1c147dafe0fa8"}, + {file = "asyncpg-0.25.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:72a1e12ea0cf7c1e02794b697e3ca967b2360eaa2ce5d4bfdd8604ec2d6b774b"}, + {file = "asyncpg-0.25.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4327f691b1bdb222df27841938b3e04c14068166b3a97491bec2cb982f49f03e"}, + {file = "asyncpg-0.25.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:739bbd7f89a2b2f6bc44cb8bf967dab12c5bc714fcbe96e68d512be45ecdf962"}, + {file = "asyncpg-0.25.0-cp38-cp38-win32.whl", hash = "sha256:18d49e2d93a7139a2fdbd113e320cc47075049997268a61bfbe0dde680c55471"}, + {file = "asyncpg-0.25.0-cp38-cp38-win_amd64.whl", hash = "sha256:191fe6341385b7fdea7dbdcf47fd6db3fd198827dcc1f2b228476d13c05a03c6"}, + {file = "asyncpg-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fab7f1b2c29e187dd8781fce896249500cf055b63471ad66332e537e9b5f7e"}, + {file = "asyncpg-0.25.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a738f1b2876f30d710d3dc1e7858160a0afe1603ba16bf5f391f5316eb0ed855"}, + {file = "asyncpg-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4105f57ad1e8fbc8b1e535d8fcefa6ce6c71081228f08680c6dea24384ff0e"}, + {file = "asyncpg-0.25.0-cp39-cp39-win32.whl", hash = "sha256:f55918ded7b85723a5eaeb34e86e7b9280d4474be67df853ab5a7fa0cc7c6bf2"}, + {file = "asyncpg-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:649e2966d98cc48d0646d9a4e29abecd8b59d38d55c256d5c857f6b27b7407ac"}, + {file = "asyncpg-0.25.0.tar.gz", hash = "sha256:63f8e6a69733b285497c2855464a34de657f2cccd25aeaeeb5071872e9382540"}, +] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, @@ -1298,6 +1447,14 @@ brotlipy = [ {file = "brotlipy-0.7.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:79aaf217072840f3e9a3b641cccc51f7fc23037496bd71e26211856b93f4b4cb"}, {file = "brotlipy-0.7.0-cp34-cp34m-win32.whl", hash = "sha256:a07647886e24e2fb2d68ca8bf3ada398eb56fd8eac46c733d4d95c64d17f743b"}, {file = "brotlipy-0.7.0-cp34-cp34m-win_amd64.whl", hash = "sha256:c6cc0036b1304dd0073eec416cb2f6b9e37ac8296afd9e481cac3b1f07f9db25"}, + {file = "brotlipy-0.7.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:382971a641125323e90486244d6266ffb0e1f4dd920fbdcf508d2a19acc7c3b3"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:82f61506d001e626ec3a1ac8a69df11eb3555a4878599befcb672c8178befac8"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:7ff18e42f51ebc9d9d77a0db33f99ad95f01dd431e4491f0eca519b90e9415a9"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:8ef230ca9e168ce2b7dc173a48a0cc3d78bcdf0bd0ea7743472a317041a4768e"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:b7cf5bb69e767a59acc3da0d199d4b5d0c9fed7bef3ffa3efa80c6f39095686b"}, + {file = "brotlipy-0.7.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:e5c549ae5928dda952463196180445c24d6fad2d73cb13bd118293aced31b771"}, + {file = "brotlipy-0.7.0-cp35-abi3-win32.whl", hash = "sha256:79ab3bca8dd12c17e092273484f2ac48b906de2b4828dcdf6a7d520f99646ab3"}, + {file = "brotlipy-0.7.0-cp35-abi3-win_amd64.whl", hash = "sha256:ac1d66c9774ee62e762750e399a0c95e93b180e96179b645f28b162b55ae8adc"}, {file = "brotlipy-0.7.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:07194f4768eb62a4f4ea76b6d0df6ade185e24ebd85877c351daa0a069f1111a"}, {file = "brotlipy-0.7.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7e31f7adcc5851ca06134705fcf3478210da45d35ad75ec181e1ce9ce345bb38"}, {file = "brotlipy-0.7.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9448227b0df082e574c45c983fa5cd4bda7bfb11ea6b59def0940c1647be0c3c"}, @@ -1309,6 +1466,7 @@ brotlipy = [ {file = "brotlipy-0.7.0-cp36-cp36m-win32.whl", hash = "sha256:2e5c64522364a9ebcdf47c5744a5ddeb3f934742d31e61ebfbbc095460b47162"}, {file = "brotlipy-0.7.0-cp36-cp36m-win_amd64.whl", hash = "sha256:09ec3e125d16749b31c74f021aba809541b3564e5359f8c265cbae442810b41a"}, {file = "brotlipy-0.7.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:4e4638b49835d567d447a2cfacec109f9a777f219f071312268b351b6839436d"}, + {file = "brotlipy-0.7.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5664fe14f3a613431db622172bad923096a303d3adce55536f4409c8e2eafba4"}, {file = "brotlipy-0.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1379347337dc3d20b2d61456d44ccce13e0625db2611c368023b4194d5e2477f"}, {file = "brotlipy-0.7.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:22a53ccebcce2425e19f99682c12be510bf27bd75c9b77a1720db63047a77554"}, {file = "brotlipy-0.7.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4bac11c1ffba9eaa2894ec958a44e7f17778b3303c2ee9f99c39fcc511c26668"}, @@ -1470,6 +1628,14 @@ coverage = [ {file = "coverage-6.0.2-pp37-none-any.whl", hash = "sha256:30922626ce6f7a5a30bdba984ad21021529d3d05a68b4f71ea3b16bda35b8895"}, {file = "coverage-6.0.2.tar.gz", hash = "sha256:6807947a09510dc31fa86f43595bf3a14017cd60bf633cc746d52141bfa6b149"}, ] +ddlparse = [ + {file = "ddlparse-1.10.0-py3-none-any.whl", hash = "sha256:71761b3457c8720853af3aeef266e2da1b6edef50936969492d586d7046a2ac2"}, + {file = "ddlparse-1.10.0.tar.gz", hash = "sha256:6418681baa848eb01251ab79eb3d0ad7e140e6ab1deaae5a019353ddb3a908da"}, +] +dictdiffer = [ + {file = "dictdiffer-0.9.0-py2.py3-none-any.whl", hash = "sha256:442bfc693cfcadaf46674575d2eba1c53b42f5e404218ca2c2ff549f2df56595"}, + {file = "dictdiffer-0.9.0.tar.gz", hash = "sha256:17bacf5fbfe613ccf1b6d512bd766e6b21fb798822a133aa86098b8ac9997578"}, +] "discord.py" = [] distlib = [ {file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"}, @@ -1523,6 +1689,7 @@ flake8-todo = [ ] ghp-import = [ {file = "ghp-import-2.0.1.tar.gz", hash = "sha256:753de2eace6e0f7d4edfb3cce5e3c3b98cd52aadb80163303d1d036bda7b4483"}, + {file = "ghp_import-2.0.1-py3-none-any.whl", hash = "sha256:8241a8e9f8dd3c1fafe9696e6e081b57a208ef907e9939c44e7415e407ab40ea"}, ] gitdb = [ {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"}, @@ -1552,6 +1719,10 @@ iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] +iso8601 = [ + {file = "iso8601-0.1.16-py2.py3-none-any.whl", hash = "sha256:906714829fedbc89955d52806c903f2332e3948ed94e31e85037f9e0226b8376"}, + {file = "iso8601-0.1.16.tar.gz", hash = "sha256:36532f77cc800594e8f16641edae7f1baf7932f05d8e508545b95fc53c6dc85b"}, +] isort = [ {file = "isort-5.9.3-py3-none-any.whl", hash = "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2"}, {file = "isort-5.9.3.tar.gz", hash = "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899"}, @@ -1877,6 +2048,10 @@ pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] +pypika-tortoise = [ + {file = "pypika-tortoise-0.1.1.tar.gz", hash = "sha256:6831d0a56e5e0ecefac3307dd9bdb3e5073fdac5d617401601d3a6713e059f3c"}, + {file = "pypika_tortoise-0.1.1-py3-none-any.whl", hash = "sha256:860020094e01058ea80602c90d4a843d0a42cffefcf4f3cb1a7f2c18b880c638"}, +] pyreadline3 = [ {file = "pyreadline3-3.3-py3-none-any.whl", hash = "sha256:0003fd0079d152ecbd8111202c5a7dfa6a5569ffd65b235e45f3c2ecbee337b4"}, {file = "pyreadline3-3.3.tar.gz", hash = "sha256:ff3b5a1ac0010d0967869f723e687d42cabc7dccf33b14934c92aa5168d260b3"}, @@ -1915,6 +2090,10 @@ 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"}, ] +pytz = [ + {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, + {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, +] 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"}, @@ -2032,6 +2211,10 @@ tomli = [ {file = "tomli-1.2.1-py3-none-any.whl", hash = "sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f"}, {file = "tomli-1.2.1.tar.gz", hash = "sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442"}, ] +tortoise-orm = [ + {file = "tortoise-orm-0.17.8.tar.gz", hash = "sha256:1f5020e9964d32a4d6ed685d466b5d7285de328a63ee92ee988c1e4baf8fefbf"}, + {file = "tortoise_orm-0.17.8-py3-none-any.whl", hash = "sha256:f18c41bb83be4748a6ca259ed7309ca954b35f5790971824bbc79a11d2b1ef3b"}, +] typing-extensions = [ {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, diff --git a/pyproject.toml b/pyproject.toml index 245c8fad..d7e6a1a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,8 @@ coloredlogs = "^15.0" "discord.py" = { url = "https://github.com/Rapptz/discord.py/archive/master.zip" } pydantic = { version = "^1.8.2", extras = ["dotenv"] } toml = "^0.10.2" +tortoise-orm = {extras = ["asyncpg"], version = "^0.17.8"} +aerich = "^0.5.8" [tool.poetry.extras] diff --git a/requirements.txt b/requirements.txt index d0251ef6..e48a68b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,28 +1,39 @@ # NOTICE: This file is automatically generated by scripts/export_requirements.py # This is also automatically regenerated when an edit to pyproject.toml or poetry.lock is commited. +aerich==0.5.8 ; python_version >= "3.7" aiodns==3.0.0 aiohttp==3.7.4.post0 ; python_version >= "3.6" +aiosqlite==0.17.0 ; python_version >= "3.6" arrow==1.1.1 ; python_version >= "3.6" async-timeout==3.0.1 ; python_full_version >= "3.5.3" +asyncpg==0.25.0 ; python_full_version >= "3.6.0" attrs==21.2.0 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" brotlipy==0.7.0 cchardet==2.1.7 cffi==1.15.0 chardet==4.0.0 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" +click==8.0.3 ; python_version >= "3.6" colorama==0.4.4 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" coloredlogs==15.0.1 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" +ddlparse==1.10.0 +dictdiffer==0.9.0 discord.py @ https://github.com/Rapptz/discord.py/archive/master.zip ; python_full_version >= "3.8.0" humanfriendly==10.0 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" and python_version != "3.4" idna==3.2 ; python_version >= "3.5" +iso8601==0.1.16 multidict==5.2.0 ; python_version >= "3.6" pycares==4.1.2 pycparser==2.20 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" and python_version != "3.3" pydantic==1.8.2 ; python_full_version >= "3.6.1" +pyparsing==2.4.7 ; python_version >= "2.6" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" +pypika-tortoise==0.1.1 ; python_version >= "3.7" pyreadline3==3.3 ; sys_platform == "win32" python-dateutil==2.8.2 ; python_version != "3.0" python-dotenv==0.19.0 ; python_version >= "3.5" +pytz==2021.3 six==1.16.0 ; python_version >= "2.7" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" toml==0.10.2 ; python_version >= "2.6" and python_version != "3.0" and python_version != "3.1" and python_version != "3.2" +tortoise-orm==0.17.8 ; python_version >= "3.7" typing-extensions==3.10.0.2 yarl==1.7.2 ; python_version >= "3.6" diff --git a/tox.ini b/tox.ini index bc557a04..a97152d4 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,7 @@ ignore= per-file-ignores= tests/*:,ANN,S101,F401 docs.py:B008 + modmail/database/models/__init__.py:F401 [isort] profile=black