diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index b8e2873..0000000 --- a/.coveragerc +++ /dev/null @@ -1,10 +0,0 @@ -[report] -exclude_lines = - import - : TypeAlias - _HAS_ORJSON - \.{3} - TypeVar - Callable -[run] -include = orx/* diff --git a/.github/checkers/pylint.json b/.github/checkers/pylint.json deleted file mode 100644 index 71480fd..0000000 --- a/.github/checkers/pylint.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "pylint", - "serverity": "warning", - "pattern": [ - { - "regexp": "(.+):(\\d+):(\\d+): (?:I|R|C|W|E|F)(?:\\d+): (.+) \\((.+)\\)", - "file": 1, - "line": 2, - "column": 3, - "message": 4, - "code": 5 - } - ] - } - ] -} diff --git a/.github/checkers/pyright.json b/.github/checkers/pyright.json deleted file mode 100644 index 70523e8..0000000 --- a/.github/checkers/pyright.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "pyright", - "pattern": [ - { - "regexp": "/home/runner/work/orx/orx/(.+):(\\d+):(\\d+) - (information|warning|error): (.+) \\((.+)\\)", - "file": 1, - "line": 2, - "column": 3, - "severity": 4, - "message": 5, - "code": 6 - } - ] - } - ] -} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml deleted file mode 100644 index a496bfd..0000000 --- a/.github/workflows/lint.yaml +++ /dev/null @@ -1,97 +0,0 @@ -name: Lint -permissions: - contents: read -on: - pull_request: {} - push: - branches: ["master"] -jobs: - black: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install poetry - run: pip install poetry - - name: Install dependencies - run: poetry install - - name: Run tests - run: poetry run task black_check - isort: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install poetry - run: pip install poetry - - name: Install dependencies - run: poetry install - - name: Run tests - run: poetry run task isort_check - pyright: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install poetry - run: pip install poetry - - name: Install dependencies - run: poetry install -E speed - - uses: actions/setup-node@v2 - name: Install node - with: - node-version: "14" - - name: Install pyright - run: npm install -g pyright - - name: Enable the problem checker - run: echo "::add-matcher::.github/checkers/pyright.json" - - name: Run pyright - run: poetry run pyright orx/ - style-guide: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install poetry - run: pip install poetry - - name: Install SSH key - uses: shimataro/ssh-key-action@v2 - with: - key: ${{ secrets.DEPLOY_KEY }} - known_hosts: ${{ secrets.KNOWN_HOSTS }} - if_key_exists: replace - - name: Install style-guide - run: poetry add git+ssh://git@github.com/nextsnake/style-guide.git --dev - - name: Install dependencies - run: poetry install - - name: Enable the problem checker - run: echo "::add-matcher::.github/checkers/pylint.json" - - name: Run pylint - run: poetry run pylint -d all -e asyncio-best-practices,license,outdated-typing --load-plugins=nextstyle orx/ - slots-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - name: Install poetry - run: pip install poetry - - name: Install dependencies - run: poetry install - - name: Run slotscheck - run: poetry run slotscheck orx/ - diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml deleted file mode 100644 index 2ee6b16..0000000 --- a/.github/workflows/tests.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: Tests -permissions: - contents: read -on: - pull_request: {} - push: - branches: ["master"] -jobs: - pytest: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.8", "3.9", "3.10"] - name: Pytest (${{ matrix.python-version }}) - steps: - - uses: actions/checkout@v1 - - name: Set up Python - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install poetry - run: pip install poetry - - name: Install dependencies - run: poetry install - - name: Run tests - run: poetry run task tests diff --git a/.gitignore b/.gitignore index b647843..a096ff3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,143 +1,3 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST +__pycache__ poetry.lock - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# Editors -.vscode/ -.idea/ +test.py \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 721995b..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks -repos: - - repo: https://github.com/psf/black - rev: 22.3.0 - hooks: - - id: black - args: ["--check"] - - repo: https://github.com/pycqa/isort - rev: 5.10.1 - hooks: - - id: isort - args: ["--check"] - - repo: local - hooks: - - id: pytest - name: pytest - entry: pytest - language: system - pass_filenames: false - always_run: true - - repo: local - hooks: - - id: pyright - name: pyright - entry: pyright - language: system - pass_filenames: false - always_run: true - - repo: local - hooks: - - id: slotscheck - name: slotscheck - entry: slotscheck orx/ - language: system - pass_filenames: false - always_run: true diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index eef29ed..0000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,15 +0,0 @@ -version: 2 - -build: - os: ubuntu-22.04 - tools: - python: "3.11" - jobs: - post_create_environment: - - pip install poetry - - poetry config virtualenvs.create false - post_install: - - poetry install - -sphinx: - configuration: docs/conf.py diff --git a/LICENSE b/LICENSE index e8375a7..d8d42f0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023-present nextsnake developers +Copyright (c) 2023 tag-epic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md deleted file mode 100644 index fa0b6aa..0000000 --- a/README.md +++ /dev/null @@ -1,43 +0,0 @@ -
- - - - # Orx - A high level Discord API wrapper made using nextcore - -
- -### ✨ Features - -- #### Speed - - We try to make the library as fast as possible, without compromising on readability of the code or features. - -- #### Modularity - - All the components can easily be swapped out with your own. - -- #### Control - - Orx offers fine-grained control over things most libraries don't support by giving you low level access to our low level library - -
- -
- - # Examples - -
- -### 🏓 Ping pong -A simple "ping pong" example in nextcore. -This will respond with "pong" each time someone sends "ping" in the chat. - -TODO: Add example - -> More examples can be seen in the [examples](examples/) directory. - -
- -## Contributing -Want to help us out? Please read our [contributing](https://orx.readthedocs.io/en/latest/contributing/getting_started.html) docs. diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index dcee282..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -j auto -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/_static/logo.ico b/docs/_static/logo.ico deleted file mode 100644 index 61d7e36..0000000 Binary files a/docs/_static/logo.ico and /dev/null differ diff --git a/docs/_static/logo.svg b/docs/_static/logo.svg deleted file mode 100644 index 739a5c6..0000000 --- a/docs/_static/logo.svg +++ /dev/null @@ -1,11 +0,0 @@ - - 3 - - - - - - - diff --git a/docs/_static/speed-showcase.mp4 b/docs/_static/speed-showcase.mp4 deleted file mode 100644 index 439158e..0000000 Binary files a/docs/_static/speed-showcase.mp4 and /dev/null differ diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index a77ac60..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,73 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys - -sys.path.insert(0, os.path.abspath("..")) - - -# -- Project information ----------------------------------------------------- - -project = "orx" -copyright = "2023 nextsnake developers" -author = "nextsnake developers" - - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.napoleon", - "sphinx.ext.intersphinx", - "sphinx.ext.autosectionlabel", - "sphinxext.opengraph", - "sphinx_inline_tabs", -] -autodoc_typehints = "description" - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] - -# -- Extension configuration ------------------------------------------------- -# Intersphinx -intersphinx_mapping = { - "aiohttp": ("https://docs.aiohttp.org/en/stable/", None), - "python": ("https://docs.python.org/3/", None), - "towncrier": ("https://towncrier.readthedocs.io/en/stable", None), -} - -# Opengraph -ogp_image = "/_static/logo.svg" -ogp_site_name = "Orx Documentation" - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "furo" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] -html_logo = "_static/logo.svg" -html_favicon = "_static/logo.ico" diff --git a/docs/contributing/getting_started.rst b/docs/contributing/getting_started.rst deleted file mode 100644 index 82bca40..0000000 --- a/docs/contributing/getting_started.rst +++ /dev/null @@ -1,45 +0,0 @@ -:og:title: Orx contribution guide -:og:description: Info about getting started helping out with the orx project! - -Contributing -============= - -Setting up a development environment --------------------------------------- -First, you will need to fork the `repository `__. - -Then you will need to setup a ssh-key. See `GitHub's tutorial `__. - -.. code-block:: sh - - git clone https://github.com//orx - - cd orx - - poetry install - - git checkout -b - - poetry shell # You will need to run this every time you need access to your development version of orx. - -We recommend making a "development" folder and adding it to your .git/info/exclude file. - -This can be done by making a ``.dev`` folder and appending ``.dev`` to the .git/info/exclude file on a new line. -Every file in the ``.dev`` directory will now be invisible to git. - -Submitting your changes -------------------------- -Before submitting we recommend checking a few things - -1. You have linted your code. This can be done by running ``task lint`` -2. You have checked your code for type errors. This can be done by running ``pyright`` - -.. hint:: - Pyright needs to be installed. This can be done by using ``npm install --global pyright`` - -3. Did you remember to write a changelog? See :external+towncrier:doc:`the towncrier tutorial ` - -Do's and Dont's ----------------- -- Do keep your PR scope small. We would rather review many small PRs with seperate features than one giant one. -- Make draft PRs. diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 8f5a557..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,42 +0,0 @@ -:og:description: A high level Discord API wrapper built on nextcore - -Welcome to Orx's documentation! -==================================== - -.. toctree:: - :maxdepth: 1 - :caption: Contents: - - releasenotes - -.. toctree:: - :hidden: - - contributing/getting_started - - -Quickstart -========== -First, you need to install the library. - -.. tab:: Pip - - .. code-block:: shell - - pip install orx - -.. tab:: Poetry - - .. code-block:: shell - - poetry add orx - -.. warning:: - - Using :data:`logging.DEBUG` logging may include secrets. - - -Helping out -============= -We would appriciate your help writing orx and related libraries. -See :ref:`contributing` for more info diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 153be5e..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst deleted file mode 100644 index 352cc6a..0000000 --- a/docs/releasenotes.rst +++ /dev/null @@ -1,2 +0,0 @@ -Release notes -============= diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index d50d7ec..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -Sphinx>=4.4.0 -sphinx-copybutton>=0.4.0 -furo>=2022.1.2 diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index b58b781..0000000 --- a/examples/README.md +++ /dev/null @@ -1 +0,0 @@ -# Orx examples diff --git a/newsfragments/.gitkeep b/newsfragments/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/orx/__init__.py b/orx/__init__.py index 2b4b128..0c54e74 100644 --- a/orx/__init__.py +++ b/orx/__init__.py @@ -1,30 +1,20 @@ -# The MIT License (MIT) -# Copyright (c) 2023-present nextsnake developers -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the "Software"), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. +""" +orx +~~~ +A simple wrapper for the Discord API. -from __future__ import annotations +:license: MIT +:copyright: 2023 tag-epic +""" -from typing import TYPE_CHECKING +from typing import Literal -if TYPE_CHECKING: - from typing import Final -__version__: Final[str] = "1.0.0a" -__all__: Final[tuple[str, ...]] = ("__version__",) +__version__: Literal['0.1.0'] = '0.1.0' + + +from .bot import * +from .events import * +from .flags import * +from .models import * +from .state import * diff --git a/orx/bot.py b/orx/bot.py new file mode 100644 index 0000000..e317bbe --- /dev/null +++ b/orx/bot.py @@ -0,0 +1,84 @@ +# cython: language_level=3 +# Copyright (c) 2023 tag-epic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +import asyncio +import typing as t + +from nextcore.gateway import ShardManager +from nextcore.http import BotAuthentication, HTTPClient + +from .events.dispatcher import ClassDispatcher +from .flags import Intents +from .models import Model +from .state import DEFAULT_MODELS, State + + +class Bot: + """A class representing a Discord bot controlled by the user.""" + + def __init__( + self, + token: str, + intents: Intents, + state_cls: t.Type[State] = State, + models: dict[Model, Model] = DEFAULT_MODELS, + shards: int | None = None, + shard_ids: list[int] | None = None, + **state_kwargs + ) -> None: + mods = DEFAULT_MODELS + for k, v in models.items(): + mods[k] = v + + self._state = state_cls(token, mods, **state_kwargs) + self._auth = BotAuthentication(token) + self._http_client = HTTPClient() + self._shard_manager = ShardManager( + self._auth, + int(intents), + self._http_client, + shard_count=shards, + shard_ids=shard_ids, + ) + self.intents = intents + self._dispatcher = ClassDispatcher() + + async def _on_gateway_event(self, event: str, data: dict[str, t.Any]) -> None: + print(event) + + async def connect(self, block: bool = True) -> None: + self._shard_manager.event_dispatcher.add_listener( + self._on_gateway_event, event_name=None + ) + await self._http_client.setup() + await self._shard_manager.connect() + + if block: + try: + await asyncio.Future() + except (asyncio.CancelledError, KeyboardInterrupt): + await self._shard_manager.close() + await self._http_client.close() + self._dispatcher.close() + + def start(self, *, debug: bool = False) -> None: + asyncio.run(self.connect(), debug=debug) diff --git a/orx/events/__init__.py b/orx/events/__init__.py new file mode 100644 index 0000000..24f3c5b --- /dev/null +++ b/orx/events/__init__.py @@ -0,0 +1,10 @@ +""" +orx.events +~~~~~~~~~~ +Module housing orx's event handling + +:license: MIT +:copyright: 2023 tag-epic +""" + +from .dispatcher import * diff --git a/orx/events/dispatcher.py b/orx/events/dispatcher.py new file mode 100644 index 0000000..ec40009 --- /dev/null +++ b/orx/events/dispatcher.py @@ -0,0 +1,38 @@ +# cython: language_level=3 +# Copyright (c) 2023 tag-epic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import typing as t + +from nextcore.common import Dispatcher + + +class Event(t.Protocol): + cache = False + + def __init__(self) -> None: + ... + + +class CacheEvent(Event): + cache = True + + +ClassDispatcher = Dispatcher[t.Type[Event]] diff --git a/orx/flags.py b/orx/flags.py new file mode 100644 index 0000000..561ba4e --- /dev/null +++ b/orx/flags.py @@ -0,0 +1,64 @@ +# cython: language_level=3 +# Copyright (c) 2023 tag-epic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +# TODO: test whether performance of these flags are subnominal + +import typing as t +from enum import IntFlag + +T = t.TypeVar("T") + + +class BaseFlag(IntFlag): + @classmethod + def all(cls: t.Type[T]) -> T: + value: int = 0 + + for name in dir(cls): + attr = getattr(cls, name) + + if isinstance(attr, BaseFlag): + value += int(attr) + + return cls(value) + + +class Intents(BaseFlag): + guilds = 1 << 0 + guild_members = 1 << 1 + guild_moderation = 1 << 2 + guild_emojis_and_stickers = 1 << 3 + guild_integrations = 1 << 4 + guild_webhooks = 1 << 5 + guild_invites = 1 << 6 + guild_voice_states = 1 << 7 + guild_presences = 1 << 8 + guild_messages = 1 << 9 + guild_message_reactions = 1 << 10 + guild_message_typing = 1 << 11 + direct_messages = 1 << 12 + direct_message_reactions = 1 << 13 + direct_message_typing = 1 << 14 + message_content = 1 << 15 + guild_scheduled_events = 1 << 16 + auto_moderation_configuration = 1 << 20 + auto_moderation_execution = 1 << 21 diff --git a/orx/models/__init__.py b/orx/models/__init__.py new file mode 100644 index 0000000..e017e36 --- /dev/null +++ b/orx/models/__init__.py @@ -0,0 +1,12 @@ +""" +orx.models +~~~~~~~~~~ +Modular representations of Discord objects and items. + +:license: MIT +:copyright: 2023 tag-epic +""" + + +class Model: + """The highest level model. Only used for typing purposes.""" diff --git a/orx/py.typed b/orx/py.typed deleted file mode 100644 index d040955..0000000 --- a/orx/py.typed +++ /dev/null @@ -1,2 +0,0 @@ -# Marker file for PEP 561. -# This marks that we use inline typings for type checkers diff --git a/orx/state/__init__.py b/orx/state/__init__.py new file mode 100644 index 0000000..1753760 --- /dev/null +++ b/orx/state/__init__.py @@ -0,0 +1,11 @@ +""" +orx.state +~~~~~~~~~ +Orx state handling. + +:license: MIT +:copyright: 2023 tag-epic +""" + +from .cache import * +from .core import * diff --git a/orx/state/cache.py b/orx/state/cache.py new file mode 100644 index 0000000..37fee45 --- /dev/null +++ b/orx/state/cache.py @@ -0,0 +1,113 @@ +# cython: language_level=3 +# Copyright (c) 2023 tag-epic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +import typing as t + + +class _stored: + __slots__ = ("parents", "id", "storing") + + def __init__(self, parents: set[t.Any], self_id: t.Any, storing: t.Any) -> None: + self.parents = parents + self.id = self_id + self.storing = storing + + +T = t.TypeVar("T") + + +class Store: + __slots__ = ("_store", "max_items") + + _store: set[_stored] + + def __init__(self, max_items: int | None = None) -> None: + self._store = set() + self.max_items = max_items + + async def fetch_one(self, parents: list[t.Any], id: t.Any) -> t.Any | None: + ps = set(parents) + + for store in self._store: + if store.parents & ps and store.id == id: + return store.storing + + async def fetch_from_id(self, id: t.Any) -> tuple[set[t.Any], t.Any] | None: + for store in self._store: + if store.id == id: + return store.parents, store.storing + + async def insert(self, parents: list[t.Any], id: t.Any, data: t.Any) -> None: + if self.max_items and len(self._store) == self.max_items: + self._store = set() + + self._store.add(_stored(set(parents), id, data)) + + async def replace( + self, parents: list[t.Any], id: t.Any, data: t.Any + ) -> t.Any | None: + ps = set(parents) + + for store in self._store: + if store.parents & ps and store.id == id: + old_data = store.storing + self._store.discard(store) + store.storing = data + self._store.add(store) + return old_data + else: + if self.max_items and len(self._store) == self.max_items: + self._store = {} + + store = _stored(set(parents), id, data) + self._store.add(store) + + async def delete( + self, parents: list[t.Any], id: t.Any, type: t.Type[T] | T = None + ) -> T: + ps = set(parents) + + for store in self._store: + if store.parents & ps or store.id == id: + self._store.remove(store) + return store + + async def fetch_all(self): + for store in self._store: + yield store.storing + + async def fetch_children(self, parents: list[t.Any]): + ps = set(parents) + + for store in self._store: + if store.parents & ps: + yield store.storing + + async def clear(self) -> None: + self._store.clear() + + async def delete_children(self, parents: list[t.Any]) -> None: + ps = set(parents) + + for store in self._store: + if store.parents & ps: + self._store.remove(store) diff --git a/orx/state/core.py b/orx/state/core.py new file mode 100644 index 0000000..d796d41 --- /dev/null +++ b/orx/state/core.py @@ -0,0 +1,35 @@ +# cython: language_level=3 +# Copyright (c) 2023 tag-epic +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +from ..events.dispatcher import Event +from ..models import Model +from .cache import Store + +DEFAULT_MODELS: dict[Model, Model] = {} + + +class State: + def __init__(self, token: str, models: dict[Model, Model] = DEFAULT_MODELS) -> None: + self.token = token + self.stores: dict[str, Store] = {} + self.models: dict[Model, Model] = models + self.cache_events: list[Event] = [] diff --git a/pyproject.toml b/pyproject.toml index ada3b54..f9e9742 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,95 +1,19 @@ [tool.poetry] name = "orx" -version = "1.0.0a" -description = "A high level Discord API wrapper made using nextcore" -authors = ["nextsnake developers"] +version = "0.1.0" +description = "A simple and powerful Python wrapper for the Discord API" +authors = ["tag-epic", "VincentRPS "] license = "MIT" -classifiers = [ - "Private :: Do Not Upload", - "Development Status :: 3 - Alpha", - "Framework :: AsyncIO", - "Framework :: aiohttp", - "Intended Audience :: Developers", - "Natural Language :: English", - "Operating System :: OS Independent", - "Programming Language :: Cython", - "Programming Language :: Python :: Implementation :: CPython", - "Topic :: Communications :: Chat", - "Topic :: Software Development :: Libraries :: Python Modules", - "Typing :: Typed" -] readme = "README.md" -homepage = "https://nextcord.dev" # TODO: Replace this? -repository = "https://github.com/nextsnake/orx" -documentation = "https://docs.nextcord.gay" # TODO: Replace this -keywords = ["discord", "bot", "wrapper", "api"] -packages = [ - { include = "orx" } -] - -[tool.poetry.urls] -"Bug Tracker" = "https://github.com/nextsnake/orx/issues" [tool.poetry.dependencies] -python = "^3.8" -aiohttp = ">=3.6.0,<4.0.0" -frozendict = "^2.3.0" -types-frozendict = "^2.0.6" # Could we extend the version requirement -typing-extensions = "^4.1.1" # Same as above -orjson = {version = "^3.6.8", optional = true} -types-orjson = {version = "^3.6.2", optional = true} -discord-typings = "^0.5.0" +python = "^3.11" +nextcore = {git = "https://github.com/nextsnake/nextcore"} [tool.poetry.group.dev.dependencies] -Sphinx = "^5.0.0" -sphinx-copybutton = "^0.4.0" -furo = "*" # CalVer, the version does not make sense to lock. -isort = "^5.10.1" -black = "^22.6.0" -pytest = "^6.2.5" -taskipy = "^1.9.0" -pytest-asyncio = "^0.18.1" -pytest-mock = "^3.7.0" -pre-commit = "^2.18.1" -sphinxext-opengraph = "^0.6.3" -slotscheck = "^0.14.0" -sphinx-inline-tabs = "*" # CalVer, the version does not make sense to lock. -towncrier = "^22.12.0" +black = "^23.3.0" +isort = "^5.12.0" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" - -[tool.poetry.extras] -speed = ["orjson", "types-orjson"] - -# Tools -[tool.taskipy.tasks] -black_check = "black . --diff --check" -isort_check = "isort . --check --diff" -tests = "pytest" -black = "black ." -isort = "isort ." -lint = "black . && isort ." - -[tool.black] -line-length = 120 -target-version = ["py38"] - -[tool.isort] -profile = "black" - -[tool.pytest.ini_options] -asyncio_mode = "strict" -testpaths = ["tests"] - -[tool.pyright] -pythonPlatform = "All" -typeCheckingMode = "strict" -pythonVersion = "3.8" -exclude = ["tests/"] - -[tool.towncrier] -package = "orx" -filename = "docs/releasenotes.rst" -directory = "newsfragments/" diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/utils.py b/tests/utils.py deleted file mode 100644 index 1a97f09..0000000 --- a/tests/utils.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import annotations - -from asyncio import TimeoutError, wait_for -from time import time -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from typing import Any, Callable - - from typing_extensions import ParamSpec - - P = ParamSpec("P") - - -def match_time(estimated: float, max_offset: float): - """Errror if the estimated time is off""" - - def outer(func: Callable[P, Any]): - async def inner(*args: P.args, **kwargs: P.kwargs) -> None: - start = time() - try: - await wait_for(func(*args, **kwargs), timeout=estimated + max_offset) - except TimeoutError: - raise TimeoutError( - f"Function returned too slowly (terminated). Expected {estimated}s, got {estimated + max_offset}s" - ) from None - end = time() - time_used = end - start - assert ( - time_used >= estimated - max_offset - ), f"Function returned too quickly. Expected {estimated}s, got {time_used}s" - assert ( - time_used <= estimated + max_offset - ), f"Function returned too slowly. Expected {estimated}s, got {time_used}s" - - return inner - - return outer