From 6db23f6490ce1c7cbcfdac33cd4270e01a24947d Mon Sep 17 00:00:00 2001 From: null <60427892+null8626@users.noreply.github.com> Date: Tue, 26 Mar 2024 18:16:42 +0700 Subject: [PATCH 01/71] refactor: remove unused import The following pull request ~~adds malware~~ removes unused `StatsWrapper` import in the `tests/test_autopost.py` file. --- tests/test_autopost.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_autopost.py b/tests/test_autopost.py index a4f8ee7a..7cf39c14 100644 --- a/tests/test_autopost.py +++ b/tests/test_autopost.py @@ -5,7 +5,7 @@ from aiohttp import ClientSession from pytest_mock import MockerFixture -from topgg import DBLClient, StatsWrapper +from topgg import DBLClient from topgg.autopost import AutoPoster from topgg.errors import ServerError, TopGGException, Unauthorized From a9f6ee7d6348d6e2099e58fac1b66c7435f3a88e Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 26 Mar 2024 18:30:08 +0700 Subject: [PATCH 02/71] meta: update links --- README.rst | 2 +- docs/conf.py | 2 +- topgg/http.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 5bdddce6..69ab58b2 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ Install from source .. code:: bash - pip3 install git+https://github.com/top-gg/python-sdk/ + pip3 install git+https://github.com/top-gg-community/python-sdk/ Documentation ------------- diff --git a/docs/conf.py b/docs/conf.py index 2d368576..ca87da68 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ autodoc_member_order = "groupwise" extlinks = { - "issue": ("https://github.com/top-gg/python-sdk/issues/%s", "GH-"), + "issue": ("https://github.com/top-gg-community/python-sdk/issues/%s", "GH-"), } intersphinx_mapping = { diff --git a/topgg/http.py b/topgg/http.py index 08160d67..6bd967c5 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -86,7 +86,7 @@ def __init__( [self.global_rate_limiter, self.bot_rate_limiter] ) self.user_agent = ( - f"topggpy (https://github.com/top-gg/python-sdk {__version__}) Python/" + f"topggpy (https://github.com/top-gg-community/python-sdk {__version__}) Python/" f"{sys.version_info[0]}.{sys.version_info[1]} aiohttp/{aiohttp.__version__}" ) From 28c56e44fb2feea9f947d5b84cb51260dd9d0acc Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 26 Mar 2024 18:32:38 +0700 Subject: [PATCH 03/71] meta: make the min supported ver 3.8 --- .github/workflows/python-package.yml | 2 +- .github/workflows/python-publish.yml | 2 +- .readthedocs.yml | 2 +- README.rst | 2 +- setup.py | 7 ++++--- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 60b37a8f..4d3db9c1 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ 3.6, 3.7, 3.8, 3.9 ] + python-version: [ 3.8, 3.9, 3.10, 3.11, 3.12 ] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 1eba4d89..2208dc39 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.7' + python-version: '3.8' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.readthedocs.yml b/.readthedocs.yml index 388f9a1f..35fe3221 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -7,7 +7,7 @@ build: image: latest python: - version: 3.8 + version: 3.12 install: - requirements: requirements.txt - requirements: requirements-docs.txt diff --git a/README.rst b/README.rst index 69ab58b2..8860689d 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ Top.gg Python Library .. image:: https://img.shields.io/pypi/v/topggpy.svg :target: https://pypi.python.org/pypi/topggpy - :alt: View on PyPi + :alt: View on PyPI .. image:: https://img.shields.io/pypi/pyversions/topggpy.svg :target: https://pypi.python.org/pypi/topggpy :alt: v1.0.0 diff --git a/setup.py b/setup.py index 36733afb..8dc72dfd 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ long_description=readme, package_data={"topgg": ["py.typed"]}, include_package_data=True, - python_requires=">= 3.6", + python_requires=">= 3.8", install_requires=requirements, keywords="discord bot server list discordservers serverlist discordbots botlist topgg top.gg", classifiers=[ @@ -52,10 +52,11 @@ "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Internet", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", From 957af685a0de676febbc7539d783cd086e9b017f Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 26 Mar 2024 18:35:28 +0700 Subject: [PATCH 04/71] fix: python 3.12 support --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9ad05803..1b80c609 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -aiohttp>=3.6.0,<3.9.0 \ No newline at end of file +aiohttp>=3.9.0 \ No newline at end of file From 3300b504aeffa72384b75cc5dde706095d68fa70 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 26 Mar 2024 18:36:50 +0700 Subject: [PATCH 05/71] fix: why does it say 3.1 :skull: --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 4d3db9c1..bea34d1f 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ 3.8, 3.9, 3.10, 3.11, 3.12 ] + python-version: [ 3.8, 3.9, '3.10', 3.11, 3.12 ] steps: - uses: actions/checkout@v2 From c2c7a691b1703181a1ea25980daa7e6481cda69d Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 26 Mar 2024 18:49:46 +0700 Subject: [PATCH 06/71] fix: migrate from setup.py to pyproject.toml --- pyproject.toml | 35 +++++++++++++++++++++++ requirements-docs.txt | 2 ++ setup.py | 65 ------------------------------------------- 3 files changed, 37 insertions(+), 65 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..6e789b6f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,35 @@ +[build-system] +requires = ["setuptools"] + +[project] +name = "topggpy" +version = "2.0.1" +description = "A simple API wrapper for Top.gg written in Python." +readme = "README.rst" +license = { text = "MIT" } +authors = [{ name = "Assanali Mukhanov" }, { name = "Norizon" }, { name = "Top.gg" }] +keywords = ["discord", "bot", "topgg", "top.gg"] +dependencies = ["aiohttp>=3.9.0"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: MIT License", + "Intended Audience :: Developers", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Internet", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Utilities" +] +requires-python = ">=3.8" + +[project.urls] +Homepage = "https://topggpy.readthedocs.io/en/stable/" +Documentation = "https://topggpy.readthedocs.io/en/stable/" +Repository = "https://github.com/top-gg-community/python-sdk" \ No newline at end of file diff --git a/requirements-docs.txt b/requirements-docs.txt index e75c15e9..b90afd89 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,2 +1,4 @@ sphinx insegel +sphinxcontrib-napoleon +sphinx-rtd-dark-mode \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 8dc72dfd..00000000 --- a/setup.py +++ /dev/null @@ -1,65 +0,0 @@ -import os -import pathlib -import re -import types - -from setuptools import find_packages, setup - -HERE = pathlib.Path(__file__).parent - -txt = (HERE / "topgg" / "__init__.py").read_text("utf-8") - -groups = {} - -for match in re.finditer(r'__(?P.*)__\s*=\s*"(?P[^"]+)"\r?', txt): - group = match.groupdict() - groups[group["identifier"]] = group["value"] - -metadata = types.SimpleNamespace(**groups) - -on_rtd = os.getenv("READTHEDOCS") == "True" - -with open("requirements.txt") as f: - requirements = f.read().splitlines() - -if on_rtd: - requirements.append("sphinxcontrib-napoleon") - requirements.append("sphinx-rtd-dark-mode") - -with open("README.rst") as f: - readme = f.read() - -setup( - name="topggpy", - author=f"{metadata.author}, Top.gg", - author_email="shivaco.osu@gmail.com", - maintainer=f"{metadata.maintainer}, Top.gg", - url="https://github.com/top-gg/python-sdk", - version=metadata.version, - packages=find_packages(), - license=metadata.license, - description="A simple API wrapper for Top.gg written in Python.", - long_description=readme, - package_data={"topgg": ["py.typed"]}, - include_package_data=True, - python_requires=">= 3.8", - install_requires=requirements, - keywords="discord bot server list discordservers serverlist discordbots botlist topgg top.gg", - classifiers=[ - "Development Status :: 5 - Production/Stable", - "License :: OSI Approved :: MIT License", - "Intended Audience :: Developers", - "Natural Language :: English", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Internet", - "Topic :: Software Development :: Libraries", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Utilities", - ], -) From ee2ec2e241a24f1ea51423fca6b6d6c38138903e Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 26 Mar 2024 18:50:09 +0700 Subject: [PATCH 07/71] meta: bump version --- topgg/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topgg/__init__.py b/topgg/__init__.py index 1a9025eb..eed4b284 100644 --- a/topgg/__init__.py +++ b/topgg/__init__.py @@ -12,7 +12,7 @@ __author__ = "Assanali Mukhanov" __maintainer__ = "Norizon" __license__ = "MIT" -__version__ = "2.0.0a1" +__version__ = "2.0.1" from .autopost import * from .client import * From 472f69d6b194e79682b63c4eb4fe722170387cc7 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 26 Mar 2024 18:56:43 +0700 Subject: [PATCH 08/71] ci: support pyproject.toml --- .github/workflows/python-package.yml | 2 +- .github/workflows/python-publish.yml | 31 ---------------------------- 2 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 .github/workflows/python-publish.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index bea34d1f..c24b9baf 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -29,7 +29,7 @@ jobs: path: "requirements-dev.txt" - name: Install itself run: | - python setup.py install + python -m pip install . - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml deleted file mode 100644 index 2208dc39..00000000 --- a/.github/workflows/python-publish.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries - -name: Upload Python Package - -on: - release: - types: [ created ] - -jobs: - deploy: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.8' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine -r requirements.txt - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - python setup.py sdist bdist_wheel - twine upload dist/* From 5ee4fece90c3e5d05fc504ca5536f9283e9b0796 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 26 Mar 2024 19:00:48 +0700 Subject: [PATCH 09/71] feat: add python-publish.yml again with PyPI token support --- .github/workflows/python-publish.yml | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/python-publish.yml diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 00000000..5ff91b04 --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,31 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: Upload Python Package + +on: + release: + types: [ created ] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.12' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine -r requirements.txt + - name: Build and publish + env: + TWINE_USERNAME: '__token__' + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* \ No newline at end of file From 82187cb79f15b742c669174c20aac6f726a33565 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 26 Mar 2024 19:31:53 +0700 Subject: [PATCH 10/71] meta: update .gitignore [skip ci] --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3d7edd2f..1d889220 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -dblpy.egg-info/ +topggpy.egg-info/ topggpy.egg-info/ topgg/__pycache__/ build/ From f4e83c06cc4e8d81298e0438ce83867f682f922b Mon Sep 17 00:00:00 2001 From: null8626 Date: Wed, 27 Mar 2024 12:37:03 +0700 Subject: [PATCH 11/71] meta: change maintainers --- LICENSE | 1 + pyproject.toml | 2 +- topgg/__init__.py | 4 ++-- topgg/py.typed | 0 4 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 topgg/py.typed diff --git a/LICENSE b/LICENSE index 96aaaf80..066ec504 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ Copyright 2021 Assanali Mukhanov & Top.gg +Copyright 2024 null & Top.gg 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: diff --git a/pyproject.toml b/pyproject.toml index 6e789b6f..fc1cdce2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ version = "2.0.1" description = "A simple API wrapper for Top.gg written in Python." readme = "README.rst" license = { text = "MIT" } -authors = [{ name = "Assanali Mukhanov" }, { name = "Norizon" }, { name = "Top.gg" }] +authors = [{ name = "null8626" }, { name = "Top.gg" }] keywords = ["discord", "bot", "topgg", "top.gg"] dependencies = ["aiohttp>=3.9.0"] classifiers = [ diff --git a/topgg/__init__.py b/topgg/__init__.py index eed4b284..d54460a1 100644 --- a/topgg/__init__.py +++ b/topgg/__init__.py @@ -5,12 +5,12 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~ A basic wrapper for the Top.gg API. :copyright: (c) 2021 Assanali Mukhanov & Top.gg +:copyright: (c) 2024 null & Top.gg :license: MIT, see LICENSE for more details. """ __title__ = "topggpy" -__author__ = "Assanali Mukhanov" -__maintainer__ = "Norizon" +__author__ = "null8626" __license__ = "MIT" __version__ = "2.0.1" diff --git a/topgg/py.typed b/topgg/py.typed deleted file mode 100644 index e69de29b..00000000 From 533295ae1b11c70b9da84b2422edc426f061f499 Mon Sep 17 00:00:00 2001 From: null8626 Date: Wed, 27 Mar 2024 12:37:24 +0700 Subject: [PATCH 12/71] doc: trauma --- README.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.rst b/README.rst index 8860689d..041b963a 100644 --- a/README.rst +++ b/README.rst @@ -17,18 +17,10 @@ A simple API wrapper for `Top.gg `_ written in Python, supporti Installation ------------ -Install via pip (recommended) - .. code:: bash pip3 install topggpy -Install from source - -.. code:: bash - - pip3 install git+https://github.com/top-gg-community/python-sdk/ - Documentation ------------- From df9b489ad9b6c938d0958e1d93288cca09b2eb77 Mon Sep 17 00:00:00 2001 From: null8626 Date: Wed, 27 Mar 2024 13:07:46 +0700 Subject: [PATCH 13/71] meta: remove duplicated path in .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1d889220..41a61499 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ topggpy.egg-info/ -topggpy.egg-info/ topgg/__pycache__/ build/ dist/ From eda566e9234883ed0ca9d8803bda301a4092d425 Mon Sep 17 00:00:00 2001 From: null8626 Date: Wed, 27 Mar 2024 13:31:06 +0700 Subject: [PATCH 14/71] meta: update LICENSE --- LICENSE | 2 +- topgg/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 066ec504..c4cd04ce 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ Copyright 2021 Assanali Mukhanov & Top.gg -Copyright 2024 null & Top.gg +Copyright 2024 null8626 & Top.gg 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: diff --git a/topgg/__init__.py b/topgg/__init__.py index d54460a1..3c5f8451 100644 --- a/topgg/__init__.py +++ b/topgg/__init__.py @@ -5,7 +5,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~ A basic wrapper for the Top.gg API. :copyright: (c) 2021 Assanali Mukhanov & Top.gg -:copyright: (c) 2024 null & Top.gg +:copyright: (c) 2024 null8626 & Top.gg :license: MIT, see LICENSE for more details. """ From e1460488291baa81cced9330072f66680e5a5e30 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 07:55:47 +0700 Subject: [PATCH 15/71] *: use ruff, remove need for default_bot_id, deprecate get_bots, make def_avatar optional --- .gitattributes | 1 + .github/workflows/python-package.yml | 7 +- .isort.cfg | 3 - MANIFEST.in | 19 +- docs/whats_new.rst | 12 ++ examples/discordpy_example/__main__.py | 1 - examples/hikari_example/__main__.py | 1 - examples/hikari_example/callbacks/autopost.py | 5 +- examples/hikari_example/callbacks/webhook.py | 1 + requirements-dev.txt | 22 +-- ruff.toml | 9 + tests/test_autopost.py | 176 +++++++++--------- tests/test_client.py | 48 ++--- tests/test_data_container.py | 16 +- tests/test_ratelimiter.py | 56 +++--- tests/test_type.py | 19 +- tests/test_webhook.py | 156 ++++++++-------- topgg/autopost.py | 42 ++--- topgg/client.py | 122 ++++-------- topgg/data.py | 18 +- topgg/http.py | 29 ++- topgg/ratelimiter.py | 7 +- topgg/types.py | 2 +- topgg/webhook.py | 59 +++--- 24 files changed, 352 insertions(+), 479 deletions(-) create mode 100644 .gitattributes delete mode 100644 .isort.cfg create mode 100644 ruff.toml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..44b4224b --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* eol=lf \ No newline at end of file diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c24b9baf..473cba85 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -30,12 +30,9 @@ jobs: - name: Install itself run: | python -m pip install . - - name: Lint with flake8 + - name: Lint with ruff run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics + ruff check - name: Test with pytest run: | pytest diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 317f1d34..00000000 --- a/.isort.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[settings] -profile=black -multi_line_output=3 \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index dc068afa..5dec6b10 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,16 @@ -include LICENSE -include requirements.txt -include README.rst +prune .github +prune .ruff_cache +prune docs +prune examples +prune scripts +prune tests +exclude .gitattributes +exclude .gitignore +exclude .readthedocs.yml +exclude mypy.ini +exclude pytest.ini +exclude requirements-dev.txt +exclude requirements-docs.txt +exclude ruff.toml +exclude ISSUE_TEMPLATE.md +exclude PULL_REQUEST_TEMPLATE.md \ No newline at end of file diff --git a/docs/whats_new.rst b/docs/whats_new.rst index 8fc1d0e9..23e3ffe3 100644 --- a/docs/whats_new.rst +++ b/docs/whats_new.rst @@ -8,6 +8,18 @@ What's New This page keeps a detailed human friendly rendering of what's new and changed in specific versions. +v2.0.1 +====== +* Added Python 3.12 support +* Dropped Python 3.6 and 3.7 support +* Removed the need to manually set a ``default_bot_id`` property +* :attr:`BotData.def_avatar` is now an optional string +* :meth:`DBLClient.get_bots` is now deprecated +* :meth:`DBLClient.get_guild_count` no longer accepts a ``bot_id`` argument +* :meth:`DBLClient.get_bot_votes` no longer raises a ``ClientException`` without a ``default_bot_id`` property +* :meth:`DBLClient.get_bot_info` no longer raises a ``ClientException`` without a ``default_bot_id`` property +* :meth:`DBLClient.generate_widget`` no longer raises a ``ClientException`` without a ``default_bot_id`` property + v2.0.0a ======= * :obj:`~.DBLClient` now doesn't take in ``discord.Client`` instance diff --git a/examples/discordpy_example/__main__.py b/examples/discordpy_example/__main__.py index f1c1f6dd..67e58232 100644 --- a/examples/discordpy_example/__main__.py +++ b/examples/discordpy_example/__main__.py @@ -39,7 +39,6 @@ @client.event async def on_ready(): assert client.user is not None - dblclient.default_bot_id = client.user.id # if it's ready, then the event loop's run, # hence it's safe starting the autopost here diff --git a/examples/hikari_example/__main__.py b/examples/hikari_example/__main__.py index 0bef502f..857329c8 100644 --- a/examples/hikari_example/__main__.py +++ b/examples/hikari_example/__main__.py @@ -40,7 +40,6 @@ async def on_started(event: hikari.StartedEvent): me: hikari.OwnUser = event.app.get_me() assert me is not None - dblclient.default_bot_id = me.id # since StartedEvent is a lifetime event # this event will only get dispatched once diff --git a/examples/hikari_example/callbacks/autopost.py b/examples/hikari_example/callbacks/autopost.py index 3ac467b3..6bab89f2 100644 --- a/examples/hikari_example/callbacks/autopost.py +++ b/examples/hikari_example/callbacks/autopost.py @@ -29,6 +29,7 @@ _LOGGER = logging.getLogger("callbacks.autopost") + # these functions can be async too! def on_autopost_success( # uncomment this if you want to get access to app @@ -56,6 +57,4 @@ def on_autopost_error( def stats(app: hikari.GatewayBot = topgg.data(hikari.GatewayBot)): - return topgg.StatsWrapper( - guild_count=len(app.cache.get_guilds_view()), shard_count=app.shard_count - ) + return topgg.StatsWrapper(guild_count=len(app.cache.get_guilds_view()), shard_count=app.shard_count) diff --git a/examples/hikari_example/callbacks/webhook.py b/examples/hikari_example/callbacks/webhook.py index 50c53a73..49daedaa 100644 --- a/examples/hikari_example/callbacks/webhook.py +++ b/examples/hikari_example/callbacks/webhook.py @@ -31,6 +31,7 @@ _LOGGER = logging.getLogger("callbacks.webhook") + # this can be async too! @topgg.endpoint("/dblwebhook", topgg.WebhookType.BOT, "youshallnotpass") async def endpoint( diff --git a/requirements-dev.txt b/requirements-dev.txt index e5d5d951..36ad3056 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,13 +1,9 @@ -# Formatting -git+https://github.com/timothycrosley/isort -git+https://github.com/psf/black - -# Unit Testing -mock -pytest -pytest-asyncio -pytest-mock -pytest-cov - -# Linting -flake8 +# Formatting and Linting +ruff + +# Unit Testing +mock +pytest +pytest-asyncio +pytest-mock +pytest-cov \ No newline at end of file diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..650555f8 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,9 @@ +line-length = 120 + +[format] +docstring-code-format = true +docstring-code-line-length = 120 +line-ending = "lf" + +[lint] +ignore = ["E722", "F401", "F403"] \ No newline at end of file diff --git a/tests/test_autopost.py b/tests/test_autopost.py index 7cf39c14..07d60a50 100644 --- a/tests/test_autopost.py +++ b/tests/test_autopost.py @@ -1,92 +1,84 @@ -import datetime - -import mock -import pytest -from aiohttp import ClientSession -from pytest_mock import MockerFixture - -from topgg import DBLClient -from topgg.autopost import AutoPoster -from topgg.errors import ServerError, TopGGException, Unauthorized - - -@pytest.fixture -def session() -> ClientSession: - return mock.Mock(ClientSession) - - -@pytest.fixture -def autopost(session: ClientSession) -> AutoPoster: - return AutoPoster(DBLClient("", session=session)) - - -@pytest.mark.asyncio -async def test_AutoPoster_breaks_autopost_loop_on_401( - mocker: MockerFixture, session: ClientSession -) -> None: - response = mock.Mock("reason, status") - response.reason = "Unauthorized" - response.status = 401 - - mocker.patch( - "topgg.DBLClient.post_guild_count", side_effect=Unauthorized(response, {}) - ) - - callback = mock.Mock() - autopost = DBLClient("", session=session).autopost().stats(callback) - assert isinstance(autopost, AutoPoster) - assert not isinstance(autopost.stats()(callback), AutoPoster) - - with pytest.raises(Unauthorized): - await autopost.start() - - callback.assert_called_once() - assert not autopost.is_running - - -@pytest.mark.asyncio -async def test_AutoPoster_raises_missing_stats(autopost: AutoPoster) -> None: - with pytest.raises( - TopGGException, match="you must provide a callback that returns the stats." - ): - await autopost.start() - - -@pytest.mark.asyncio -async def test_AutoPoster_raises_already_running(autopost: AutoPoster) -> None: - autopost.stats(mock.Mock()).start() - with pytest.raises(TopGGException, match="the autopost is already running."): - await autopost.start() - - -@pytest.mark.asyncio -async def test_AutoPoster_interval_too_short(autopost: AutoPoster) -> None: - with pytest.raises(ValueError, match="interval must be greated than 900 seconds."): - autopost.set_interval(50) - - -@pytest.mark.asyncio -async def test_AutoPoster_error_callback( - mocker: MockerFixture, autopost: AutoPoster -) -> None: - error_callback = mock.Mock() - response = mock.Mock("reason, status") - response.reason = "Internal Server Error" - response.status = 500 - side_effect = ServerError(response, {}) - - mocker.patch("topgg.DBLClient.post_guild_count", side_effect=side_effect) - task = autopost.on_error(error_callback).stats(mock.Mock()).start() - autopost.stop() - await task - error_callback.assert_called_once_with(side_effect) - - -def test_AutoPoster_interval(autopost: AutoPoster): - assert autopost.interval == 900 - autopost.set_interval(datetime.timedelta(hours=1)) - assert autopost.interval == 3600 - autopost.interval = datetime.timedelta(hours=2) - assert autopost.interval == 7200 - autopost.interval = 3600 - assert autopost.interval == 3600 +import datetime + +import mock +import pytest +from aiohttp import ClientSession +from pytest_mock import MockerFixture + +from topgg import DBLClient +from topgg.autopost import AutoPoster +from topgg.errors import ServerError, TopGGException, Unauthorized + + +@pytest.fixture +def session() -> ClientSession: + return mock.Mock(ClientSession) + + +@pytest.fixture +def autopost(session: ClientSession) -> AutoPoster: + return AutoPoster(DBLClient("", session=session)) + + +@pytest.mark.asyncio +async def test_AutoPoster_breaks_autopost_loop_on_401(mocker: MockerFixture, session: ClientSession) -> None: + response = mock.Mock("reason, status") + response.reason = "Unauthorized" + response.status = 401 + + mocker.patch("topgg.DBLClient.post_guild_count", side_effect=Unauthorized(response, {})) + + callback = mock.Mock() + autopost = DBLClient("", session=session).autopost().stats(callback) + assert isinstance(autopost, AutoPoster) + assert not isinstance(autopost.stats()(callback), AutoPoster) + + with pytest.raises(Unauthorized): + await autopost.start() + + callback.assert_called_once() + assert not autopost.is_running + + +@pytest.mark.asyncio +async def test_AutoPoster_raises_missing_stats(autopost: AutoPoster) -> None: + with pytest.raises(TopGGException, match="you must provide a callback that returns the stats."): + await autopost.start() + + +@pytest.mark.asyncio +async def test_AutoPoster_raises_already_running(autopost: AutoPoster) -> None: + autopost.stats(mock.Mock()).start() + with pytest.raises(TopGGException, match="the autopost is already running."): + await autopost.start() + + +@pytest.mark.asyncio +async def test_AutoPoster_interval_too_short(autopost: AutoPoster) -> None: + with pytest.raises(ValueError, match="interval must be greated than 900 seconds."): + autopost.set_interval(50) + + +@pytest.mark.asyncio +async def test_AutoPoster_error_callback(mocker: MockerFixture, autopost: AutoPoster) -> None: + error_callback = mock.Mock() + response = mock.Mock("reason, status") + response.reason = "Internal Server Error" + response.status = 500 + side_effect = ServerError(response, {}) + + mocker.patch("topgg.DBLClient.post_guild_count", side_effect=side_effect) + task = autopost.on_error(error_callback).stats(mock.Mock()).start() + autopost.stop() + await task + error_callback.assert_called_once_with(side_effect) + + +def test_AutoPoster_interval(autopost: AutoPoster): + assert autopost.interval == 900 + autopost.set_interval(datetime.timedelta(hours=1)) + assert autopost.interval == 3600 + autopost.interval = datetime.timedelta(hours=2) + assert autopost.interval == 7200 + autopost.interval = 3600 + assert autopost.interval == 3600 diff --git a/tests/test_client.py b/tests/test_client.py index fb634ead..fbd7a196 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,5 +1,3 @@ -import typing as t - import mock import pytest from aiohttp import ClientSession @@ -8,6 +6,9 @@ from topgg import errors +MOCK_TOKEN = "amogus.eyJpZCI6IjEwMjY1MjU1NjgzNDQyNjQ3MjQiLCJib3QiOnRydWUsImlhdCI6MTY5OTk4NDYyM30.amogus" + + @pytest.fixture def session() -> ClientSession: return mock.Mock(ClientSession) @@ -15,14 +16,14 @@ def session() -> ClientSession: @pytest.fixture def client() -> topgg.DBLClient: - client = topgg.DBLClient(token="TOKEN", default_bot_id=1234) + client = topgg.DBLClient(token=MOCK_TOKEN) client.http = mock.Mock(topgg.http.HTTPClient) return client @pytest.mark.asyncio async def test_HTTPClient_with_external_session(session: ClientSession): - http = topgg.http.HTTPClient("TOKEN", session=session) + http = topgg.http.HTTPClient(MOCK_TOKEN, session=session) assert not http._own_session await http.close() session.close.assert_not_called() @@ -30,26 +31,16 @@ async def test_HTTPClient_with_external_session(session: ClientSession): @pytest.mark.asyncio async def test_HTTPClient_with_no_external_session(session: ClientSession): - http = topgg.http.HTTPClient("TOKEN") + http = topgg.http.HTTPClient(MOCK_TOKEN) http.session = session assert http._own_session await http.close() session.close.assert_called_once() -@pytest.mark.asyncio -async def test_DBLClient_get_bot_votes_with_no_default_bot_id(): - client = topgg.DBLClient("TOKEN") - with pytest.raises( - errors.ClientException, - match="you must set default_bot_id when constructing the client.", - ): - await client.get_bot_votes() - - @pytest.mark.asyncio async def test_DBLClient_post_guild_count_with_no_args(): - client = topgg.DBLClient("TOKEN", default_bot_id=1234) + client = topgg.DBLClient(MOCK_TOKEN) with pytest.raises(TypeError, match="stats or guild_count must be provided."): await client.post_guild_count() @@ -67,20 +58,9 @@ async def test_DBLClient_post_guild_count_with_no_args(): ), ], ) -@pytest.mark.asyncio -async def test_DBLClient_get_guild_count_with_no_id( - method: t.Callable, kwargs: t.Dict[str, t.Any] -): - client = topgg.DBLClient("TOKEN") - with pytest.raises( - errors.ClientException, match="bot_id or default_bot_id is unset." - ): - await method(client, **kwargs) - - @pytest.mark.asyncio async def test_closed_DBLClient_raises_exception(): - client = topgg.DBLClient("TOKEN") + client = topgg.DBLClient(MOCK_TOKEN) assert not client.is_closed await client.close() assert client.is_closed @@ -88,6 +68,11 @@ async def test_closed_DBLClient_raises_exception(): await client.get_weekend_status() +@pytest.mark.asyncio +async def test_closed_DBLClient_bot_id(): + assert client.bot_id == 1026525568344264724 + + @pytest.mark.asyncio async def test_DBLClient_get_weekend_status(client: topgg.DBLClient): client.http.get_weekend_status = mock.AsyncMock() @@ -116,13 +101,6 @@ async def test_DBLClient_get_bot_votes(client: topgg.DBLClient): client.http.get_bot_votes.assert_called_once() -@pytest.mark.asyncio -async def test_DBLClient_get_bots(client: topgg.DBLClient): - client.http.get_bots = mock.AsyncMock(return_value={"results": []}) - await client.get_bots() - client.http.get_bots.assert_called_once() - - @pytest.mark.asyncio async def test_DBLClient_get_user_info(client: topgg.DBLClient): client.http.get_user_info = mock.AsyncMock(return_value={}) diff --git a/tests/test_data_container.py b/tests/test_data_container.py index 978574fb..f89466ea 100644 --- a/tests/test_data_container.py +++ b/tests/test_data_container.py @@ -13,20 +13,13 @@ def data_container() -> DataContainerMixin: return dc -async def _async_callback( - text: str = data(str), number: int = data(int), mapping: dict = data(dict) -): - ... +async def _async_callback(text: str = data(str), number: int = data(int), mapping: dict = data(dict)): ... -def _sync_callback( - text: str = data(str), number: int = data(int), mapping: dict = data(dict) -): - ... +def _sync_callback(text: str = data(str), number: int = data(int), mapping: dict = data(dict)): ... -def _invalid_callback(number: float = data(float)): - ... +def _invalid_callback(number: float = data(float)): ... @pytest.mark.asyncio @@ -42,8 +35,7 @@ async def test_data_container_invoke_sync_callback(data_container: DataContainer def test_data_container_raises_data_already_exists(data_container: DataContainerMixin): with pytest.raises( TopGGException, - match=" already exists. If you wish to override it, " - "pass True into the override parameter.", + match=" already exists. If you wish to override it, " "pass True into the override parameter.", ): data_container.set_data("TEST") diff --git a/tests/test_ratelimiter.py b/tests/test_ratelimiter.py index f1fbed6b..9153b3a1 100644 --- a/tests/test_ratelimiter.py +++ b/tests/test_ratelimiter.py @@ -1,28 +1,28 @@ -import pytest - -from topgg.ratelimiter import AsyncRateLimiter - -n = period = 10 - - -@pytest.fixture -def limiter() -> AsyncRateLimiter: - return AsyncRateLimiter(max_calls=n, period=period) - - -@pytest.mark.asyncio -async def test_AsyncRateLimiter_calls(limiter: AsyncRateLimiter) -> None: - for _ in range(n): - async with limiter: - pass - - assert len(limiter.calls) == limiter.max_calls == n - - -@pytest.mark.asyncio -async def test_AsyncRateLimiter_timespan_property(limiter: AsyncRateLimiter) -> None: - for _ in range(n): - async with limiter: - pass - - assert limiter._timespan < period +import pytest + +from topgg.ratelimiter import AsyncRateLimiter + +n = period = 10 + + +@pytest.fixture +def limiter() -> AsyncRateLimiter: + return AsyncRateLimiter(max_calls=n, period=period) + + +@pytest.mark.asyncio +async def test_AsyncRateLimiter_calls(limiter: AsyncRateLimiter) -> None: + for _ in range(n): + async with limiter: + pass + + assert len(limiter.calls) == limiter.max_calls == n + + +@pytest.mark.asyncio +async def test_AsyncRateLimiter_timespan_property(limiter: AsyncRateLimiter) -> None: + for _ in range(n): + async with limiter: + pass + + assert limiter._timespan < period diff --git a/tests/test_type.py b/tests/test_type.py index caec363c..8cea66c5 100644 --- a/tests/test_type.py +++ b/tests/test_type.py @@ -143,12 +143,7 @@ def test_widget_options_fields(widget_options: types.WidgetOptions) -> None: for attr in widget_options: if "id" in attr.lower(): assert isinstance(widget_options[attr], int) or widget_options[attr] is None - assert ( - widget_options.get(attr) - == widget_options[attr] - == widget_options[attr] - == getattr(widget_options, attr) - ) + assert widget_options.get(attr) == widget_options[attr] == widget_options[attr] == getattr(widget_options, attr) def test_vote_data_fields(vote_data: types.VoteDataDict) -> None: @@ -165,11 +160,7 @@ def test_bot_vote_data_fields(bot_vote_data: types.BotVoteData) -> None: assert isinstance(bot_vote_data["bot"], int) for attr in bot_vote_data: - assert ( - getattr(bot_vote_data, attr) - == bot_vote_data.get(attr) - == bot_vote_data[attr] - ) + assert getattr(bot_vote_data, attr) == bot_vote_data.get(attr) == bot_vote_data[attr] def test_server_vote_data_fields(server_vote_data: types.BotVoteData) -> None: @@ -178,11 +169,7 @@ def test_server_vote_data_fields(server_vote_data: types.BotVoteData) -> None: assert isinstance(server_vote_data["guild"], int) for attr in server_vote_data: - assert ( - getattr(server_vote_data, attr) - == server_vote_data.get(attr) - == server_vote_data[attr] - ) + assert getattr(server_vote_data, attr) == server_vote_data.get(attr) == server_vote_data[attr] def test_bot_stats_data_attrs(bot_stats_data: types.BotStatsData) -> None: diff --git a/tests/test_webhook.py b/tests/test_webhook.py index 8ef3c71d..db1da09d 100644 --- a/tests/test_webhook.py +++ b/tests/test_webhook.py @@ -1,80 +1,76 @@ -import typing as t - -import aiohttp -import mock -import pytest - -from topgg import WebhookManager, WebhookType -from topgg.errors import TopGGException - -auth = "youshallnotpass" - - -@pytest.fixture -def webhook_manager() -> WebhookManager: - return ( - WebhookManager() - .endpoint() - .type(WebhookType.BOT) - .auth(auth) - .route("/dbl") - .callback(print) - .add_to_manager() - .endpoint() - .type(WebhookType.GUILD) - .auth(auth) - .route("/dsl") - .callback(print) - .add_to_manager() - ) - - -def test_WebhookManager_routes(webhook_manager: WebhookManager) -> None: - assert len(webhook_manager.app.router.routes()) == 2 - - -@pytest.mark.asyncio -@pytest.mark.parametrize( - "headers, result, state", - [({"authorization": auth}, 200, True), ({}, 401, False)], -) -async def test_WebhookManager_validates_auth( - webhook_manager: WebhookManager, headers: t.Dict[str, str], result: int, state: bool -) -> None: - await webhook_manager.start(5000) - - try: - for path in ("dbl", "dsl"): - async with aiohttp.request( - "POST", f"http://localhost:5000/{path}", headers=headers, json={} - ) as r: - assert r.status == result - finally: - await webhook_manager.close() - assert not webhook_manager.is_running - - -def test_WebhookEndpoint_callback_unset(webhook_manager: WebhookManager): - with pytest.raises( - TopGGException, - match="endpoint missing callback.", - ): - webhook_manager.endpoint().add_to_manager() - - -def test_WebhookEndpoint_route_unset(webhook_manager: WebhookManager): - with pytest.raises( - TopGGException, - match="endpoint missing type.", - ): - webhook_manager.endpoint().callback(mock.Mock()).add_to_manager() - - -def test_WebhookEndpoint_type_unset(webhook_manager: WebhookManager): - with pytest.raises( - TopGGException, - match="endpoint missing route.", - ): - webhook_manager.endpoint().callback(mock.Mock()).type( - WebhookType.BOT - ).add_to_manager() +import typing as t + +import aiohttp +import mock +import pytest + +from topgg import WebhookManager, WebhookType +from topgg.errors import TopGGException + +auth = "youshallnotpass" + + +@pytest.fixture +def webhook_manager() -> WebhookManager: + return ( + WebhookManager() + .endpoint() + .type(WebhookType.BOT) + .auth(auth) + .route("/dbl") + .callback(print) + .add_to_manager() + .endpoint() + .type(WebhookType.GUILD) + .auth(auth) + .route("/dsl") + .callback(print) + .add_to_manager() + ) + + +def test_WebhookManager_routes(webhook_manager: WebhookManager) -> None: + assert len(webhook_manager.app.router.routes()) == 2 + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "headers, result, state", + [({"authorization": auth}, 200, True), ({}, 401, False)], +) +async def test_WebhookManager_validates_auth( + webhook_manager: WebhookManager, headers: t.Dict[str, str], result: int, state: bool +) -> None: + await webhook_manager.start(5000) + + try: + for path in ("dbl", "dsl"): + async with aiohttp.request("POST", f"http://localhost:5000/{path}", headers=headers, json={}) as r: + assert r.status == result + finally: + await webhook_manager.close() + assert not webhook_manager.is_running + + +def test_WebhookEndpoint_callback_unset(webhook_manager: WebhookManager): + with pytest.raises( + TopGGException, + match="endpoint missing callback.", + ): + webhook_manager.endpoint().add_to_manager() + + +def test_WebhookEndpoint_route_unset(webhook_manager: WebhookManager): + with pytest.raises( + TopGGException, + match="endpoint missing type.", + ): + webhook_manager.endpoint().callback(mock.Mock()).add_to_manager() + + +def test_WebhookEndpoint_type_unset(webhook_manager: WebhookManager): + with pytest.raises( + TopGGException, + match="endpoint missing route.", + ): + webhook_manager.endpoint().callback(mock.Mock()).type(WebhookType.BOT).add_to_manager() diff --git a/topgg/autopost.py b/topgg/autopost.py index 3bfe4afa..96aa9888 100644 --- a/topgg/autopost.py +++ b/topgg/autopost.py @@ -78,17 +78,13 @@ def __init__(self, client: "DBLClient") -> None: def _default_error_handler(self, exception: Exception) -> None: print("Ignoring exception in auto post loop:", file=sys.stderr) - traceback.print_exception( - type(exception), exception, exception.__traceback__, file=sys.stderr - ) + traceback.print_exception(type(exception), exception, exception.__traceback__, file=sys.stderr) @t.overload - def on_success(self, callback: None) -> t.Callable[[CallbackT], CallbackT]: - ... + def on_success(self, callback: None) -> t.Callable[[CallbackT], CallbackT]: ... @t.overload - def on_success(self, callback: CallbackT) -> "AutoPoster": - ... + def on_success(self, callback: CallbackT) -> "AutoPoster": ... def on_success(self, callback: t.Any = None) -> t.Any: """ @@ -103,15 +99,15 @@ def on_success(self, callback: t.Any = None) -> t.Any: # The following are valid. autopost = dblclient.autopost().on_success(lambda: print("Success!")) + # Used as decorator, the decorated function will become the AutoPoster object. @autopost.on_success - def autopost(): - ... + def autopost(): ... + # Used as decorator factory, the decorated function will still be the function itself. @autopost.on_success() - def on_success(): - ... + def on_success(): ... """ if callback is not None: self._success = callback @@ -124,12 +120,10 @@ def decorator(callback: CallbackT) -> CallbackT: return decorator @t.overload - def on_error(self, callback: None) -> t.Callable[[CallbackT], CallbackT]: - ... + def on_error(self, callback: None) -> t.Callable[[CallbackT], CallbackT]: ... @t.overload - def on_error(self, callback: CallbackT) -> "AutoPoster": - ... + def on_error(self, callback: CallbackT) -> "AutoPoster": ... def on_error(self, callback: t.Any = None) -> t.Any: """ @@ -148,15 +142,15 @@ def on_error(self, callback: t.Any = None) -> t.Any: # The following are valid. autopost = dblclient.autopost().on_error(lambda exc: print("Failed posting stats!", exc)) + # Used as decorator, the decorated function will become the AutoPoster object. @autopost.on_error - def autopost(exc: Exception): - ... + def autopost(exc: Exception): ... + # Used as decorator factory, the decorated function will still be the function itself. @autopost.on_error() - def on_error(exc: Exception): - ... + def on_error(exc: Exception): ... """ if callback is not None: self._error = callback @@ -169,12 +163,10 @@ def decorator(callback: CallbackT) -> CallbackT: return decorator @t.overload - def stats(self, callback: None) -> t.Callable[[StatsCallbackT], StatsCallbackT]: - ... + def stats(self, callback: None) -> t.Callable[[StatsCallbackT], StatsCallbackT]: ... @t.overload - def stats(self, callback: StatsCallbackT) -> "AutoPoster": - ... + def stats(self, callback: StatsCallbackT) -> "AutoPoster": ... def stats(self, callback: t.Any = None) -> t.Any: """ @@ -295,9 +287,7 @@ def start(self) -> "asyncio.Task[None]": If there's no callback provided or the autopost is already running. """ if not hasattr(self, "_stats"): - raise errors.TopGGException( - "you must provide a callback that returns the stats." - ) + raise errors.TopGGException("you must provide a callback that returns the stats.") if self.is_running: raise errors.TopGGException("the autopost is already running.") diff --git a/topgg/client.py b/topgg/client.py index 0f1a72db..ff7717eb 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -3,6 +3,7 @@ # The MIT License (MIT) # Copyright (c) 2021 Assanali Mukhanov +# Copyright (c) 2024 null8626 # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -24,7 +25,11 @@ __all__ = ["DBLClient"] +import base64 +import json +import re import typing as t +import warnings import aiohttp @@ -45,28 +50,35 @@ class DBLClient(DataContainerMixin): token (:obj:`str`): Your bot's Top.gg API Token. Keyword Args: - default_bot_id (:obj:`typing.Optional` [ :obj:`int` ]) - The default bot_id. You can override this by passing it when calling a method. session (:class:`aiohttp.ClientSession`) An `aiohttp session`_ to use for requests to the API. **kwargs: Arbitrary kwargs to be passed to :class:`aiohttp.ClientSession` if session was not provided. """ - __slots__ = ("http", "default_bot_id", "_token", "_is_closed", "_autopost") + __slots__ = ("http", "bot_id", "_token", "_is_closed", "_autopost") http: HTTPClient def __init__( self, token: str, *, - default_bot_id: t.Optional[int] = None, session: t.Optional[aiohttp.ClientSession] = None, **kwargs: t.Any, ) -> None: super().__init__() self._token = token - self.default_bot_id = default_bot_id + + try: + encoded_json = re.sub(r"[^a-zA-Z0-9\+\/]+", "", token.split(".")[1]) + missing_padding = len(encoded_json) % 4 + if missing_padding: + encoded_json += "=" * (4 - missing_padding) + + self.bot_id = int(json.loads(base64.b64decode(encoded_json))["id"]) + except: + raise errors.ClientException("invalid token.") + self._is_closed = False if session is not None: self.http = HTTPClient(token, session=session) @@ -83,13 +95,6 @@ async def _ensure_session(self) -> None: if not hasattr(self, "http"): self.http = HTTPClient(self._token, session=None) - def _validate_and_get_bot_id(self, bot_id: t.Optional[int]) -> int: - bot_id = bot_id or self.default_bot_id - if bot_id is None: - raise errors.ClientException("bot_id or default_bot_id is unset.") - - return bot_id - async def get_weekend_status(self) -> bool: """Gets weekend status from Top.gg. @@ -105,8 +110,7 @@ async def get_weekend_status(self) -> bool: return data["is_weekend"] @t.overload - async def post_guild_count(self, stats: types.StatsWrapper) -> None: - ... + async def post_guild_count(self, stats: types.StatsWrapper) -> None: ... @t.overload async def post_guild_count( @@ -115,8 +119,7 @@ async def post_guild_count( guild_count: t.Union[int, t.List[int]], shard_count: t.Optional[int] = None, shard_id: t.Optional[int] = None, - ) -> None: - ... + ) -> None: ... async def post_guild_count( self, @@ -161,28 +164,19 @@ async def post_guild_count( await self._ensure_session() await self.http.post_guild_count(guild_count, shard_count, shard_id) - async def get_guild_count( - self, bot_id: t.Optional[int] = None - ) -> types.BotStatsData: - """Gets a bot's guild count and shard info from Top.gg. - - Args: - bot_id (int) - ID of the bot you want to look up. Defaults to the provided Client object. + async def get_guild_count(self) -> types.BotStatsData: + """Gets this bot's guild count and shard info from Top.gg. Returns: :obj:`~.types.BotStatsData`: The guild count and shards of a bot on Top.gg. Raises: - :obj:`~.errors.ClientException` - If neither bot_id or default_bot_id was set. :obj:`~.errors.ClientStateException` If the client has been closed. """ - bot_id = self._validate_and_get_bot_id(bot_id) await self._ensure_session() - response = await self.http.get_guild_count(bot_id) + response = await self.http.get_guild_count(self.bot_id) return types.BotStatsData(**response) async def get_bot_votes(self) -> t.List[types.BriefUserData]: @@ -196,17 +190,11 @@ async def get_bot_votes(self) -> t.List[types.BriefUserData]: Users who voted for your bot. Raises: - :obj:`~.errors.ClientException` - If default_bot_id isn't provided when constructing the client. :obj:`~.errors.ClientStateException` If the client has been closed. """ - if not self.default_bot_id: - raise errors.ClientException( - "you must set default_bot_id when constructing the client." - ) await self._ensure_session() - response = await self.http.get_bot_votes(self.default_bot_id) + response = await self.http.get_bot_votes(self.bot_id) return [types.BriefUserData(**user) for user in response] async def get_bot_info(self, bot_id: t.Optional[int] = None) -> types.BotData: @@ -216,7 +204,7 @@ async def get_bot_info(self, bot_id: t.Optional[int] = None) -> types.BotData: Args: bot_id (int) - ID of the bot to look up. Defaults to the provided Client object. + ID of the bot to look up. Defaults to this bot's ID. Returns: :obj:`~.types.BotData`: @@ -224,14 +212,11 @@ async def get_bot_info(self, bot_id: t.Optional[int] = None) -> types.BotData: `here `_. Raises: - :obj:`~.errors.ClientException` - If neither bot_id or default_bot_id was set. :obj:`~.errors.ClientStateException` If the client has been closed. """ - bot_id = self._validate_and_get_bot_id(bot_id) await self._ensure_session() - response = await self.http.get_bot_info(bot_id) + response = await self.http.get_bot_info(bot_id or self.bot_id) return types.BotData(**response) async def get_bots( @@ -242,38 +227,16 @@ async def get_bots( search: t.Optional[t.Dict[str, t.Any]] = None, fields: t.Optional[t.List[str]] = None, ) -> types.DataDict[str, t.Any]: - """This function is a coroutine. - - Gets information about listed bots on Top.gg. - - Args: - limit (int) - The number of results to look up. Defaults to 50. Max 500 allowed. - offset (int) - The amount of bots to skip. Defaults to 0. - sort (str) - The field to sort by. Prefix with ``-`` to reverse the order. - search (:obj:`dict` [ :obj:`str`, :obj:`typing.Any` ]) - The search data. - fields (:obj:`list` [ :obj:`str` ]) - Fields to output. - - Returns: - :obj:`~.types.DataDict`: - Info on bots that match the search query on Top.gg. - - Raises: - :obj:`~.errors.ClientStateException` - If the client has been closed. - """ + """This function is deprecated.""" + + warnings.warn("get_bots is now deprecated.", DeprecationWarning) + sort = sort or "" search = search or {} fields = fields or [] await self._ensure_session() response = await self.http.get_bots(limit, offset, sort, search, fields) - response["results"] = [ - types.BotData(**bot_data) for bot_data in response["results"] - ] + response["results"] = [types.BotData(**bot_data) for bot_data in response["results"]] return types.DataDict(**response) async def get_user_info(self, user_id: int) -> types.UserData: @@ -308,18 +271,11 @@ async def get_user_vote(self, user_id: int) -> bool: :obj:`bool`: Info about the user's vote. Raises: - :obj:`~.errors.ClientException` - If default_bot_id isn't provided when constructing the client. :obj:`~.errors.ClientStateException` If the client has been closed. """ - if not self.default_bot_id: - raise errors.ClientException( - "you must set default_bot_id when constructing the client." - ) - await self._ensure_session() - data = await self.http.get_user_vote(self.default_bot_id, user_id) + data = await self.http.get_user_vote(self.bot_id, user_id) return bool(data["voted"]) def generate_widget(self, *, options: types.WidgetOptions) -> str: @@ -334,28 +290,22 @@ def generate_widget(self, *, options: types.WidgetOptions) -> str: str: Generated widget URL. Raises: - :obj:`~.errors.ClientException` - If bot_id or default_bot_id is unset. TypeError: If options passed is not of type WidgetOptions. """ if not isinstance(options, types.WidgetOptions): - raise TypeError( - "options argument passed to generate_widget must be of type WidgetOptions" - ) - - bot_id = options.id or self.default_bot_id - if bot_id is None: - raise errors.ClientException("bot_id or default_bot_id is unset.") + raise TypeError("options argument passed to generate_widget must be of type WidgetOptions") + bot_id = options.id or self.bot_id widget_query = f"noavatar={str(options.noavatar).lower()}" + for key, value in options.colors.items(): widget_query += f"&{key.lower()}{'' if key.lower().endswith('color') else 'color'}={value:x}" + widget_format = options.format widget_type = f"/{options.type}" if options.type else "" - url = f"""https://top.gg/api/widget{widget_type}/{bot_id}.{widget_format}?{widget_query}""" - return url + return f"https://top.gg/api/widget{widget_type}/{bot_id}.{widget_format}?{widget_query}" async def close(self) -> None: """Closes all connections.""" diff --git a/topgg/data.py b/topgg/data.py index 7126d3bf..baafde66 100644 --- a/topgg/data.py +++ b/topgg/data.py @@ -52,6 +52,7 @@ def data(type_: t.Type[T]) -> T: dblclient = topgg.DBLClient(TOKEN).set_data(client) autopost: topgg.AutoPoster = dblclient.autopost() + @autopost.stats() def get_stats(client: Client = topgg.data(Client)): return topgg.StatsWrapper(guild_count=len(client.guilds), shard_count=len(client.shards)) @@ -79,9 +80,7 @@ class DataContainerMixin: def __init__(self) -> None: self._data: t.Dict[t.Type, t.Any] = {type(self): self} - def set_data( - self: DataContainerT, data_: t.Any, *, override: bool = False - ) -> DataContainerT: + def set_data(self: DataContainerT, data_: t.Any, *, override: bool = False) -> DataContainerT: """ Sets data to be available in your functions. @@ -105,20 +104,16 @@ def set_data( return self @t.overload - def get_data(self, type_: t.Type[T]) -> t.Optional[T]: - ... + def get_data(self, type_: t.Type[T]) -> t.Optional[T]: ... @t.overload - def get_data(self, type_: t.Type[T], default: t.Any = None) -> t.Any: - ... + def get_data(self, type_: t.Type[T], default: t.Any = None) -> t.Any: ... def get_data(self, type_: t.Any, default: t.Any = None) -> t.Any: """Gets the injected data.""" return self._data.get(type_, default) - async def _invoke_callback( - self, callback: t.Callable[..., T], *args: t.Any, **kwargs: t.Any - ) -> T: + async def _invoke_callback(self, callback: t.Callable[..., T], *args: t.Any, **kwargs: t.Any) -> T: parameters: t.Mapping[str, inspect.Parameter] try: parameters = inspect.signature(callback).parameters @@ -128,8 +123,7 @@ async def _invoke_callback( signatures: t.Dict[str, Data] = { k: v.default for k, v in parameters.items() - if v.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD - and isinstance(v.default, Data) + if v.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD and isinstance(v.default, Data) } for k, v in signatures.items(): diff --git a/topgg/http.py b/topgg/http.py index 6bd967c5..9e34242d 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -3,6 +3,7 @@ # The MIT License (MIT) # Copyright (c) 2021 Assanali Mukhanov +# Copyright (c) 2024 null8626 # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -28,6 +29,7 @@ import json import logging import sys +import warnings from datetime import datetime from typing import Any, Coroutine, Dict, Iterable, List, Optional, Sequence, Union, cast @@ -76,15 +78,9 @@ def __init__( self.token = token self._own_session = session is None self.session: aiohttp.ClientSession = session or aiohttp.ClientSession(**kwargs) - self.global_rate_limiter = AsyncRateLimiter( - max_calls=99, period=1, callback=_rate_limit_handler - ) - self.bot_rate_limiter = AsyncRateLimiter( - max_calls=59, period=60, callback=_rate_limit_handler - ) - self.rate_limiters = AsyncRateLimiterManager( - [self.global_rate_limiter, self.bot_rate_limiter] - ) + self.global_rate_limiter = AsyncRateLimiter(max_calls=99, period=1, callback=_rate_limit_handler) + self.bot_rate_limiter = AsyncRateLimiter(max_calls=59, period=60, callback=_rate_limit_handler) + self.rate_limiters = AsyncRateLimiterManager([self.global_rate_limiter, self.bot_rate_limiter]) self.user_agent = ( f"topggpy (https://github.com/top-gg-community/python-sdk {__version__}) Python/" f"{sys.version_info[0]}.{sys.version_info[1]} aiohttp/{aiohttp.__version__}" @@ -92,11 +88,7 @@ def __init__( async def request(self, method: str, endpoint: str, **kwargs: Any) -> dict: """Handles requests to the API.""" - rate_limiters = ( - self.rate_limiters - if endpoint.startswith("/bots") - else self.global_rate_limiter - ) + rate_limiters = self.rate_limiters if endpoint.startswith("/bots") else self.global_rate_limiter url = f"{self.BASE}{endpoint}" if not self.token: @@ -210,7 +202,10 @@ def get_bots( search: Dict[str, str], fields: Sequence[str], ) -> Coroutine[Any, Any, dict]: - """Gets an object of bots on Top.gg.""" + """This function is now deprecated.""" + + warnings.warn("get_bots is now deprecated.", DeprecationWarning) + limit = min(limit, 500) fields = ", ".join(fields) search = " ".join([f"{field}: {value}" for field, value in search.items()]) @@ -240,9 +235,7 @@ async def _rate_limit_handler(until: float) -> None: """Handles the displayed message when we are ratelimited.""" duration = round(until - datetime.utcnow().timestamp()) mins = duration / 60 - fmt = ( - "We have exhausted a ratelimit quota. Retrying in %.2f seconds (%.3f minutes)." - ) + fmt = "We have exhausted a ratelimit quota. Retrying in %.2f seconds (%.3f minutes)." _LOGGER.warning(fmt, duration, mins) diff --git a/topgg/ratelimiter.py b/topgg/ratelimiter.py index 028a98ee..d2d75f79 100644 --- a/topgg/ratelimiter.py +++ b/topgg/ratelimiter.py @@ -102,9 +102,4 @@ async def __aexit__( exc_val: BaseException, exc_tb: TracebackType, ) -> None: - await asyncio.gather( - *[ - manager.__aexit__(exc_type, exc_val, exc_tb) - for manager in self.rate_limiters - ] - ) + await asyncio.gather(*[manager.__aexit__(exc_type, exc_val, exc_tb) for manager in self.rate_limiters]) diff --git a/topgg/types.py b/topgg/types.py index 2da13f95..8bfd0671 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -214,7 +214,7 @@ class BotData(DataDict[str, t.Any]): avatar: t.Optional[str] """The avatar hash of the bot.""" - def_avatar: str + def_avatar: t.Optional[str] """The avatar hash of the bot's default avatar.""" prefix: str diff --git a/topgg/webhook.py b/topgg/webhook.py index 4b94ec2b..28b92e91 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -74,12 +74,10 @@ def __init__(self) -> None: self._is_running = False @t.overload - def endpoint(self, endpoint_: None = None) -> "BoundWebhookEndpoint": - ... + def endpoint(self, endpoint_: None = None) -> "BoundWebhookEndpoint": ... @t.overload - def endpoint(self, endpoint_: "WebhookEndpoint") -> "WebhookManager": - ... + def endpoint(self, endpoint_: "WebhookEndpoint") -> "WebhookManager": ... def endpoint(self, endpoint_: t.Optional["WebhookEndpoint"] = None) -> t.Any: """Helper method that returns a WebhookEndpoint object. @@ -109,9 +107,7 @@ def endpoint(self, endpoint_: t.Optional["WebhookEndpoint"] = None) -> t.Any: self.app.router.add_post( endpoint_._route, - self._get_handler( - endpoint_._type, endpoint_._auth, endpoint_._callback - ), + self._get_handler(endpoint_._type, endpoint_._auth, endpoint_._callback), ) return self @@ -150,9 +146,7 @@ async def close(self) -> None: await self._webserver.stop() self._is_running = False - def _get_handler( - self, type_: WebhookType, auth: str, callback: t.Callable[..., t.Any] - ) -> _HandlerT: + def _get_handler(self, type_: WebhookType, auth: str, callback: t.Callable[..., t.Any]) -> _HandlerT: async def _handler(request: aiohttp.web.Request) -> web.Response: if request.headers.get("Authorization", "") != auth: return web.Response(status=401, text="Unauthorized") @@ -225,12 +219,10 @@ def auth(self: T, auth_: str) -> T: return self @t.overload - def callback(self, callback_: None) -> t.Callable[[CallbackT], CallbackT]: - ... + def callback(self, callback_: None) -> t.Callable[[CallbackT], CallbackT]: ... @t.overload - def callback(self: T, callback_: CallbackT) -> T: - ... + def callback(self: T, callback_: CallbackT) -> T: ... def callback(self, callback_: t.Any = None) -> t.Any: """ @@ -245,25 +237,21 @@ def callback(self, callback_: t.Any = None) -> t.Any: import topgg webhook_manager = topgg.WebhookManager() - endpoint = ( - topgg.WebhookEndpoint() - .type(topgg.WebhookType.BOT) - .route("/dblwebhook") - .auth("youshallnotpass") - ) + endpoint = topgg.WebhookEndpoint().type(topgg.WebhookType.BOT).route("/dblwebhook").auth("youshallnotpass") # The following are valid. endpoint.callback(lambda vote_data: print("Receives a vote!", vote_data)) + # Used as decorator, the decorated function will become the WebhookEndpoint object. @endpoint.callback - def endpoint(vote_data: topgg.BotVoteData): - ... + def endpoint(vote_data: topgg.BotVoteData): ... + # Used as decorator factory, the decorated function will still be the function itself. @endpoint.callback() - def on_vote(vote_data: topgg.BotVoteData): - ... + def on_vote(vote_data: topgg.BotVoteData): ... + webhook_manager.endpoint(endpoint) """ @@ -286,25 +274,22 @@ class BoundWebhookEndpoint(WebhookEndpoint): import topgg webhook_manager = ( - topgg.WebhookManager() - .endpoint() - .type(topgg.WebhookType.BOT) - .route("/dblwebhook") - .auth("youshallnotpass") + topgg.WebhookManager().endpoint().type(topgg.WebhookType.BOT).route("/dblwebhook").auth("youshallnotpass") ) # The following are valid. endpoint.callback(lambda vote_data: print("Receives a vote!", vote_data)) + # Used as decorator, the decorated function will become the BoundWebhookEndpoint object. @endpoint.callback - def endpoint(vote_data: topgg.BotVoteData): - ... + def endpoint(vote_data: topgg.BotVoteData): ... + # Used as decorator factory, the decorated function will still be the function itself. @endpoint.callback() - def on_vote(vote_data: topgg.BotVoteData): - ... + def on_vote(vote_data: topgg.BotVoteData): ... + endpoint.add_to_manager() """ @@ -330,9 +315,7 @@ def add_to_manager(self) -> WebhookManager: return self.manager -def endpoint( - route: str, type: WebhookType, auth: str = "" -) -> t.Callable[[t.Callable[..., t.Any]], WebhookEndpoint]: +def endpoint(route: str, type: WebhookType, auth: str = "") -> t.Callable[[t.Callable[..., t.Any]], WebhookEndpoint]: """ A decorator factory for instantiating WebhookEndpoint. @@ -353,13 +336,13 @@ def endpoint( import topgg + @topgg.endpoint("/dblwebhook", WebhookType.BOT, "youshallnotpass") async def on_vote( vote_data: topgg.BotVoteData, # database here is an injected data database: Database = topgg.data(Database), - ): - ... + ): ... """ def decorator(func: t.Callable[..., t.Any]) -> WebhookEndpoint: From a4ce9f20612b01527e52c5eeed5f4cf8f5cf011b Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 08:05:29 +0700 Subject: [PATCH 16/71] fix: let's see if this fixes it --- tests/test_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_client.py b/tests/test_client.py index fbd7a196..d70f241f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -50,6 +50,7 @@ async def test_DBLClient_post_guild_count_with_no_args(): [ (topgg.DBLClient.get_guild_count, {}), (topgg.DBLClient.get_bot_info, {}), + (topgg.DBLClient.get_weekend_status, {}), ( topgg.DBLClient.generate_widget, { From d4a9123a1dd0f74e54d9dabba3d0a4e248d66ed6 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 08:10:41 +0700 Subject: [PATCH 17/71] fix: this should fix it --- tests/test_client.py | 14 -------------- topgg/client.py | 4 ++-- topgg/http.py | 4 ++-- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index d70f241f..eaa18578 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -45,20 +45,6 @@ async def test_DBLClient_post_guild_count_with_no_args(): await client.post_guild_count() -@pytest.mark.parametrize( - "method, kwargs", - [ - (topgg.DBLClient.get_guild_count, {}), - (topgg.DBLClient.get_bot_info, {}), - (topgg.DBLClient.get_weekend_status, {}), - ( - topgg.DBLClient.generate_widget, - { - "options": topgg.types.WidgetOptions(), - }, - ), - ], -) @pytest.mark.asyncio async def test_closed_DBLClient_raises_exception(): client = topgg.DBLClient(MOCK_TOKEN) diff --git a/topgg/client.py b/topgg/client.py index ff7717eb..eddd8297 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -228,9 +228,9 @@ async def get_bots( fields: t.Optional[t.List[str]] = None, ) -> types.DataDict[str, t.Any]: """This function is deprecated.""" - + warnings.warn("get_bots is now deprecated.", DeprecationWarning) - + sort = sort or "" search = search or {} fields = fields or [] diff --git a/topgg/http.py b/topgg/http.py index 9e34242d..00717666 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -203,9 +203,9 @@ def get_bots( fields: Sequence[str], ) -> Coroutine[Any, Any, dict]: """This function is now deprecated.""" - + warnings.warn("get_bots is now deprecated.", DeprecationWarning) - + limit = min(limit, 500) fields = ", ".join(fields) search = " ".join([f"{field}: {value}" for field, value in search.items()]) From d3b48dcc12d636b0f109e67b0da620f771037b6c Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 08:14:02 +0700 Subject: [PATCH 18/71] fix: AAAAAAAAAAHHHHHHH --- tests/test_autopost.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_autopost.py b/tests/test_autopost.py index 07d60a50..115297cc 100644 --- a/tests/test_autopost.py +++ b/tests/test_autopost.py @@ -10,6 +10,9 @@ from topgg.errors import ServerError, TopGGException, Unauthorized +MOCK_TOKEN = "amogus.eyJpZCI6IjEwMjY1MjU1NjgzNDQyNjQ3MjQiLCJib3QiOnRydWUsImlhdCI6MTY5OTk4NDYyM30.amogus" + + @pytest.fixture def session() -> ClientSession: return mock.Mock(ClientSession) @@ -17,7 +20,7 @@ def session() -> ClientSession: @pytest.fixture def autopost(session: ClientSession) -> AutoPoster: - return AutoPoster(DBLClient("", session=session)) + return AutoPoster(DBLClient(MOCK_TOKEN, session=session)) @pytest.mark.asyncio @@ -29,7 +32,7 @@ async def test_AutoPoster_breaks_autopost_loop_on_401(mocker: MockerFixture, ses mocker.patch("topgg.DBLClient.post_guild_count", side_effect=Unauthorized(response, {})) callback = mock.Mock() - autopost = DBLClient("", session=session).autopost().stats(callback) + autopost = DBLClient(MOCK_TOKEN, session=session).autopost().stats(callback) assert isinstance(autopost, AutoPoster) assert not isinstance(autopost.stats()(callback), AutoPoster) From e7bd0b8049e9cd66400ab1e06fd63c72bf0904ae Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 08:16:11 +0700 Subject: [PATCH 19/71] fix: fix bot_id test --- tests/test_client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index eaa18578..fa11c434 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -56,8 +56,12 @@ async def test_closed_DBLClient_raises_exception(): @pytest.mark.asyncio -async def test_closed_DBLClient_bot_id(): +async def test_DBLClient_bot_id(): + client = topgg.DBLClient(MOCK_TOKEN) + assert not client.is_closed assert client.bot_id == 1026525568344264724 + await client.close() + assert client.is_closed @pytest.mark.asyncio From 99d8ab7120ee499501fcc2a77f367fd358a19680 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 12:55:48 +0700 Subject: [PATCH 20/71] refactor: collapse if-statement --- topgg/client.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/topgg/client.py b/topgg/client.py index eddd8297..cab34637 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -71,9 +71,7 @@ def __init__( try: encoded_json = re.sub(r"[^a-zA-Z0-9\+\/]+", "", token.split(".")[1]) - missing_padding = len(encoded_json) % 4 - if missing_padding: - encoded_json += "=" * (4 - missing_padding) + encoded_json += "=" * (4 - (len(encoded_json) % 4)) self.bot_id = int(json.loads(base64.b64decode(encoded_json))["id"]) except: From b6be4c4da92e18b15590b3e996a5a94f5b9e1cc1 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 12:56:58 +0700 Subject: [PATCH 21/71] doc: update readme --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 041b963a..99fa29e5 100644 --- a/README.rst +++ b/README.rst @@ -12,7 +12,7 @@ Top.gg Python Library :target: https://topggpy.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status -A simple API wrapper for `Top.gg `_ written in Python, supporting discord.py. +A simple API wrapper for `Top.gg `_ written in Python. Installation ------------ @@ -31,7 +31,6 @@ Features * POST server count * GET bot info, server count, upvote info -* GET all bots * GET user info * GET widgets (large and small) including custom ones. See `docs.top.gg `_ for more info. * GET weekend status From d2ae045179eb1d7e13868829a7427a221cae456d Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 15:10:58 +0700 Subject: [PATCH 22/71] doc: documentation overhaul --- .gitignore | 11 +- docs/_static/css/custom.css | 7 -- docs/_static/favicon.ico | Bin 0 -> 15406 bytes docs/_static/img/favicon-16x16.png | Bin 1150 -> 0 bytes docs/_static/style.css | 48 ++++++++ docs/api.rst | 2 +- docs/api/autopost.rst | 6 +- docs/api/client.rst | 6 +- docs/api/data.rst | 6 +- docs/api/errors.rst | 9 +- docs/api/types.rst | 6 +- docs/api/webhook.rst | 6 +- docs/conf.py | 169 +++-------------------------- docs/index.rst | 56 ++++++++-- docs/repository.rst | 5 + docs/support.rst | 5 + docs/topgg.svg | 12 +- docs/whats_new.rst | 63 +++++------ requirements-docs.txt | 5 +- topgg/autopost.py | 14 +-- topgg/client.py | 22 ++-- topgg/data.py | 6 +- topgg/webhook.py | 8 +- 23 files changed, 203 insertions(+), 269 deletions(-) delete mode 100644 docs/_static/css/custom.css create mode 100644 docs/_static/favicon.ico delete mode 100644 docs/_static/img/favicon-16x16.png create mode 100644 docs/_static/style.css create mode 100644 docs/repository.rst create mode 100644 docs/support.rst diff --git a/.gitignore b/.gitignore index 41a61499..51f781c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,9 @@ topggpy.egg-info/ -topgg/__pycache__/ build/ dist/ -/docs/_build -/docs/_templates -.vscode -/.idea/ -__pycache__ +docs/_build +docs/_templates +.vscode/ +.idea/ +**/__pycache__/ .coverage diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css deleted file mode 100644 index c7e6795e..00000000 --- a/docs/_static/css/custom.css +++ /dev/null @@ -1,7 +0,0 @@ -header #logo-container img { - height: 100px; -} - -#search input[type="text"] { - font-size: 1em; -} \ No newline at end of file diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ad108bb290ac60782ab9222a51717307e5772b1c GIT binary patch literal 15406 zcmeHOJ!lj`6rPwEK@{U37*RMyG=>P5+r0}SYQ#bWK@iW%LSk}%NkFg=Ec8UdBDDyD zDHSb(7FOB_Vre0YHn9j;NE8VP_!E=o_wH`uX7w(!JCj>ZX5lgW^WOWu`F3u0W_C}A zCebX~+62m&$h8QO5<QX^LX6^EJYKoJTZosNh3J3}LJ(0tkD_6=;p4O_+e4jf zg(PhWN}S^|4pGJCvkr~*9iX+Ud3^DuJjiiNE8e9E&G^Y`@fYIMcG00t(sC|!dFo&0 z@6u;^9oJUNT&UM=9M60G2a|Njd+gsw%xuP``>ZMUfBLU46)*j>E{$S;5jI~_j(2EU z=JJ-R@f-vGbAHRG=%D{xwc}!uR`^W_YVx0}b}a8hqn6yy#9+NJ?w{;t6=-w?`g>>_ z`li!f%cba8m}gNKEg09^pUHjnL9?*2yh}U7Z6Jezjr}%tN8!V_-ME7Lk@LR zvtlQ;2B`nIKM(g!#Wc10bq4M_{AM4aoGbly{*2G%IK5PIK2`i0dA zOx$}V?UxH*@=z(4qy8$m(r3Bwx&5mc`ma4`ucuUegZLDCsYS&~b4P(WrJ}#8pX>*1 zX%(;v)L#K!C&3b}I0v)qBoM)FRspMkRlq7>T!CDIPUIaL@f+qLBbbL>9Z1rd@>(V} z83#9zka^4bK#g*S=k%yc96l=zz#2=T$e-5;_}%0+uEixs!xN$LBzUTWFPCfX0aGBq0%po!;7v>K*zJcSw z+&OQZ`O9`OjeBxU)&cxKFqbeE5r5`7l}*rDZu{W>Eev;#iI6{`&A$MLcHE2ajmRII z`8&=uEKr~0t!tF}S9m-``PcWPUXHc)qvnscGf|7*QrDv9kKeA+JN|0*h@5}OepA-R z@(=gik^1SDR)2l{`S}&r3Jx9N@nEf0U$aj_q1{#@+U0w1X5M}?^Jd;##`@^B*%*yOY^0yDLB`lHflOp++i7Bq){+Bl zaGeIxJ%)e9^r(3}TDy1IO(D-uBE9N0<`Xx);z2R6k5cgP8;ZdL)RHlj!>15Ua;U_^ zuzFr20b~BmII78Lhu&%#%DwPwqJaF~I-1W}sA-AhHKY4vuucDEA#7DCMO|5fTQ{R5(yJ`Q4NN3L`QaNN&f9gqQbAjhu5w5@yr&)(^ z&2{A_$a`$oKi#gMpWL<&dH)9QlQieV%b6b!%P!LG#=Yyh1JbMmwYwP9ObW7hg2&US v9)*)#h~(SyuFyFs9PdCsZ+hKRi&Idd7szfcS;n^X-IeEGef#wv`0e}&td}C1 diff --git a/docs/_static/style.css b/docs/_static/style.css new file mode 100644 index 00000000..ee1e4dd5 --- /dev/null +++ b/docs/_static/style.css @@ -0,0 +1,48 @@ +body { + --color-link-underline: rgba(0, 0, 0, 0); + --color-link-underline--hover: var(--color-link); + --color-inline-code-background: rgba(0, 0, 0, 0); + --color-api-background-hover: var(--color-background-primary); + --color-highlight-on-target: var(--color-background-primary) !important; + + --font-stack: "Inter", sans-serif !important; + --font-stack--monospace: "Roboto Mono", monospace !important; +} + +aside.toc-drawer { + visibility: hidden; +} + +#furo-readthedocs-versions, .injected, .edit-this-page, .related-pages, .headerlink { + visibility: hidden; + user-select: none; +} + +dd dt { + color: var(--color-foreground-secondary); +} + +aside.toc-drawer .docutils:hover, .sidebar-brand-text:hover { + transition: 0.15s; + filter: opacity(75%); +} + +.highlight .c1, em { + font-style: normal !important; +} + +.highlight .nn { + text-decoration: none !important; +} + +h1 { + font-weight: 900; +} + +.sidebar-brand-text { + font-weight: bolder; +} + +.sidebar-scroll .reference.internal { + color: var(--color-brand-primary); +} \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index 69691659..c1dc98ff 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,7 +1,7 @@ .. currentmodule:: topgg ############# -API Reference +API reference ############# The following section outlines the API of topggpy. diff --git a/docs/api/autopost.rst b/docs/api/autopost.rst index 668af79a..0151646d 100644 --- a/docs/api/autopost.rst +++ b/docs/api/autopost.rst @@ -1,6 +1,6 @@ -####################### -Auto-post API Reference -####################### +################## +Autopost reference +################## .. automodule:: topgg.autopost :members: diff --git a/docs/api/client.rst b/docs/api/client.rst index 1bac1971..1d8966e8 100644 --- a/docs/api/client.rst +++ b/docs/api/client.rst @@ -1,6 +1,6 @@ -#################### -Client API Reference -#################### +################ +Client reference +################ .. automodule:: topgg.client :members: diff --git a/docs/api/data.rst b/docs/api/data.rst index 3f10ff2e..090494ef 100644 --- a/docs/api/data.rst +++ b/docs/api/data.rst @@ -1,6 +1,6 @@ -################## -Data API Reference -################## +############## +Data reference +############## .. automodule:: topgg.data :members: diff --git a/docs/api/errors.rst b/docs/api/errors.rst index 804fdfa3..d54af678 100644 --- a/docs/api/errors.rst +++ b/docs/api/errors.rst @@ -1,7 +1,6 @@ -#################### -Errors API Reference -#################### +################ +Errors reference +################ .. automodule:: topgg.errors - :members: - :inherited-members: \ No newline at end of file + :members: \ No newline at end of file diff --git a/docs/api/types.rst b/docs/api/types.rst index a6a70f84..14b983db 100644 --- a/docs/api/types.rst +++ b/docs/api/types.rst @@ -1,6 +1,6 @@ -#################### -Models API Reference -#################### +################ +Models reference +################ .. automodule:: topgg.types :members: diff --git a/docs/api/webhook.rst b/docs/api/webhook.rst index 53a41c92..c1b067a5 100644 --- a/docs/api/webhook.rst +++ b/docs/api/webhook.rst @@ -1,6 +1,6 @@ -##################### -Webhook API Reference -##################### +################# +Webhook reference +################# .. automodule:: topgg.webhook :members: diff --git a/docs/conf.py b/docs/conf.py index ca87da68..f4f80040 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,23 +21,10 @@ import os import sys -import alabaster - sys.path.insert(0, os.path.abspath("../")) -from topgg import __version__ as version - -# import re +from topgg import __version__ as version -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# 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.viewcode", @@ -45,12 +32,13 @@ "sphinx.ext.extlinks", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", + "sphinx_reredirects", ] autodoc_member_order = "groupwise" extlinks = { - "issue": ("https://github.com/top-gg-community/python-sdk/issues/%s", "GH-"), + "issue": ("https://github.com/top-gg-community/python-sdk/issues/%s", "#%s"), } intersphinx_mapping = { @@ -59,167 +47,38 @@ "aiohttp": ("https://docs.aiohttp.org/en/stable/", None), } -releases_github_path = "top-gg/python-sdk" - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] +redirects = {"repository": "https://github.com/top-gg-community/python-sdk", "support": "https://discord.gg/dbl"} -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] +releases_github_path = "top-gg-community/python-sdk" source_suffix = ".rst" - -# The master toctree document. master_doc = "index" -# General information about the project. project = "topggpy" -copyright = "2021, Assanali Mukhanov" -author = "Assanali Mukhanov" - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. - -# with open('../dbl/__init__.py') as f: -# version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE).group(1) -# The full version, including alpha/beta/rc tags. +copyright = "2021 Assanali Mukhanov; 2024 null8626" +author = "null8626" release = version -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path +language = "en" exclude_patterns = ["_build"] -# -- Options for HTML output ---------------------------------------------- - -html_theme_options = {"navigation_depth": 2} -html_theme_path = [alabaster.get_path()] -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "insegel" - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. +html_css_files = [ + "style.css", + "https://fonts.googleapis.com/css2?family=Inter:wght@100..900&family=Roboto+Mono&display=swap", +] +html_favicon = "_static/favicon.ico" +html_theme = "furo" html_logo = "topgg.svg" - -# 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"] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. html_show_sphinx = False - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -# html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# Now only 'ja' uses this config value -# html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -# html_search_scorer = 'scorer.js' - - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. htmlhelp_basename = "topggpydoc" -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, "topggpy.tex", "topggpy Documentation", "Assanali Mukhanov", "manual"), ] -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). man_pages = [(master_doc, "topggpy", "topggpy Documentation", [author], 1)] -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) texinfo_documents = [ ( master_doc, diff --git a/docs/index.rst b/docs/index.rst index a634d380..d8b9e3d0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,19 +3,53 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -################################### -Welcome to topggpy's documentation! -################################### +##################### +Top.gg Python Library +##################### + +.. image:: https://img.shields.io/pypi/v/topggpy.svg + :target: https://pypi.python.org/pypi/topggpy + :alt: View on PyPI +.. image:: https://img.shields.io/pypi/pyversions/topggpy.svg + :target: https://pypi.python.org/pypi/topggpy + :alt: v1.0.0 +.. image:: https://readthedocs.org/projects/topggpy/badge/?version=latest + :target: https://topggpy.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +A simple API wrapper for `Top.gg `_ written in Python. + +Installation +------------ + +.. code:: bash + + pip3 install topggpy + +Features +-------- + +* POST server count +* GET bot info, server count, upvote info +* GET user info +* GET widgets (large and small) including custom ones. See `docs.top.gg `_ for more info. +* GET weekend status +* Built-in webhook to handle Top.gg votes +* Automated server count posting +* Searching for bots via the API + +Additional information +---------------------- + +* Before using the webhook provided by this library, make sure that you have specified port open. +* Optimal values for port are between 1024 and 49151. +* If you happen to need help implementing topggpy in your bot, feel free to ask in the ``#development`` or ``#api`` channels in our `Discord server `_. .. toctree:: - :maxdepth: 1 + :maxdepth: 2 + :hidden: api whats_new - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` + repository + support \ No newline at end of file diff --git a/docs/repository.rst b/docs/repository.rst new file mode 100644 index 00000000..a542ad6a --- /dev/null +++ b/docs/repository.rst @@ -0,0 +1,5 @@ +================= +GitHub repository +================= + +You should be redirected in a few moments. Otherwise, click here: https://github.com/top-gg-community/python-sdk \ No newline at end of file diff --git a/docs/support.rst b/docs/support.rst new file mode 100644 index 00000000..531270f0 --- /dev/null +++ b/docs/support.rst @@ -0,0 +1,5 @@ +============== +Support server +============== + +You should be redirected in a few moments. Otherwise, click here: https://discord.gg/dbl \ No newline at end of file diff --git a/docs/topgg.svg b/docs/topgg.svg index 9afe2351..63f58125 100644 --- a/docs/topgg.svg +++ b/docs/topgg.svg @@ -1,10 +1,4 @@ - - - - - + + + diff --git a/docs/whats_new.rst b/docs/whats_new.rst index 23e3ffe3..eaf52c67 100644 --- a/docs/whats_new.rst +++ b/docs/whats_new.rst @@ -3,22 +3,23 @@ .. _whats_new: ########## -What's New +What's new ########## This page keeps a detailed human friendly rendering of what's new and changed in specific versions. v2.0.1 ====== -* Added Python 3.12 support -* Dropped Python 3.6 and 3.7 support +* Added Python 3.12 support (:issue:`78`) +* Dropped Python 3.6 and 3.7 support (:issue:`75`) * Removed the need to manually set a ``default_bot_id`` property -* :attr:`BotData.def_avatar` is now an optional string -* :meth:`DBLClient.get_bots` is now deprecated -* :meth:`DBLClient.get_guild_count` no longer accepts a ``bot_id`` argument -* :meth:`DBLClient.get_bot_votes` no longer raises a ``ClientException`` without a ``default_bot_id`` property -* :meth:`DBLClient.get_bot_info` no longer raises a ``ClientException`` without a ``default_bot_id`` property -* :meth:`DBLClient.generate_widget`` no longer raises a ``ClientException`` without a ``default_bot_id`` property +* :attr:`~.BotData.def_avatar` is now an optional string +* :meth:`~.DBLClient.get_bots` is now deprecated +* :meth:`~.DBLClient.get_guild_count` no longer accepts a ``bot_id`` argument +* :meth:`~.DBLClient.get_bot_votes` no longer raises a :class:`~.ClientException` without a ``default_bot_id`` property +* :meth:`~.DBLClient.get_bot_info` no longer raises a :class:`~.ClientException` without a ``default_bot_id`` property +* :meth:`~.DBLClient.generate_widget` no longer raises a :class:`~.ClientException` without a ``default_bot_id`` property +* Documentation overhaul v2.0.0a ======= @@ -31,15 +32,15 @@ v2.0.0a v1.4.0 ====== -* The type of data passed to ``on_dbl_vote`` has been changed from :class:`dict` to :obj:`BotVoteData` -* The type of data passed to ``on_dsl_vote`` has been changed from :class:`dict` to :obj:`ServerVoteData` +* The type of data passed to ``on_dbl_vote`` has been changed from :class:`~.dict` to :obj:`BotVoteData` +* The type of data passed to ``on_dsl_vote`` has been changed from :class:`~.dict` to :obj:`ServerVoteData` v1.3.0 ====== * Introduced `global ratelimiter `__ to follow Top.gg global ratelimits - * Fixed an :exc:`AttributeError` raised by :meth:`HTTPClient.request` + * Fixed an :exc:`AttributeError` raised by :meth:`~.HTTPClient.request` * `Resource-specific ratelimit `__ is now actually resource-specific @@ -47,41 +48,41 @@ v1.2.0 ====== * Introduced global ratelimiter along with bot endpoints ratelimiter -* Follow consistency with typing in :class:`HTTPClient` and :class:`DBLClient` along with updated docstrings (:issue:`55`) +* Follow consistency with typing in :class:`~.HTTPClient` and :class:`~.DBLClient` along with updated docstrings (:issue:`55`) v1.1.0 ====== * Introduced `data models `__ - * :meth:`DBLClient.get_bot_votes` now returns a list of :class:`BriefUserData` objects + * :meth:`~.DBLClient.get_bot_votes` now returns a list of :class:`~.BriefUserData` objects - * :meth:`DBLClient.get_bot_info` now returns a :class:`BotData` object + * :meth:`~.DBLClient.get_bot_info` now returns a :class:`~.BotData` object - * :meth:`DBLClient.get_guild_count` now returns a :class:`BotStatsData` object + * :meth:`~.DBLClient.get_guild_count` now returns a :class:`~.BotStatsData` object - * :meth:`DBLClient.get_user_info` now returns a :class:`UserData` object + * :meth:`~.DBLClient.get_user_info` now returns a :class:`~.UserData` object -* :meth:`WebhookManager.run` now returns an :class:`asyncio.Task`, meaning it can now be optionally awaited +* :meth:`~.WebhookManager.run` now returns an :class:`~.asyncio.Task`, meaning it can now be optionally awaited v1.0.1 ====== -* :attr:`WebhookManager.webserver` now instead returns :class:`aiohttp.web.Application` for ease of use +* :attr:`~.WebhookManager.webserver` now instead returns :class:`~.aiohttp.web.Application` for ease of use v1.0.0 ====== * Renamed the module folder from ``dbl`` to ``topgg`` -* Added ``post_shard_count`` argument to :meth:`DBLClient.post_guild_count` +* Added ``post_shard_count`` argument to :meth:`~.DBLClient.post_guild_count` * Autopost now supports automatic shard posting (:issue:`42`) * Large webhook system rework, read the :obj:`api/webhook` section for more * Added support for server webhooks -* Renamed ``DBLException`` to :class:`TopGGException` -* Renamed ``DBLClient.get_bot_upvotes()`` to :meth:`DBLClient.get_bot_votes` -* Added :meth:`DBLClient.generate_widget` along with the ``widgets`` section in the documentation +* Renamed ``DBLException`` to :class:`~.TopGGException` +* Renamed ``DBLClient.get_bot_upvotes()`` to :meth:`~.DBLClient.get_bot_votes` +* Added :meth:`~.DBLClient.generate_widget` along with the ``widgets`` section in the documentation * Implemented a properly working ratelimiter * Added :func:`on_autopost_error` * All autopost events now follow ``on_autopost_x`` naming format, e.g. :func:`on_autopost_error`, :func:`on_autopost_success` @@ -90,7 +91,7 @@ v1.0.0 v0.4.0 ====== -* :meth:`DBLClient.post_guild_count` now supports a custom ``guild_count`` argument, which accepts either an integer or list of integers +* :meth:`~.DBLClient.post_guild_count` now supports a custom ``guild_count`` argument, which accepts either an integer or list of integers * Reworked how shard info is posted * Removed ``InvalidArgument`` and ``ConnectionClosed`` exceptions * Added ``ServerError`` exception @@ -99,12 +100,12 @@ v0.3.3 ====== * Internal changes regarding support of Top.gg migration -* Fixed errors raised when using :meth:`DBLClient.close` without built-in webhook +* Fixed errors raised when using :meth:`~.DBLClient.close` without built-in webhook v0.3.2 ====== -* ``Client`` class has been renamed to ``DBLClient`` +* ``Client`` class has been renamed to :class:`~.DBLClient` v0.3.1 ====== @@ -116,7 +117,7 @@ v0.3.1 v0.3.0 ====== -* :class:`DBLClient` now has ``autopost`` kwarg that will post server count automatically every 30 minutes +* :class:`~.DBLClient` now has ``autopost`` kwarg that will post server count automatically every 30 minutes * Fixed code 403 errors * Added ``on_dbl_vote``, an event that is called when you test your webhook * Added ``on_dbl_test``, an event that is called when someone tests your webhook @@ -126,7 +127,7 @@ v0.2.1 * Added webhook * Removed support for discord.py versions lower than 1.0.0 -* Made :meth:`DBLClient.get_weekend_status` return a boolean value +* Made :meth:`~.DBLClient.get_weekend_status` return a boolean value * Added webhook example in README * Removed ``post_server_count`` and ``get_server_count`` @@ -141,9 +142,9 @@ v0.2.0 * Made ``get_server_count`` an alias for ``get_guild_count`` -* Added :meth:`DBLClient.get_weekend_status` -* Removed all parameters from :meth:`DBLClient.get_upvote_info` -* Added limit to :meth:`DBLClient.get_bots` +* Added :meth:`~.DBLClient.get_weekend_status` +* Removed all parameters from :meth:`~.DBLClient.get_upvote_info` +* Added limit to :meth:`~.DBLClient.get_bots` * Fixed example in README v0.1.6 diff --git a/requirements-docs.txt b/requirements-docs.txt index b90afd89..8315d05b 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,4 +1,3 @@ +furo sphinx -insegel -sphinxcontrib-napoleon -sphinx-rtd-dark-mode \ No newline at end of file +sphinx-reredirects \ No newline at end of file diff --git a/topgg/autopost.py b/topgg/autopost.py index 96aa9888..7025b9b9 100644 --- a/topgg/autopost.py +++ b/topgg/autopost.py @@ -184,16 +184,14 @@ def stats(self, callback: t.Any = None) -> t.Any: # In this example, we fetch the stats from a Discord client instance. client = Client(...) dblclient = topgg.DBLClient(TOKEN).set_data(client) - autopost = ( - dblclient - .autopost() - .on_success(lambda: print("Successfully posted the stats!") - ) + autopost = dblclient.autopost().on_success(lambda: print("Successfully posted the stats!")) + @autopost.stats() def get_stats(client: Client = topgg.data(Client)): return topgg.StatsWrapper(guild_count=len(client.guilds), shard_count=len(client.shards)) + # somewhere after the event loop has started autopost.start() """ @@ -222,11 +220,11 @@ def set_interval(self, seconds: t.Union[float, datetime.timedelta]) -> "AutoPost Sets the interval between posting stats. Args: - seconds (:obj:`typing.Union` [ :obj:`float`, :obj:`datetime.timedelta` ]) + seconds (Union[:obj:`float`, :obj:`datetime.timedelta`]) The interval. Raises: - :obj:`ValueError` + ValueError If the provided interval is less than 900 seconds. """ if isinstance(seconds, datetime.timedelta): @@ -283,7 +281,7 @@ def start(self) -> "asyncio.Task[None]": This method must be called when the event loop has already running! Raises: - :obj:`~.errors.TopGGException` + :exc:`~.errors.TopGGException` If there's no callback provided or the autopost is already running. """ if not hasattr(self, "_stats"): diff --git a/topgg/client.py b/topgg/client.py index cab34637..1be4edda 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -100,7 +100,7 @@ async def get_weekend_status(self) -> bool: :obj:`bool`: The boolean value of weekend status. Raises: - :obj:`~.errors.ClientStateException` + :exc:`~.errors.ClientStateException` If the client has been closed. """ await self._ensure_session() @@ -139,18 +139,18 @@ async def post_guild_count( An instance of StatsWrapper containing guild_count, shard_count, and shard_id. Keyword Arguments: - guild_count (:obj:`typing.Optional` [:obj:`typing.Union` [ :obj:`int`, :obj:`list` [ :obj:`int` ]]]) + guild_count (Optional[Union[:obj:`int`, List[:obj:`int`]]]) Number of guilds the bot is in. Applies the number to a shard instead if shards are specified. If not specified, length of provided client's property `.guilds` will be posted. - shard_count (:obj:`.typing.Optional` [ :obj:`int` ]) + shard_count (Optional[:obj:`int`]) The total number of shards. - shard_id (:obj:`.typing.Optional` [ :obj:`int` ]) + shard_id (Optional[:obj:`int`]) The index of the current shard. Top.gg uses `0 based indexing`_ for shards. Raises: TypeError If no argument is provided. - :obj:`~.errors.ClientStateException` + :exc:`~.errors.ClientStateException` If the client has been closed. """ if stats: @@ -170,7 +170,7 @@ async def get_guild_count(self) -> types.BotStatsData: The guild count and shards of a bot on Top.gg. Raises: - :obj:`~.errors.ClientStateException` + :exc:`~.errors.ClientStateException` If the client has been closed. """ await self._ensure_session() @@ -184,11 +184,11 @@ async def get_bot_votes(self) -> t.List[types.BriefUserData]: This API endpoint is only available to the bot's owner. Returns: - :obj:`list` [ :obj:`~.types.BriefUserData` ]: + List[:obj:`~.types.BriefUserData` ]: Users who voted for your bot. Raises: - :obj:`~.errors.ClientStateException` + :exc:`~.errors.ClientStateException` If the client has been closed. """ await self._ensure_session() @@ -210,7 +210,7 @@ async def get_bot_info(self, bot_id: t.Optional[int] = None) -> types.BotData: `here `_. Raises: - :obj:`~.errors.ClientStateException` + :exc:`~.errors.ClientStateException` If the client has been closed. """ await self._ensure_session() @@ -251,7 +251,7 @@ async def get_user_info(self, user_id: int) -> types.UserData: Information about a Top.gg user. Raises: - :obj:`~.errors.ClientStateException` + :exc:`~.errors.ClientStateException` If the client has been closed. """ await self._ensure_session() @@ -269,7 +269,7 @@ async def get_user_vote(self, user_id: int) -> bool: :obj:`bool`: Info about the user's vote. Raises: - :obj:`~.errors.ClientStateException` + :exc:`~.errors.ClientStateException` If the client has been closed. """ await self._ensure_session() diff --git a/topgg/data.py b/topgg/data.py index baafde66..7d5f422d 100644 --- a/topgg/data.py +++ b/topgg/data.py @@ -36,7 +36,7 @@ def data(type_: t.Type[T]) -> T: Represents the injected data. This should be set as the parameter's default value. Args: - `type_` (:obj:`type` [ :obj:`T` ]) + `type_` (:obj:`type` [ :obj:`T`]) The type of the injected data. Returns: @@ -85,13 +85,13 @@ def set_data(self: DataContainerT, data_: t.Any, *, override: bool = False) -> D Sets data to be available in your functions. Args: - `data_` (:obj:`typing.Any`) + `data_` (Any) The data to be injected. override (:obj:`bool`) Whether or not to override another instance that already exists. Raises: - :obj:`~.errors.TopGGException` + :exc:`~.errors.TopGGException` If override is False and another instance of the same type exists. """ type_ = type(data_) diff --git a/topgg/webhook.py b/topgg/webhook.py index 28b92e91..01b30f7b 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -83,16 +83,16 @@ def endpoint(self, endpoint_: t.Optional["WebhookEndpoint"] = None) -> t.Any: """Helper method that returns a WebhookEndpoint object. Args: - `endpoint_` (:obj:`typing.Optional` [ :obj:`WebhookEndpoint` ]) + `endpoint_` (Optional[:obj:`WebhookEndpoint`]) The endpoint to add. Returns: - :obj:`typing.Union` [ :obj:`WebhookManager`, :obj:`BoundWebhookEndpoint` ]: + Union[:obj:`WebhookManager`, :obj:`BoundWebhookEndpoint` ]: An instance of :obj:`WebhookManager` if endpoint was provided, otherwise :obj:`BoundWebhookEndpoint`. Raises: - :obj:`~.errors.TopGGException` + :exc:`~.errors.TopGGException` If the endpoint is lacking attributes. """ if endpoint_: @@ -328,7 +328,7 @@ def endpoint(route: str, type: WebhookType, auth: str = "") -> t.Callable[[t.Cal The auth for the endpoint. Returns: - :obj:`typing.Callable` [[ :obj:`typing.Callable` [..., :obj:`typing.Any` ]], :obj:`WebhookEndpoint` ]: + Callable[[Callable[..., Any]], :obj:`WebhookEndpoint`]: The actual decorator. :Example: From 99b9bc305a42848223971a2f941bdf1c85e64175 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 17:13:18 +0700 Subject: [PATCH 23/71] doc: add examples --- docs/_static/script.js | 22 ++++++++++++++++++++++ docs/api.rst | 6 ------ docs/conf.py | 8 +++++++- docs/examples.rst | 9 +++++++++ docs/examples/discord_py.rst | 5 +++++ docs/examples/hikari.rst | 5 +++++ docs/index.rst | 1 + docs/whats_new.rst | 4 ---- examples/discordpy_example/__main__.py | 1 + examples/hikari_example/__main__.py | 1 + 10 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 docs/_static/script.js create mode 100644 docs/examples.rst create mode 100644 docs/examples/discord_py.rst create mode 100644 docs/examples/hikari.rst diff --git a/docs/_static/script.js b/docs/_static/script.js new file mode 100644 index 00000000..0d7b93f8 --- /dev/null +++ b/docs/_static/script.js @@ -0,0 +1,22 @@ +document.addEventListener('load', () => { + try { + document.querySelector('.edit-this-page').remove() + + // remove these useless crap that appears on official readthedocs builds + document.querySelector('#furo-readthedocs-versions').remove() + document.querySelector('.injected').remove() + } catch { + // we're building this locally, forget it + } +}) + +const findChildrenWithName = (elem, name) => [...elem.children].find(child => child.nodeName === name) + +for (const label of document.querySelectorAll('.sidebar-container label')) { + const link = findChildrenWithName(label.parentElement, 'A') + + link.addEventListener('click', event => { + event.preventDefault() + label.click() + }) +} \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index c1dc98ff..28409ff7 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,13 +1,7 @@ -.. currentmodule:: topgg - ############# API reference ############# -The following section outlines the API of topggpy. - -Index: - .. toctree:: :maxdepth: 2 diff --git a/docs/conf.py b/docs/conf.py index f4f80040..492b540a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,7 +47,12 @@ "aiohttp": ("https://docs.aiohttp.org/en/stable/", None), } -redirects = {"repository": "https://github.com/top-gg-community/python-sdk", "support": "https://discord.gg/dbl"} +redirects = { + "repository": "https://github.com/top-gg-community/python-sdk", + "support": "https://discord.gg/dbl", + "examples/discord_py": "https://github.com/Top-gg-Community/python-sdk/tree/master/examples/discordpy_example", + "examples/hikari": "https://github.com/Top-gg-Community/python-sdk/tree/master/examples/hikari_example", +} releases_github_path = "top-gg-community/python-sdk" source_suffix = ".rst" @@ -61,6 +66,7 @@ language = "en" exclude_patterns = ["_build"] +html_js_files = ["script.js"] html_css_files = [ "style.css", "https://fonts.googleapis.com/css2?family=Inter:wght@100..900&family=Roboto+Mono&display=swap", diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 00000000..c18a83f6 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,9 @@ +######## +Examples +######## + + .. toctree:: + :maxdepth: 2 + + examples/discord_py + examples/hikari \ No newline at end of file diff --git a/docs/examples/discord_py.rst b/docs/examples/discord_py.rst new file mode 100644 index 00000000..e77808d8 --- /dev/null +++ b/docs/examples/discord_py.rst @@ -0,0 +1,5 @@ +================== +Discord.py example +================== + +You should be redirected in a few moments. Otherwise, click here: https://github.com/Top-gg-Community/python-sdk/tree/master/examples/discordpy_example \ No newline at end of file diff --git a/docs/examples/hikari.rst b/docs/examples/hikari.rst new file mode 100644 index 00000000..89fc6513 --- /dev/null +++ b/docs/examples/hikari.rst @@ -0,0 +1,5 @@ +============== +Hikari example +============== + +You should be redirected in a few moments. Otherwise, click here: https://github.com/Top-gg-Community/python-sdk/tree/master/examples/hikari_example \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index d8b9e3d0..f616a71c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -50,6 +50,7 @@ Additional information :hidden: api + examples whats_new repository support \ No newline at end of file diff --git a/docs/whats_new.rst b/docs/whats_new.rst index eaf52c67..d506500b 100644 --- a/docs/whats_new.rst +++ b/docs/whats_new.rst @@ -1,7 +1,3 @@ -.. currentmodule:: topgg - -.. _whats_new: - ########## What's new ########## diff --git a/examples/discordpy_example/__main__.py b/examples/discordpy_example/__main__.py index 67e58232..70771708 100644 --- a/examples/discordpy_example/__main__.py +++ b/examples/discordpy_example/__main__.py @@ -1,6 +1,7 @@ # The MIT License (MIT) # Copyright (c) 2021 Norizon +# Copyright (c) 2024 null8626 # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), diff --git a/examples/hikari_example/__main__.py b/examples/hikari_example/__main__.py index 857329c8..82da8ba0 100644 --- a/examples/hikari_example/__main__.py +++ b/examples/hikari_example/__main__.py @@ -1,6 +1,7 @@ # The MIT License (MIT) # Copyright (c) 2021 Norizon +# Copyright (c) 2024 null8626 # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), From 8ea8f7adfe8a01e474e455918d9fca140274b077 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 17:26:38 +0700 Subject: [PATCH 24/71] doc: more redirects and refactor js script --- docs/_static/script.js | 4 +--- docs/api.rst | 13 ------------- docs/api/index.rst | 13 +++++++++++++ docs/conf.py | 2 ++ docs/{examples.rst => examples/index.rst} | 4 ++-- docs/index.rst | 7 ++----- 6 files changed, 20 insertions(+), 23 deletions(-) delete mode 100644 docs/api.rst create mode 100644 docs/api/index.rst rename docs/{examples.rst => examples/index.rst} (58%) diff --git a/docs/_static/script.js b/docs/_static/script.js index 0d7b93f8..c8cfd5b7 100644 --- a/docs/_static/script.js +++ b/docs/_static/script.js @@ -10,10 +10,8 @@ document.addEventListener('load', () => { } }) -const findChildrenWithName = (elem, name) => [...elem.children].find(child => child.nodeName === name) - for (const label of document.querySelectorAll('.sidebar-container label')) { - const link = findChildrenWithName(label.parentElement, 'A') + const link = [...label.parentElement.children].find(child => child.nodeName === 'A') link.addEventListener('click', event => { event.preventDefault() diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index 28409ff7..00000000 --- a/docs/api.rst +++ /dev/null @@ -1,13 +0,0 @@ -############# -API reference -############# - - .. toctree:: - :maxdepth: 2 - - api/autopost - api/client - api/data - api/errors - api/types - api/webhook \ No newline at end of file diff --git a/docs/api/index.rst b/docs/api/index.rst new file mode 100644 index 00000000..4c4e05bd --- /dev/null +++ b/docs/api/index.rst @@ -0,0 +1,13 @@ +############# +API reference +############# + + .. toctree:: + :maxdepth: 2 + + autopost + client + data + errors + types + webhook \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 492b540a..50eaf9d5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,6 +50,8 @@ redirects = { "repository": "https://github.com/top-gg-community/python-sdk", "support": "https://discord.gg/dbl", + "api/index": "autopost.html", + "examples/index": "discord_py.html", "examples/discord_py": "https://github.com/Top-gg-Community/python-sdk/tree/master/examples/discordpy_example", "examples/hikari": "https://github.com/Top-gg-Community/python-sdk/tree/master/examples/hikari_example", } diff --git a/docs/examples.rst b/docs/examples/index.rst similarity index 58% rename from docs/examples.rst rename to docs/examples/index.rst index c18a83f6..20573261 100644 --- a/docs/examples.rst +++ b/docs/examples/index.rst @@ -5,5 +5,5 @@ Examples .. toctree:: :maxdepth: 2 - examples/discord_py - examples/hikari \ No newline at end of file + discord_py + hikari \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index f616a71c..2f9bd0ea 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,9 +10,6 @@ Top.gg Python Library .. image:: https://img.shields.io/pypi/v/topggpy.svg :target: https://pypi.python.org/pypi/topggpy :alt: View on PyPI -.. image:: https://img.shields.io/pypi/pyversions/topggpy.svg - :target: https://pypi.python.org/pypi/topggpy - :alt: v1.0.0 .. image:: https://readthedocs.org/projects/topggpy/badge/?version=latest :target: https://topggpy.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status @@ -49,8 +46,8 @@ Additional information :maxdepth: 2 :hidden: - api - examples + api/index.rst + examples/index.rst whats_new repository support \ No newline at end of file From 1ed5505e7f6bde3fe6c626466a961c41e67dba81 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 17:48:20 +0700 Subject: [PATCH 25/71] doc: tweaks --- topgg/client.py | 7 +++++-- topgg/http.py | 5 ++++- topgg/webhook.py | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/topgg/client.py b/topgg/client.py index 1be4edda..b895806e 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -184,7 +184,7 @@ async def get_bot_votes(self) -> t.List[types.BriefUserData]: This API endpoint is only available to the bot's owner. Returns: - List[:obj:`~.types.BriefUserData` ]: + List[:obj:`~.types.BriefUserData`]: Users who voted for your bot. Raises: @@ -225,7 +225,10 @@ async def get_bots( search: t.Optional[t.Dict[str, t.Any]] = None, fields: t.Optional[t.List[str]] = None, ) -> types.DataDict[str, t.Any]: - """This function is deprecated.""" + """ + Warning: + This function is deprecated. + """ warnings.warn("get_bots is now deprecated.", DeprecationWarning) diff --git a/topgg/http.py b/topgg/http.py index 00717666..4333c4a2 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -202,7 +202,10 @@ def get_bots( search: Dict[str, str], fields: Sequence[str], ) -> Coroutine[Any, Any, dict]: - """This function is now deprecated.""" + """ + Warning: + This function is deprecated. + """ warnings.warn("get_bots is now deprecated.", DeprecationWarning) diff --git a/topgg/webhook.py b/topgg/webhook.py index 01b30f7b..9a3fd7f8 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -87,7 +87,7 @@ def endpoint(self, endpoint_: t.Optional["WebhookEndpoint"] = None) -> t.Any: The endpoint to add. Returns: - Union[:obj:`WebhookManager`, :obj:`BoundWebhookEndpoint` ]: + Union[:obj:`WebhookManager`, :obj:`BoundWebhookEndpoint`]: An instance of :obj:`WebhookManager` if endpoint was provided, otherwise :obj:`BoundWebhookEndpoint`. From ad4386b3affffa3b6ed06af32d2b429435106d0a Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 17:52:03 +0700 Subject: [PATCH 26/71] doc: show monthly downloads --- docs/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 2f9bd0ea..82acd4af 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,9 +10,9 @@ Top.gg Python Library .. image:: https://img.shields.io/pypi/v/topggpy.svg :target: https://pypi.python.org/pypi/topggpy :alt: View on PyPI -.. image:: https://readthedocs.org/projects/topggpy/badge/?version=latest +.. image:: https://img.shields.io/pypi/dm/topggpy?style=flat-square :target: https://topggpy.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status + :alt: Monthly PyPI downloads A simple API wrapper for `Top.gg `_ written in Python. From dd1a277a9399e236db15a0d6b5d11a9a1e19d1cb Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 17:54:23 +0700 Subject: [PATCH 27/71] doc: show up monthly pypi downloads --- README.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 99fa29e5..b29327f0 100644 --- a/README.rst +++ b/README.rst @@ -5,12 +5,9 @@ Top.gg Python Library .. image:: https://img.shields.io/pypi/v/topggpy.svg :target: https://pypi.python.org/pypi/topggpy :alt: View on PyPI -.. image:: https://img.shields.io/pypi/pyversions/topggpy.svg - :target: https://pypi.python.org/pypi/topggpy - :alt: v1.0.0 -.. image:: https://readthedocs.org/projects/topggpy/badge/?version=latest +.. image:: https://img.shields.io/pypi/dm/topggpy?style=flat-square :target: https://topggpy.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status + :alt: Monthly PyPI downloads A simple API wrapper for `Top.gg `_ written in Python. From cd6380f72ccdd3d59657faed79c320c69b85ed72 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 17:59:02 +0700 Subject: [PATCH 28/71] doc: use pip, not pip3 --- README.rst | 2 +- docs/index.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index b29327f0..423eae6a 100644 --- a/README.rst +++ b/README.rst @@ -16,7 +16,7 @@ Installation .. code:: bash - pip3 install topggpy + pip install topggpy Documentation ------------- diff --git a/docs/index.rst b/docs/index.rst index 82acd4af..f2aebe23 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,7 +21,7 @@ Installation .. code:: bash - pip3 install topggpy + pip install topggpy Features -------- From 023d63e0af639078aa2f2640586f4a7c68714319 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 28 Mar 2024 21:16:10 +0700 Subject: [PATCH 29/71] meta: update project URLs --- README.rst | 2 +- pyproject.toml | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 423eae6a..928930b5 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ Installation Documentation ------------- -Documentation can be found `here `_ +Documentation can be found `here `_ Features -------- diff --git a/pyproject.toml b/pyproject.toml index fc1cdce2..5c2dc6f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ classifiers = [ requires-python = ">=3.8" [project.urls] -Homepage = "https://topggpy.readthedocs.io/en/stable/" -Documentation = "https://topggpy.readthedocs.io/en/stable/" -Repository = "https://github.com/top-gg-community/python-sdk" \ No newline at end of file +Documentation = "https://topggpy.readthedocs.io/en/latest/" +"Release notes" = "https://topggpy.readthedocs.io/en/latest/whats_new.html" +Repository = "https://github.com/top-gg-community/python-sdk" +"Support server" = "https://discord.gg/dbl" \ No newline at end of file From f80bd29fb4bc6430d3cddb0e2497f4e0a17112ed Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 6 Jun 2024 10:30:30 +0700 Subject: [PATCH 30/71] fix: shutdown web.Application on close() --- topgg/webhook.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/topgg/webhook.py b/topgg/webhook.py index 9a3fd7f8..a491fb60 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -3,6 +3,7 @@ # The MIT License (MIT) # Copyright (c) 2021 Assanali Mukhanov +# Copyright (c) 2024 null8626 # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -70,7 +71,6 @@ class WebhookManager(DataContainerMixin): def __init__(self) -> None: super().__init__() - self.__app = web.Application() self._is_running = False @t.overload @@ -120,6 +120,8 @@ async def start(self, port: int) -> None: port (int) The port to run the webhook on. """ + + self.__app = web.Application() runner = web.AppRunner(self.__app) await runner.setup() self._webserver = web.TCPSite(runner, "0.0.0.0", port) @@ -144,6 +146,7 @@ def app(self) -> web.Application: async def close(self) -> None: """Stops the webhook.""" await self._webserver.stop() + await self.__app.shutdown() self._is_running = False def _get_handler(self, type_: WebhookType, auth: str, callback: t.Callable[..., t.Any]) -> _HandlerT: From dcfdaea0f04a252461dbafa88ea17cb01e241805 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 6 Jun 2024 13:29:09 +0700 Subject: [PATCH 31/71] fix: initialize __app as None at initialization --- topgg/webhook.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/topgg/webhook.py b/topgg/webhook.py index a491fb60..b3c961b4 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -71,6 +71,8 @@ class WebhookManager(DataContainerMixin): def __init__(self) -> None: super().__init__() + + self.__app = None self._is_running = False @t.overload @@ -145,6 +147,7 @@ def app(self) -> web.Application: async def close(self) -> None: """Stops the webhook.""" + await self._webserver.stop() await self.__app.shutdown() self._is_running = False From 549eb593d47f516bc0c131300d93aecbeb1611e1 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 29 Jun 2024 18:07:20 +0700 Subject: [PATCH 32/71] meta: bump aiohttp --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5c2dc6f0..80a0f4d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.rst" license = { text = "MIT" } authors = [{ name = "null8626" }, { name = "Top.gg" }] keywords = ["discord", "bot", "topgg", "top.gg"] -dependencies = ["aiohttp>=3.9.0"] +dependencies = ["aiohttp>=3.9.5"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", diff --git a/requirements.txt b/requirements.txt index 1b80c609..86edf4d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -aiohttp>=3.9.0 \ No newline at end of file +aiohttp>=3.9.5 \ No newline at end of file From 47e96f51766b51c2504b7c440338b5f718c8675b Mon Sep 17 00:00:00 2001 From: null8626 Date: Mon, 1 Jul 2024 21:41:26 +0700 Subject: [PATCH 33/71] refactor: remove unneeded non-base64 removal --- topgg/client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/topgg/client.py b/topgg/client.py index b895806e..50e0fc47 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -27,7 +27,6 @@ import base64 import json -import re import typing as t import warnings @@ -70,7 +69,7 @@ def __init__( self._token = token try: - encoded_json = re.sub(r"[^a-zA-Z0-9\+\/]+", "", token.split(".")[1]) + encoded_json = token.split(".")[1] encoded_json += "=" * (4 - (len(encoded_json) % 4)) self.bot_id = int(json.loads(base64.b64decode(encoded_json))["id"]) From 1d3f36d4feb24be685839d941553b86bcaf17c44 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 24 Sep 2024 08:51:30 +0700 Subject: [PATCH 34/71] deps: manually bump aiohttp version to at least version 3.10.5 --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 80a0f4d2..cebb7cbc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.rst" license = { text = "MIT" } authors = [{ name = "null8626" }, { name = "Top.gg" }] keywords = ["discord", "bot", "topgg", "top.gg"] -dependencies = ["aiohttp>=3.9.5"] +dependencies = ["aiohttp>=3.10.5"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", diff --git a/requirements.txt b/requirements.txt index 86edf4d0..225877d9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -aiohttp>=3.9.5 \ No newline at end of file +aiohttp>=3.10.5 \ No newline at end of file From 404d7134c131e493eb3efd5cc2da37659c10d296 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 24 Sep 2024 08:52:34 +0700 Subject: [PATCH 35/71] meta: remove unneeded scripts directory as we already have ruff --- MANIFEST.in | 1 - scripts/format.sh | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 scripts/format.sh diff --git a/MANIFEST.in b/MANIFEST.in index 5dec6b10..e1c7d412 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,6 @@ prune .github prune .ruff_cache prune docs prune examples -prune scripts prune tests exclude .gitattributes exclude .gitignore diff --git a/scripts/format.sh b/scripts/format.sh deleted file mode 100644 index 4fa2e31d..00000000 --- a/scripts/format.sh +++ /dev/null @@ -1,2 +0,0 @@ -black . -isort . \ No newline at end of file From 59522092df06092c8fe11cf5a9456bc414136115 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 24 Sep 2024 08:56:37 +0700 Subject: [PATCH 36/71] meta: add .ruff_cache to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 51f781c6..52fd1028 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ docs/_templates .idea/ **/__pycache__/ .coverage +.ruff_cache/ \ No newline at end of file From 9f092a39e939b7d7cfa1a713519a09d34c4a2543 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 28 Sep 2024 17:28:04 +0700 Subject: [PATCH 37/71] meta: update keywords --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index cebb7cbc..1b4a8f05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ description = "A simple API wrapper for Top.gg written in Python." readme = "README.rst" license = { text = "MIT" } authors = [{ name = "null8626" }, { name = "Top.gg" }] -keywords = ["discord", "bot", "topgg", "top.gg"] +keywords = ["discord", "discord-bot", "topgg"] dependencies = ["aiohttp>=3.10.5"] classifiers = [ "Development Status :: 5 - Production/Stable", From 44cc56c6fdeb847a03715a567805cba4c3c44304 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 28 Sep 2024 17:30:01 +0700 Subject: [PATCH 38/71] deps: bump aiohttp dependency version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1b4a8f05..112daa49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.rst" license = { text = "MIT" } authors = [{ name = "null8626" }, { name = "Top.gg" }] keywords = ["discord", "discord-bot", "topgg"] -dependencies = ["aiohttp>=3.10.5"] +dependencies = ["aiohttp>=3.10.7"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", From 06966d5dd6bed8820404d1fbd28e0ed1c991298b Mon Sep 17 00:00:00 2001 From: null <60427892+null8626@users.noreply.github.com> Date: Sat, 28 Sep 2024 17:31:17 +0700 Subject: [PATCH 39/71] deps: bump aiohttp version again --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 225877d9..04c8fa4c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -aiohttp>=3.10.5 \ No newline at end of file +aiohttp>=3.10.7 From 0cecb50137e9711fd673e377f158a7c7c12959be Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 28 Sep 2024 19:57:08 +0700 Subject: [PATCH 40/71] meta: remove obselete comments and update __all__ to use tuples [skip ci] --- topgg/__init__.py | 2 -- topgg/autopost.py | 2 +- topgg/client.py | 4 +--- topgg/data.py | 2 +- topgg/errors.py | 6 ++---- topgg/http.py | 4 +--- topgg/ratelimiter.py | 2 -- topgg/types.py | 4 +--- topgg/webhook.py | 6 ++---- 9 files changed, 9 insertions(+), 23 deletions(-) diff --git a/topgg/__init__.py b/topgg/__init__.py index 3c5f8451..5262f281 100644 --- a/topgg/__init__.py +++ b/topgg/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Top.gg Python API Wrapper ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/topgg/autopost.py b/topgg/autopost.py index 7025b9b9..c2ffb3a3 100644 --- a/topgg/autopost.py +++ b/topgg/autopost.py @@ -20,7 +20,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -__all__ = ["AutoPoster"] +__all__ = ("AutoPoster",) import asyncio import datetime diff --git a/topgg/client.py b/topgg/client.py index 50e0fc47..919e6ed9 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # The MIT License (MIT) # Copyright (c) 2021 Assanali Mukhanov @@ -23,7 +21,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -__all__ = ["DBLClient"] +__all__ = ("DBLClient",) import base64 import json diff --git a/topgg/data.py b/topgg/data.py index 7d5f422d..abceaa6c 100644 --- a/topgg/data.py +++ b/topgg/data.py @@ -20,7 +20,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -__all__ = ["data", "DataContainerMixin"] +__all__ = ("data", "DataContainerMixin",) import inspect import typing as t diff --git a/topgg/errors.py b/topgg/errors.py index d8c157a5..914e236a 100644 --- a/topgg/errors.py +++ b/topgg/errors.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # The MIT License (MIT) # Copyright (c) 2021 Assanali Mukhanov @@ -22,7 +20,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -__all__ = [ +__all__ = ( "TopGGException", "ClientException", "ClientStateException", @@ -32,7 +30,7 @@ "Forbidden", "NotFound", "ServerError", -] +) from typing import TYPE_CHECKING, Union diff --git a/topgg/http.py b/topgg/http.py index 4333c4a2..d52dc8da 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # The MIT License (MIT) # Copyright (c) 2021 Assanali Mukhanov @@ -23,7 +21,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -__all__ = ["HTTPClient"] +__all__ = ("HTTPClient",) import asyncio import json diff --git a/topgg/ratelimiter.py b/topgg/ratelimiter.py index d2d75f79..77b7a087 100644 --- a/topgg/ratelimiter.py +++ b/topgg/ratelimiter.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # The MIT License (MIT) # Copyright (c) 2021 Assanali Mukhanov diff --git a/topgg/types.py b/topgg/types.py index 8bfd0671..84d91911 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # The MIT License (MIT) # Copyright (c) 2021 Assanali Mukhanov @@ -22,7 +20,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -__all__ = ["WidgetOptions", "StatsWrapper"] +__all__ = ("WidgetOptions", "StatsWrapper") import dataclasses import typing as t diff --git a/topgg/webhook.py b/topgg/webhook.py index b3c961b4..1e95fba4 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # The MIT License (MIT) # Copyright (c) 2021 Assanali Mukhanov @@ -23,13 +21,13 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -__all__ = [ +__all__ = ( "endpoint", "BoundWebhookEndpoint", "WebhookEndpoint", "WebhookManager", "WebhookType", -] +) import enum import typing as t From 5fa5aaa61fe365b3c531f2e1903bdbe9ddf33d87 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 28 Sep 2024 20:14:12 +0700 Subject: [PATCH 41/71] refactor: add __slots__ --- topgg/autopost.py | 2 +- topgg/client.py | 3 ++- topgg/data.py | 9 ++++++--- topgg/http.py | 18 +++++++++++++++--- topgg/ratelimiter.py | 6 +++++- topgg/types.py | 18 ++++++++++++++++++ topgg/webhook.py | 11 +++++++---- 7 files changed, 54 insertions(+), 13 deletions(-) diff --git a/topgg/autopost.py b/topgg/autopost.py index c2ffb3a3..aae19eaa 100644 --- a/topgg/autopost.py +++ b/topgg/autopost.py @@ -54,7 +54,7 @@ class AutoPoster: An instance of DBLClient. """ - __slots__ = ( + __slots__: t.Tuple[str, ...] = ( "_error", "_success", "_interval", diff --git a/topgg/client.py b/topgg/client.py index 919e6ed9..9622018b 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -53,7 +53,8 @@ class DBLClient(DataContainerMixin): Arbitrary kwargs to be passed to :class:`aiohttp.ClientSession` if session was not provided. """ - __slots__ = ("http", "bot_id", "_token", "_is_closed", "_autopost") + __slots__: t.Tuple[str, ...] = ("http", "bot_id", "_token", "_is_closed", "_autopost") + http: HTTPClient def __init__( diff --git a/topgg/data.py b/topgg/data.py index abceaa6c..639c8d21 100644 --- a/topgg/data.py +++ b/topgg/data.py @@ -20,7 +20,10 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -__all__ = ("data", "DataContainerMixin",) +__all__ = ( + "data", + "DataContainerMixin", +) import inspect import typing as t @@ -61,7 +64,7 @@ def get_stats(client: Client = topgg.data(Client)): class Data(t.Generic[T]): - __slots__ = ("type",) + __slots__: t.Tuple[str, ...] = ("type",) def __init__(self, type_: t.Type[T]) -> None: self.type: t.Type[T] = type_ @@ -75,7 +78,7 @@ class DataContainerMixin: as arguments in your functions. """ - __slots__ = ("_data",) + __slots__: t.Tuple[str, ...] = ("_data",) def __init__(self) -> None: self._data: t.Dict[t.Type, t.Any] = {type(self): self} diff --git a/topgg/http.py b/topgg/http.py index d52dc8da..7c09d5dd 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -29,7 +29,7 @@ import sys import warnings from datetime import datetime -from typing import Any, Coroutine, Dict, Iterable, List, Optional, Sequence, Union, cast +from typing import Any, Coroutine, Dict, Iterable, List, Optional, Sequence, Union, cast, Tuple import aiohttp from aiohttp import ClientResponse @@ -49,6 +49,9 @@ async def _json_or_text( return text +BASE = "https://top.gg/api" + + class HTTPClient: """Represents an HTTP client sending HTTP requests to the Top.gg API. @@ -65,6 +68,16 @@ class HTTPClient: Arbitrary kwargs to be passed to :class:`aiohttp.ClientSession`. """ + __slots__: Tuple[str, ...] = ( + "token", + "_own_session", + "session", + "global_rate_limiter", + "bot_rate_limiter", + "rate_limiters", + "user_agent", + ) + def __init__( self, token: str, @@ -72,7 +85,6 @@ def __init__( session: Optional[aiohttp.ClientSession] = None, **kwargs: Any, ) -> None: - self.BASE = "https://top.gg/api" self.token = token self._own_session = session is None self.session: aiohttp.ClientSession = session or aiohttp.ClientSession(**kwargs) @@ -87,7 +99,7 @@ def __init__( async def request(self, method: str, endpoint: str, **kwargs: Any) -> dict: """Handles requests to the API.""" rate_limiters = self.rate_limiters if endpoint.startswith("/bots") else self.global_rate_limiter - url = f"{self.BASE}{endpoint}" + url = BASE + endpoint if not self.token: raise errors.UnauthorizedDetected("Top.gg API token not provided") diff --git a/topgg/ratelimiter.py b/topgg/ratelimiter.py index 77b7a087..b95e5e92 100644 --- a/topgg/ratelimiter.py +++ b/topgg/ratelimiter.py @@ -24,7 +24,7 @@ import collections from datetime import datetime from types import TracebackType -from typing import Any, Awaitable, Callable, List, Optional, Type +from typing import Any, Awaitable, Callable, List, Optional, Type, Tuple class AsyncRateLimiter: @@ -32,6 +32,8 @@ class AsyncRateLimiter: Provides rate limiting for an operation with a configurable number of requests for a time period. """ + __slots__: Tuple[str, ...] = ("__lock", "callback", "max_calls", "period", "calls") + __lock: asyncio.Lock callback: Optional[Callable[[float], Awaitable[Any]]] max_calls: int @@ -85,6 +87,8 @@ def _timespan(self) -> float: class AsyncRateLimiterManager: + __slots__: Tuple[str, ...] = ("rate_limiters",) + rate_limiters: List[AsyncRateLimiter] def __init__(self, rate_limiters: List[AsyncRateLimiter]): diff --git a/topgg/types.py b/topgg/types.py index 84d91911..746a1099 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -140,6 +140,8 @@ class WidgetOptions(DataDict[str, t.Any]): """Model that represents widget options that are passed to Top.gg widget URL generated via :meth:`DBLClient.generate_widget`.""" + __slots__: t.Tuple[str, ...] = () + id: t.Optional[int] """ID of a bot to generate the widget for. Must resolve to an ID of a listed bot when converted to a string.""" colors: Colors @@ -200,6 +202,8 @@ class BotData(DataDict[str, t.Any]): """Model that contains information about a listed bot on top.gg. The data this model contains can be found `here `__.""" + __slots__: t.Tuple[str, ...] = () + id: int """The ID of the bot.""" @@ -270,6 +274,8 @@ def __init__(self, **kwargs: t.Any): class BotStatsData(DataDict[str, t.Any]): """Model that contains information about a listed bot's guild and shard count.""" + __slots__: t.Tuple[str, ...] = () + server_count: t.Optional[int] """The amount of servers the bot is in.""" shards: t.List[int] @@ -284,6 +290,8 @@ def __init__(self, **kwargs: t.Any): class BriefUserData(DataDict[str, t.Any]): """Model that contains brief information about a Top.gg user.""" + __slots__: t.Tuple[str, ...] = () + id: int """The Discord ID of the user.""" username: str @@ -300,6 +308,8 @@ def __init__(self, **kwargs: t.Any): class SocialData(DataDict[str, str]): """Model that contains social information about a top.gg user.""" + __slots__: t.Tuple[str, ...] = () + youtube: str """The YouTube channel ID of the user.""" reddit: str @@ -316,6 +326,8 @@ class UserData(DataDict[str, t.Any]): """Model that contains information about a top.gg user. The data this model contains can be found `here `__.""" + __slots__: t.Tuple[str, ...] = () + id: int """The ID of the user.""" @@ -353,6 +365,8 @@ def __init__(self, **kwargs: t.Any): class VoteDataDict(DataDict[str, t.Any]): """Base model that represents received information from Top.gg via webhooks.""" + __slots__: t.Tuple[str, ...] = () + type: str """Type of the action (``upvote`` or ``test``).""" user: int @@ -367,6 +381,8 @@ def __init__(self, **kwargs: t.Any): class BotVoteData(VoteDataDict): """Model that contains information about a bot vote.""" + __slots__: t.Tuple[str, ...] = () + bot: int """ID of the bot the user voted for.""" is_weekend: bool @@ -376,6 +392,8 @@ class BotVoteData(VoteDataDict): class GuildVoteData(VoteDataDict): """Model that contains information about a guild vote.""" + __slots__: t.Tuple[str, ...] = () + guild: int """ID of the guild the user voted for.""" diff --git a/topgg/webhook.py b/topgg/webhook.py index 1e95fba4..0b85bd93 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -50,6 +50,8 @@ class WebhookType(enum.Enum): """An enum that represents the type of an endpoint.""" + __slots__: t.Tuple[str, ...] = () + BOT = enum.auto() """Marks the endpoint as a bot webhook.""" @@ -62,10 +64,11 @@ class WebhookManager(DataContainerMixin): A class for managing Top.gg webhooks. """ + __slots__: t.Tuple[str, ...] = ("__app", "_webserver", "_is_running") + __app: web.Application _webserver: web.TCPSite - _is_closed: bool - __slots__ = ("__app", "_webserver", "_is_running") + _is_running: bool def __init__(self) -> None: super().__init__() @@ -173,7 +176,7 @@ class WebhookEndpoint: A helper class to setup webhook endpoint. """ - __slots__ = ("_callback", "_auth", "_route", "_type") + __slots__: t.Tuple[str, ...] = ("_callback", "_auth", "_route", "_type") def __init__(self) -> None: self._auth = "" @@ -298,7 +301,7 @@ def on_vote(vote_data: topgg.BotVoteData): ... endpoint.add_to_manager() """ - __slots__ = ("manager",) + __slots__: t.Tuple[str, ...] = ("manager",) def __init__(self, manager: WebhookManager): super().__init__() From e6d3f3b8a6c9b74f8b59a7c52b5484f84a194790 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sun, 29 Sep 2024 15:58:47 +0700 Subject: [PATCH 42/71] meta: use triple quotes for MIT license comments [skip ci] --- examples/discordpy_example/__main__.py | 48 ++++++++++--------- .../discordpy_example/callbacks/autopost.py | 47 +++++++++--------- .../discordpy_example/callbacks/webhook.py | 45 ++++++++--------- examples/hikari_example/__main__.py | 48 ++++++++++--------- examples/hikari_example/callbacks/autopost.py | 47 +++++++++--------- examples/hikari_example/callbacks/webhook.py | 46 +++++++++--------- examples/hikari_example/events/autopost.py | 45 +++++++++-------- examples/hikari_example/events/webhook.py | 38 ++++++++------- topgg/autopost.py | 44 +++++++++-------- topgg/client.py | 46 +++++++++--------- topgg/data.py | 44 +++++++++-------- topgg/errors.py | 44 +++++++++-------- topgg/http.py | 46 +++++++++--------- topgg/ratelimiter.py | 44 +++++++++-------- topgg/types.py | 44 +++++++++-------- topgg/webhook.py | 46 +++++++++--------- 16 files changed, 375 insertions(+), 347 deletions(-) diff --git a/examples/discordpy_example/__main__.py b/examples/discordpy_example/__main__.py index 70771708..2859f8ab 100644 --- a/examples/discordpy_example/__main__.py +++ b/examples/discordpy_example/__main__.py @@ -1,27 +1,29 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Norizon -# Copyright (c) 2024 null8626 - -# 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 discord +""" +The MIT License (MIT) + +Copyright (c) 2021 Norizon +Copyright (c) 2024 null8626 + +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 discord import topgg from .callbacks import autopost, webhook diff --git a/examples/discordpy_example/callbacks/autopost.py b/examples/discordpy_example/callbacks/autopost.py index e6592a6d..2ca58b78 100644 --- a/examples/discordpy_example/callbacks/autopost.py +++ b/examples/discordpy_example/callbacks/autopost.py @@ -1,28 +1,29 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Norizon - -# 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 sys +""" +The MIT License (MIT) + +Copyright (c) 2021 Norizon + +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 sys import discord - import topgg diff --git a/examples/discordpy_example/callbacks/webhook.py b/examples/discordpy_example/callbacks/webhook.py index 358753c1..2207547c 100644 --- a/examples/discordpy_example/callbacks/webhook.py +++ b/examples/discordpy_example/callbacks/webhook.py @@ -1,27 +1,28 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Norizon - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Norizon + +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 discord - import topgg diff --git a/examples/hikari_example/__main__.py b/examples/hikari_example/__main__.py index 82da8ba0..e962f95a 100644 --- a/examples/hikari_example/__main__.py +++ b/examples/hikari_example/__main__.py @@ -1,27 +1,29 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Norizon -# Copyright (c) 2024 null8626 - -# 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 hikari +""" +The MIT License (MIT) + +Copyright (c) 2021 Norizon +Copyright (c) 2024 null8626 + +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 hikari import topgg from .callbacks import autopost, webhook diff --git a/examples/hikari_example/callbacks/autopost.py b/examples/hikari_example/callbacks/autopost.py index 6bab89f2..d50080e2 100644 --- a/examples/hikari_example/callbacks/autopost.py +++ b/examples/hikari_example/callbacks/autopost.py @@ -1,28 +1,29 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Norizon - -# 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 logging +""" +The MIT License (MIT) + +Copyright (c) 2021 Norizon + +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 logging import hikari - import topgg # from ..events.autopost import AutoPostErrorEvent, AutoPostSuccessEvent diff --git a/examples/hikari_example/callbacks/webhook.py b/examples/hikari_example/callbacks/webhook.py index 49daedaa..222f9445 100644 --- a/examples/hikari_example/callbacks/webhook.py +++ b/examples/hikari_example/callbacks/webhook.py @@ -1,29 +1,29 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Norizon - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Norizon + +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 logging - import topgg - # import hikari diff --git a/examples/hikari_example/events/autopost.py b/examples/hikari_example/events/autopost.py index ddc7aa22..02e9967d 100644 --- a/examples/hikari_example/events/autopost.py +++ b/examples/hikari_example/events/autopost.py @@ -1,24 +1,27 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Norizon - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Norizon + +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 attr import hikari diff --git a/examples/hikari_example/events/webhook.py b/examples/hikari_example/events/webhook.py index b9b6d21f..8183b742 100644 --- a/examples/hikari_example/events/webhook.py +++ b/examples/hikari_example/events/webhook.py @@ -1,27 +1,29 @@ -# The MIT License (MIT) +""" +The MIT License (MIT) -# Copyright (c) 2021 Norizon +Copyright (c) 2021 Norizon -# 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: +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 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. +""" -# 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 attr import hikari - import topgg diff --git a/topgg/autopost.py b/topgg/autopost.py index aae19eaa..cfc16fca 100644 --- a/topgg/autopost.py +++ b/topgg/autopost.py @@ -1,24 +1,26 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Norizon - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Norizon + +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. +""" __all__ = ("AutoPoster",) diff --git a/topgg/client.py b/topgg/client.py index 9622018b..e7c32dbe 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -1,25 +1,27 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Assanali Mukhanov -# Copyright (c) 2024 null8626 - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Assanali Mukhanov +Copyright (c) 2024 null8626 + +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. +""" __all__ = ("DBLClient",) diff --git a/topgg/data.py b/topgg/data.py index 639c8d21..0d05668b 100644 --- a/topgg/data.py +++ b/topgg/data.py @@ -1,24 +1,26 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Norizon - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Norizon + +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. +""" __all__ = ( "data", diff --git a/topgg/errors.py b/topgg/errors.py index 914e236a..f4381c0d 100644 --- a/topgg/errors.py +++ b/topgg/errors.py @@ -1,24 +1,26 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Assanali Mukhanov - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Assanali Mukhanov + +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. +""" __all__ = ( "TopGGException", diff --git a/topgg/http.py b/topgg/http.py index 7c09d5dd..e2de080c 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -1,25 +1,27 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Assanali Mukhanov -# Copyright (c) 2024 null8626 - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Assanali Mukhanov +Copyright (c) 2024 null8626 + +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. +""" __all__ = ("HTTPClient",) diff --git a/topgg/ratelimiter.py b/topgg/ratelimiter.py index b95e5e92..9323081b 100644 --- a/topgg/ratelimiter.py +++ b/topgg/ratelimiter.py @@ -1,24 +1,26 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Assanali Mukhanov - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Assanali Mukhanov + +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 collections diff --git a/topgg/types.py b/topgg/types.py index 746a1099..6aee3e13 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -1,24 +1,26 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Assanali Mukhanov - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Assanali Mukhanov + +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. +""" __all__ = ("WidgetOptions", "StatsWrapper") diff --git a/topgg/webhook.py b/topgg/webhook.py index 0b85bd93..46b94ccd 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -1,25 +1,27 @@ -# The MIT License (MIT) - -# Copyright (c) 2021 Assanali Mukhanov -# Copyright (c) 2024 null8626 - -# 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. +""" +The MIT License (MIT) + +Copyright (c) 2021 Assanali Mukhanov +Copyright (c) 2024 null8626 + +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. +""" __all__ = ( "endpoint", From 6a4ca42093c5e230e1877e69562b64ceeed033fb Mon Sep 17 00:00:00 2001 From: null8626 Date: Mon, 30 Sep 2024 10:23:08 +0700 Subject: [PATCH 43/71] deps: bump minimum aiohttp version to version 3.10.8 --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 112daa49..d1765e4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.rst" license = { text = "MIT" } authors = [{ name = "null8626" }, { name = "Top.gg" }] keywords = ["discord", "discord-bot", "topgg"] -dependencies = ["aiohttp>=3.10.7"] +dependencies = ["aiohttp>=3.10.8"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", diff --git a/requirements.txt b/requirements.txt index 04c8fa4c..f7e0272f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -aiohttp>=3.10.7 +aiohttp>=3.10.8 From abe17ca3e58e49cd804bb704f3216a8de3c7ddb7 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 8 Oct 2024 11:31:44 +0700 Subject: [PATCH 44/71] meta: add support for python 3.13 --- .github/workflows/python-package.yml | 2 +- .github/workflows/python-publish.yml | 2 +- .readthedocs.yml | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 473cba85..5c782b4c 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ 3.8, 3.9, '3.10', 3.11, 3.12 ] + python-version: [ 3.8, 3.9, '3.10', 3.11, 3.12, 3.13 ] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 5ff91b04..684f44ad 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.12' + python-version: '3.13' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.readthedocs.yml b/.readthedocs.yml index 35fe3221..6780bd88 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -7,7 +7,7 @@ build: image: latest python: - version: 3.12 + version: 3.13 install: - requirements: requirements.txt - requirements: requirements-docs.txt diff --git a/pyproject.toml b/pyproject.toml index d1765e4b..c5b4bd82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.rst" license = { text = "MIT" } authors = [{ name = "null8626" }, { name = "Top.gg" }] keywords = ["discord", "discord-bot", "topgg"] -dependencies = ["aiohttp>=3.10.8"] +dependencies = ["aiohttp>=3.10.9"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", diff --git a/requirements.txt b/requirements.txt index f7e0272f..9b5cf4af 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -aiohttp>=3.10.8 +aiohttp>=3.10.9 From e535b6290d1771c7d1cb180d7bdc5a78218f07cc Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 8 Oct 2024 11:33:58 +0700 Subject: [PATCH 45/71] meta: add python 3.13 classifier --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index c5b4bd82..09b654fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Internet", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", From 286a46ca2bc69963beca21e7c4cc094beb600011 Mon Sep 17 00:00:00 2001 From: null8626 Date: Mon, 14 Oct 2024 19:02:24 +0700 Subject: [PATCH 46/71] doc: add ~ prefix --- topgg/client.py | 4 ++-- topgg/webhook.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/topgg/client.py b/topgg/client.py index e7c32dbe..80054e89 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -49,10 +49,10 @@ class DBLClient(DataContainerMixin): token (:obj:`str`): Your bot's Top.gg API Token. Keyword Args: - session (:class:`aiohttp.ClientSession`) + session (:class:`~aiohttp.ClientSession`) An `aiohttp session`_ to use for requests to the API. **kwargs: - Arbitrary kwargs to be passed to :class:`aiohttp.ClientSession` if session was not provided. + Arbitrary kwargs to be passed to :class:`~aiohttp.ClientSession` if session was not provided. """ __slots__: t.Tuple[str, ...] = ("http", "bot_id", "_token", "_is_closed", "_autopost") diff --git a/topgg/webhook.py b/topgg/webhook.py index 46b94ccd..b3add959 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -143,7 +143,7 @@ def app(self) -> web.Application: """Returns the internal web application that handles webhook requests. Returns: - :class:`aiohttp.web.Application`: + :class:`~aiohttp.web.Application`: The internal web application. """ return self.__app From d10e7af99cea9af006af06784211f0a4c2083d0a Mon Sep 17 00:00:00 2001 From: null8626 Date: Mon, 14 Oct 2024 19:55:34 +0700 Subject: [PATCH 47/71] doc: add raw API reference redirect page --- docs/conf.py | 2 +- docs/index.rst | 3 ++- docs/raw-api-reference.rst | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 docs/raw-api-reference.rst diff --git a/docs/conf.py b/docs/conf.py index 50eaf9d5..eddd0879 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,13 +43,13 @@ intersphinx_mapping = { "py": ("https://docs.python.org/3", None), - "discord": ("https://discordpy.readthedocs.io/en/latest/", None), "aiohttp": ("https://docs.aiohttp.org/en/stable/", None), } redirects = { "repository": "https://github.com/top-gg-community/python-sdk", "support": "https://discord.gg/dbl", + "raw-api-reference": "https://docs.top.gg/docs/", "api/index": "autopost.html", "examples/index": "discord_py.html", "examples/discord_py": "https://github.com/Top-gg-Community/python-sdk/tree/master/examples/discordpy_example", diff --git a/docs/index.rst b/docs/index.rst index f2aebe23..09e04bd4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -50,4 +50,5 @@ Additional information examples/index.rst whats_new repository - support \ No newline at end of file + support + raw-api-reference \ No newline at end of file diff --git a/docs/raw-api-reference.rst b/docs/raw-api-reference.rst new file mode 100644 index 00000000..7486b294 --- /dev/null +++ b/docs/raw-api-reference.rst @@ -0,0 +1,5 @@ +================= +Raw API reference +================= + +You should be redirected in a few moments. Otherwise, click here: https://docs.top.gg/docs/ \ No newline at end of file From 3707ff5579909fb44930eacb5bb9a972eef86281 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 15 Oct 2024 14:06:13 +0700 Subject: [PATCH 48/71] doc: simplify documentation hyperlinks [skip ci] --- docs/whats_new.rst | 60 +++++++++++++++++++++++----------------------- topgg/errors.py | 4 ++-- topgg/types.py | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/whats_new.rst b/docs/whats_new.rst index d506500b..6faad770 100644 --- a/docs/whats_new.rst +++ b/docs/whats_new.rst @@ -9,34 +9,34 @@ v2.0.1 * Added Python 3.12 support (:issue:`78`) * Dropped Python 3.6 and 3.7 support (:issue:`75`) * Removed the need to manually set a ``default_bot_id`` property -* :attr:`~.BotData.def_avatar` is now an optional string -* :meth:`~.DBLClient.get_bots` is now deprecated -* :meth:`~.DBLClient.get_guild_count` no longer accepts a ``bot_id`` argument -* :meth:`~.DBLClient.get_bot_votes` no longer raises a :class:`~.ClientException` without a ``default_bot_id`` property -* :meth:`~.DBLClient.get_bot_info` no longer raises a :class:`~.ClientException` without a ``default_bot_id`` property -* :meth:`~.DBLClient.generate_widget` no longer raises a :class:`~.ClientException` without a ``default_bot_id`` property +* :attr:`.BotData.def_avatar` is now an optional string +* :meth:`.DBLClient.get_bots` is now deprecated +* :meth:`.DBLClient.get_guild_count` no longer accepts a ``bot_id`` argument +* :meth:`.DBLClient.get_bot_votes` no longer raises a :class:`.ClientException` without a ``default_bot_id`` property +* :meth:`.DBLClient.get_bot_info` no longer raises a :class:`.ClientException` without a ``default_bot_id`` property +* :meth:`.DBLClient.generate_widget` no longer raises a :class:`.ClientException` without a ``default_bot_id`` property * Documentation overhaul v2.0.0a ======= -* :obj:`~.DBLClient` now doesn't take in ``discord.Client`` instance +* :obj:`.DBLClient` now doesn't take in ``discord.Client`` instance * Introduced new `autopost `__ and `data injection `__ API * `Webhook `__ API breaking changes * No longer depends on any Discord API wrapper -* :obj:`~.GuildVoteData` alias +* :obj:`.GuildVoteData` alias v1.4.0 ====== -* The type of data passed to ``on_dbl_vote`` has been changed from :class:`~.dict` to :obj:`BotVoteData` -* The type of data passed to ``on_dsl_vote`` has been changed from :class:`~.dict` to :obj:`ServerVoteData` +* The type of data passed to ``on_dbl_vote`` has been changed from :class:`.dict` to :obj:`BotVoteData` +* The type of data passed to ``on_dsl_vote`` has been changed from :class:`.dict` to :obj:`ServerVoteData` v1.3.0 ====== * Introduced `global ratelimiter `__ to follow Top.gg global ratelimits - * Fixed an :exc:`AttributeError` raised by :meth:`~.HTTPClient.request` + * Fixed an :exc:`AttributeError` raised by :meth:`.HTTPClient.request` * `Resource-specific ratelimit `__ is now actually resource-specific @@ -44,41 +44,41 @@ v1.2.0 ====== * Introduced global ratelimiter along with bot endpoints ratelimiter -* Follow consistency with typing in :class:`~.HTTPClient` and :class:`~.DBLClient` along with updated docstrings (:issue:`55`) +* Follow consistency with typing in :class:`.HTTPClient` and :class:`.DBLClient` along with updated docstrings (:issue:`55`) v1.1.0 ====== * Introduced `data models `__ - * :meth:`~.DBLClient.get_bot_votes` now returns a list of :class:`~.BriefUserData` objects + * :meth:`.DBLClient.get_bot_votes` now returns a list of :class:`.BriefUserData` objects - * :meth:`~.DBLClient.get_bot_info` now returns a :class:`~.BotData` object + * :meth:`.DBLClient.get_bot_info` now returns a :class:`.BotData` object - * :meth:`~.DBLClient.get_guild_count` now returns a :class:`~.BotStatsData` object + * :meth:`.DBLClient.get_guild_count` now returns a :class:`.BotStatsData` object - * :meth:`~.DBLClient.get_user_info` now returns a :class:`~.UserData` object + * :meth:`.DBLClient.get_user_info` now returns a :class:`.UserData` object -* :meth:`~.WebhookManager.run` now returns an :class:`~.asyncio.Task`, meaning it can now be optionally awaited +* :meth:`.WebhookManager.run` now returns an :class:`.asyncio.Task`, meaning it can now be optionally awaited v1.0.1 ====== -* :attr:`~.WebhookManager.webserver` now instead returns :class:`~.aiohttp.web.Application` for ease of use +* :attr:`.WebhookManager.webserver` now instead returns :class:`.aiohttp.web.Application` for ease of use v1.0.0 ====== * Renamed the module folder from ``dbl`` to ``topgg`` -* Added ``post_shard_count`` argument to :meth:`~.DBLClient.post_guild_count` +* Added ``post_shard_count`` argument to :meth:`.DBLClient.post_guild_count` * Autopost now supports automatic shard posting (:issue:`42`) * Large webhook system rework, read the :obj:`api/webhook` section for more * Added support for server webhooks -* Renamed ``DBLException`` to :class:`~.TopGGException` -* Renamed ``DBLClient.get_bot_upvotes()`` to :meth:`~.DBLClient.get_bot_votes` -* Added :meth:`~.DBLClient.generate_widget` along with the ``widgets`` section in the documentation +* Renamed ``DBLException`` to :class:`.TopGGException` +* Renamed ``DBLClient.get_bot_upvotes()`` to :meth:`.DBLClient.get_bot_votes` +* Added :meth:`.DBLClient.generate_widget` along with the ``widgets`` section in the documentation * Implemented a properly working ratelimiter * Added :func:`on_autopost_error` * All autopost events now follow ``on_autopost_x`` naming format, e.g. :func:`on_autopost_error`, :func:`on_autopost_success` @@ -87,7 +87,7 @@ v1.0.0 v0.4.0 ====== -* :meth:`~.DBLClient.post_guild_count` now supports a custom ``guild_count`` argument, which accepts either an integer or list of integers +* :meth:`.DBLClient.post_guild_count` now supports a custom ``guild_count`` argument, which accepts either an integer or list of integers * Reworked how shard info is posted * Removed ``InvalidArgument`` and ``ConnectionClosed`` exceptions * Added ``ServerError`` exception @@ -96,12 +96,12 @@ v0.3.3 ====== * Internal changes regarding support of Top.gg migration -* Fixed errors raised when using :meth:`~.DBLClient.close` without built-in webhook +* Fixed errors raised when using :meth:`.DBLClient.close` without built-in webhook v0.3.2 ====== -* ``Client`` class has been renamed to :class:`~.DBLClient` +* ``Client`` class has been renamed to :class:`.DBLClient` v0.3.1 ====== @@ -113,7 +113,7 @@ v0.3.1 v0.3.0 ====== -* :class:`~.DBLClient` now has ``autopost`` kwarg that will post server count automatically every 30 minutes +* :class:`.DBLClient` now has ``autopost`` kwarg that will post server count automatically every 30 minutes * Fixed code 403 errors * Added ``on_dbl_vote``, an event that is called when you test your webhook * Added ``on_dbl_test``, an event that is called when someone tests your webhook @@ -123,7 +123,7 @@ v0.2.1 * Added webhook * Removed support for discord.py versions lower than 1.0.0 -* Made :meth:`~.DBLClient.get_weekend_status` return a boolean value +* Made :meth:`.DBLClient.get_weekend_status` return a boolean value * Added webhook example in README * Removed ``post_server_count`` and ``get_server_count`` @@ -138,9 +138,9 @@ v0.2.0 * Made ``get_server_count`` an alias for ``get_guild_count`` -* Added :meth:`~.DBLClient.get_weekend_status` -* Removed all parameters from :meth:`~.DBLClient.get_upvote_info` -* Added limit to :meth:`~.DBLClient.get_bots` +* Added :meth:`.DBLClient.get_weekend_status` +* Removed all parameters from :meth:`.DBLClient.get_upvote_info` +* Added limit to :meth:`.DBLClient.get_bots` * Fixed example in README v0.1.6 diff --git a/topgg/errors.py b/topgg/errors.py index f4381c0d..e15d39be 100644 --- a/topgg/errors.py +++ b/topgg/errors.py @@ -48,14 +48,14 @@ class TopGGException(Exception): class ClientException(TopGGException): - """Exception that's thrown when an operation in the :class:`~.DBLClient` fails. + """Exception that's thrown when an operation in the :class:`.DBLClient` fails. These are usually for exceptions that happened due to user input. """ class ClientStateException(ClientException): - """Exception that's thrown when an operation happens in a closed :obj:`~.DBLClient` instance.""" + """Exception that's thrown when an operation happens in a closed :obj:`.DBLClient` instance.""" class HTTPException(TopGGException): diff --git a/topgg/types.py b/topgg/types.py index 6aee3e13..f2204c5a 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -374,7 +374,7 @@ class VoteDataDict(DataDict[str, t.Any]): user: int """ID of the voter.""" query: DataDict - """Query parameters in :obj:`~.DataDict`.""" + """Query parameters in :obj:`.DataDict`.""" def __init__(self, **kwargs: t.Any): super().__init__(**parse_vote_dict(kwargs)) From af94e5ea1d654c8074ad9faf5d0b9432456f2842 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 19 Oct 2024 12:35:21 +0700 Subject: [PATCH 49/71] refactor: use fromisoformat instead of strptime --- topgg/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topgg/types.py b/topgg/types.py index f2204c5a..ea1b831f 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -90,7 +90,7 @@ def parse_bot_dict(d: dict) -> dict: data = parse_dict(d.copy()) if data.get("date") and not isinstance(data["date"], datetime): - data["date"] = datetime.strptime(data["date"], "%Y-%m-%dT%H:%M:%S.%fZ") + data["date"] = datetime.fromisoformat(data["date"]) if data.get("owners"): data["owners"] = [int(e) for e in data["owners"]] From 0bf758fcb55e3aeef1d6850a1552cc1dbb0ce099 Mon Sep 17 00:00:00 2001 From: null8626 Date: Thu, 24 Oct 2024 12:23:08 +0700 Subject: [PATCH 50/71] doc: reorder documentation tabs --- docs/index.rst | 4 ++-- docs/{support.rst => support-server.rst} | 0 docs/{whats_new.rst => whats-new.rst} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename docs/{support.rst => support-server.rst} (100%) rename docs/{whats_new.rst => whats-new.rst} (100%) diff --git a/docs/index.rst b/docs/index.rst index 09e04bd4..1ce0a3dd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -48,7 +48,7 @@ Additional information api/index.rst examples/index.rst - whats_new + whats-new + support-server repository - support raw-api-reference \ No newline at end of file diff --git a/docs/support.rst b/docs/support-server.rst similarity index 100% rename from docs/support.rst rename to docs/support-server.rst diff --git a/docs/whats_new.rst b/docs/whats-new.rst similarity index 100% rename from docs/whats_new.rst rename to docs/whats-new.rst From 803a7a17df77e3f8206492f551988f5df7085341 Mon Sep 17 00:00:00 2001 From: null8626 Date: Wed, 27 Nov 2024 18:20:32 +0700 Subject: [PATCH 51/71] fix: remove self.__app's None assignment --- topgg/webhook.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/topgg/webhook.py b/topgg/webhook.py index b3add959..9e2bc260 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -75,7 +75,7 @@ class WebhookManager(DataContainerMixin): def __init__(self) -> None: super().__init__() - self.__app = None + self.__app = web.Application() self._is_running = False @t.overload @@ -126,7 +126,6 @@ async def start(self, port: int) -> None: The port to run the webhook on. """ - self.__app = web.Application() runner = web.AppRunner(self.__app) await runner.setup() self._webserver = web.TCPSite(runner, "0.0.0.0", port) From 6e0eb06c7e2db413b5c43ed75a49b258e4ed76ea Mon Sep 17 00:00:00 2001 From: null8626 Date: Wed, 27 Nov 2024 18:25:43 +0700 Subject: [PATCH 52/71] deps: bump aiohttp version to 3.11.7 --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 09b654fc..899bab75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.rst" license = { text = "MIT" } authors = [{ name = "null8626" }, { name = "Top.gg" }] keywords = ["discord", "discord-bot", "topgg"] -dependencies = ["aiohttp>=3.10.9"] +dependencies = ["aiohttp>=3.11.7"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", diff --git a/requirements.txt b/requirements.txt index 9b5cf4af..a107e820 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -aiohttp>=3.10.9 +aiohttp>=3.11.7 From 1d6da7de4f78564c90fae59191901c6182a2cd3d Mon Sep 17 00:00:00 2001 From: null8626 Date: Fri, 6 Dec 2024 17:45:24 +0700 Subject: [PATCH 53/71] deps: bump aiohttp dependency to version 3.11.10 --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 899bab75..9580ebb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.rst" license = { text = "MIT" } authors = [{ name = "null8626" }, { name = "Top.gg" }] keywords = ["discord", "discord-bot", "topgg"] -dependencies = ["aiohttp>=3.11.7"] +dependencies = ["aiohttp>=3.11.10"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", diff --git a/requirements.txt b/requirements.txt index a107e820..cf64d0f8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -aiohttp>=3.11.7 +aiohttp>=3.11.10 From d0c9da015faf33c1493d303f6c8502d827c3687d Mon Sep 17 00:00:00 2001 From: null8626 Date: Fri, 14 Feb 2025 15:39:10 +0700 Subject: [PATCH 54/71] meta: update secrets name from PYPI_PASSWORD to PYPI_TOKEN --- .github/workflows/python-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 684f44ad..d91de2dd 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -25,7 +25,7 @@ jobs: - name: Build and publish env: TWINE_USERNAME: '__token__' - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} run: | python setup.py sdist bdist_wheel twine upload dist/* \ No newline at end of file From 9952b208c2d8f1da7a6c7664367259e5f913d2b0 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 15 Feb 2025 09:58:07 +0700 Subject: [PATCH 55/71] fix: fix iso timestamp for python versions prior to 3.11 --- topgg/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topgg/types.py b/topgg/types.py index ea1b831f..4d704743 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -90,7 +90,7 @@ def parse_bot_dict(d: dict) -> dict: data = parse_dict(d.copy()) if data.get("date") and not isinstance(data["date"], datetime): - data["date"] = datetime.fromisoformat(data["date"]) + data["date"] = datetime.fromisoformat(data["date"].replace("Z", "+00:00")) if data.get("owners"): data["owners"] = [int(e) for e in data["owners"]] From 14809553dd7679b1312b63c738c685505f0732a5 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 15 Feb 2025 09:59:36 +0700 Subject: [PATCH 56/71] ci: remove support python 3.8 as it's now EOL meta: update pyproject.toml --- .github/workflows/python-package.yml | 2 +- pyproject.toml | 5 ++--- requirements.txt | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5c782b4c..81ae40b8 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ 3.8, 3.9, '3.10', 3.11, 3.12, 3.13 ] + python-version: [ 3.9, '3.10', 3.11, 3.12, 3.13 ] steps: - uses: actions/checkout@v2 diff --git a/pyproject.toml b/pyproject.toml index 9580ebb6..a75debf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.rst" license = { text = "MIT" } authors = [{ name = "null8626" }, { name = "Top.gg" }] keywords = ["discord", "discord-bot", "topgg"] -dependencies = ["aiohttp>=3.11.10"] +dependencies = ["aiohttp>=3.11.12"] classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", @@ -17,7 +17,6 @@ classifiers = [ "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -28,7 +27,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities" ] -requires-python = ">=3.8" +requires-python = ">=3.9" [project.urls] Documentation = "https://topggpy.readthedocs.io/en/latest/" diff --git a/requirements.txt b/requirements.txt index cf64d0f8..4143b58b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -aiohttp>=3.11.10 +aiohttp>=3.11.12 From 8ad44a7e629594f13ae8d14610f5c22a5c218a9f Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 15 Feb 2025 11:58:23 +0700 Subject: [PATCH 57/71] ci: rewrite ci [skip ci] --- .github/workflows/python-package.yml | 38 ---------------------------- .github/workflows/python-publish.yml | 31 ----------------------- .github/workflows/release.yml | 21 +++++++++++++++ .github/workflows/test.yml | 32 +++++++++++++++++++++++ 4 files changed, 53 insertions(+), 69 deletions(-) delete mode 100644 .github/workflows/python-package.yml delete mode 100644 .github/workflows/python-publish.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml deleted file mode 100644 index 81ae40b8..00000000 --- a/.github/workflows/python-package.yml +++ /dev/null @@ -1,38 +0,0 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - -name: Test Python package - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ 3.9, '3.10', 3.11, 3.12, 3.13 ] - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install testing dependencies - uses: py-actions/py-dependency-install@v2 - with: - path: "requirements-dev.txt" - - name: Install itself - run: | - python -m pip install . - - name: Lint with ruff - run: | - ruff check - - name: Test with pytest - run: | - pytest diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml deleted file mode 100644 index d91de2dd..00000000 --- a/.github/workflows/python-publish.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries - -name: Upload Python Package - -on: - release: - types: [ created ] - -jobs: - deploy: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.13' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine -r requirements.txt - - name: Build and publish - env: - TWINE_USERNAME: '__token__' - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - run: | - python setup.py sdist bdist_wheel - twine upload dist/* \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..4d603df0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,21 @@ +name: Publish +on: + release: + types: [created] +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.13 + - name: Install dependencies + run: python3 -m pip install twine + - name: Build and publish + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + run: | + python3 -m build + python3 -m twine upload dist/* \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..fa960764 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,32 @@ +name: Run tests +on: + push: + branches: [master] + paths: + - '.github/workflows/test.yml' + - 'topgg/**/*.py' + pull_request: + paths: + - '.github/workflows/test.yml' + - 'topgg/**/*.py' +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.9, '3.10', 3.11, 3.12, 3.13] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: python3 -m pip install . -r requirements-dev.txt + - name: Run tests + run: python3 -m pytest + - name: Install ruff + if: ${{ matrix.python-version == 3.13 }} + run: python3 -m pip install ruff + - name: Lint with ruff + if: ${{ matrix.python-version == 3.13 }} + run: python3 -m ruff check \ No newline at end of file From f93de301e6a0b0cf5fbefe2adaa1f9c30e55fd84 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 15 Feb 2025 14:55:25 +0700 Subject: [PATCH 58/71] meta: update copyright year --- LICENSE | 2 +- docs/conf.py | 2 +- examples/discordpy_example/__main__.py | 2 +- examples/hikari_example/__main__.py | 2 +- topgg/__init__.py | 2 +- topgg/client.py | 2 +- topgg/http.py | 2 +- topgg/webhook.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/LICENSE b/LICENSE index c4cd04ce..4624af32 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ Copyright 2021 Assanali Mukhanov & Top.gg -Copyright 2024 null8626 & Top.gg +Copyright 2024-2025 null8626 & Top.gg 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: diff --git a/docs/conf.py b/docs/conf.py index eddd0879..b2308b58 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -61,7 +61,7 @@ master_doc = "index" project = "topggpy" -copyright = "2021 Assanali Mukhanov; 2024 null8626" +copyright = "2021 Assanali Mukhanov; 2024-2025 null8626" author = "null8626" release = version diff --git a/examples/discordpy_example/__main__.py b/examples/discordpy_example/__main__.py index 2859f8ab..9ae6595b 100644 --- a/examples/discordpy_example/__main__.py +++ b/examples/discordpy_example/__main__.py @@ -2,7 +2,7 @@ The MIT License (MIT) Copyright (c) 2021 Norizon -Copyright (c) 2024 null8626 +Copyright (c) 2024-2025 null8626 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/examples/hikari_example/__main__.py b/examples/hikari_example/__main__.py index e962f95a..f23b6b4c 100644 --- a/examples/hikari_example/__main__.py +++ b/examples/hikari_example/__main__.py @@ -2,7 +2,7 @@ The MIT License (MIT) Copyright (c) 2021 Norizon -Copyright (c) 2024 null8626 +Copyright (c) 2024-2025 null8626 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/topgg/__init__.py b/topgg/__init__.py index 5262f281..d3e6b2fa 100644 --- a/topgg/__init__.py +++ b/topgg/__init__.py @@ -3,7 +3,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~ A basic wrapper for the Top.gg API. :copyright: (c) 2021 Assanali Mukhanov & Top.gg -:copyright: (c) 2024 null8626 & Top.gg +:copyright: (c) 2024-2025 null8626 & Top.gg :license: MIT, see LICENSE for more details. """ diff --git a/topgg/client.py b/topgg/client.py index 80054e89..f6ae2886 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -2,7 +2,7 @@ The MIT License (MIT) Copyright (c) 2021 Assanali Mukhanov -Copyright (c) 2024 null8626 +Copyright (c) 2024-2025 null8626 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/topgg/http.py b/topgg/http.py index e2de080c..87d7bf2c 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -2,7 +2,7 @@ The MIT License (MIT) Copyright (c) 2021 Assanali Mukhanov -Copyright (c) 2024 null8626 +Copyright (c) 2024-2025 null8626 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/topgg/webhook.py b/topgg/webhook.py index 9e2bc260..9b947d8f 100644 --- a/topgg/webhook.py +++ b/topgg/webhook.py @@ -2,7 +2,7 @@ The MIT License (MIT) Copyright (c) 2021 Assanali Mukhanov -Copyright (c) 2024 null8626 +Copyright (c) 2024-2025 null8626 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), From 0a1bbe284bc1878b2f4605aa6fc0452559de2b6d Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 15 Feb 2025 15:24:59 +0700 Subject: [PATCH 59/71] doc: add raw API documentation link and update .readthedocs.yml --- .readthedocs.yml | 16 +++++++--------- MANIFEST.in | 1 + pyproject.toml | 1 + 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 6780bd88..1bbdc13e 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,13 +1,11 @@ version: 2 - -sphinx: - configuration: docs/conf.py - build: - image: latest - + os: ubuntu-24.04 + tools: + python: '3.13' +sphinx: + configuration: docs/conf.py python: - version: 3.13 install: - - requirements: requirements.txt - - requirements: requirements-docs.txt + - requirements: requirements.txt + - requirements: requirements-docs.txt \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index e1c7d412..bf4d57b9 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,6 +8,7 @@ exclude .gitignore exclude .readthedocs.yml exclude mypy.ini exclude pytest.ini +exclude requirements.txt exclude requirements-dev.txt exclude requirements-docs.txt exclude ruff.toml diff --git a/pyproject.toml b/pyproject.toml index a75debf1..5534328b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ requires-python = ">=3.9" [project.urls] Documentation = "https://topggpy.readthedocs.io/en/latest/" +"Raw API Documentation" = "https://docs.top.gg/docs/" "Release notes" = "https://topggpy.readthedocs.io/en/latest/whats_new.html" Repository = "https://github.com/top-gg-community/python-sdk" "Support server" = "https://discord.gg/dbl" \ No newline at end of file From be287fe1c8372ca1bc74599e6cd6f7e537f51cf7 Mon Sep 17 00:00:00 2001 From: null8626 Date: Sat, 15 Feb 2025 16:50:40 +0700 Subject: [PATCH 60/71] ci: install build as well --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d603df0..bc2c4346 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: with: python-version: 3.13 - name: Install dependencies - run: python3 -m pip install twine + run: python3 -m pip install build twine - name: Build and publish env: TWINE_USERNAME: __token__ From ef7aba982d1e96131a1bce66e61feab99da05a38 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 12:20:19 +0700 Subject: [PATCH 61/71] refactor: use time() instead --- pytest.ini | 10 +++------- topgg/ratelimiter.py | 8 ++++---- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/pytest.ini b/pytest.ini index 919cbc45..83633087 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,8 +1,4 @@ -[pytest] -xfail_strict = true -norecursedirs = docs *.egg-info .git - -filterwarnings = - ignore::DeprecationWarning - +[pytest] +xfail_strict = true +norecursedirs = docs *.egg-info .git addopts = --cov=topgg \ No newline at end of file diff --git a/topgg/ratelimiter.py b/topgg/ratelimiter.py index 9323081b..f8894a15 100644 --- a/topgg/ratelimiter.py +++ b/topgg/ratelimiter.py @@ -24,7 +24,7 @@ import asyncio import collections -from datetime import datetime +from time import time from types import TracebackType from typing import Any, Awaitable, Callable, List, Optional, Type, Tuple @@ -62,10 +62,10 @@ def __init__( async def __aenter__(self) -> "AsyncRateLimiter": async with self.__lock: if len(self.calls) >= self.max_calls: - until = datetime.utcnow().timestamp() + self.period - self._timespan + until = time() + self.period - self._timespan if self.callback: asyncio.ensure_future(self.callback(until)) - sleep_time = until - datetime.utcnow().timestamp() + sleep_time = until - time() if sleep_time > 0: await asyncio.sleep(sleep_time) return self @@ -78,7 +78,7 @@ async def __aexit__( ) -> None: async with self.__lock: # Store the last operation timestamp. - self.calls.append(datetime.utcnow().timestamp()) + self.calls.append(time()) while self._timespan >= self.period: self.calls.popleft() From bcd414adef82d55149bf2c2ed7461204c422080b Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 12:45:46 +0700 Subject: [PATCH 62/71] refactor: use walrus operator --- topgg/autopost.py | 2 +- topgg/ratelimiter.py | 9 +++++---- topgg/types.py | 38 +++++++++++++++++--------------------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/topgg/autopost.py b/topgg/autopost.py index cfc16fca..49199826 100644 --- a/topgg/autopost.py +++ b/topgg/autopost.py @@ -255,7 +255,7 @@ def _fut_done_callback(self, future: "asyncio.Future") -> None: async def _internal_loop(self) -> None: try: - while 1: + while True: stats = await self.client._invoke_callback(self._stats) try: await self.client.post_guild_count(stats) diff --git a/topgg/ratelimiter.py b/topgg/ratelimiter.py index f8894a15..84ea5dad 100644 --- a/topgg/ratelimiter.py +++ b/topgg/ratelimiter.py @@ -65,8 +65,7 @@ async def __aenter__(self) -> "AsyncRateLimiter": until = time() + self.period - self._timespan if self.callback: asyncio.ensure_future(self.callback(until)) - sleep_time = until - time() - if sleep_time > 0: + if (sleep_time := until - time()) > 0: await asyncio.sleep(sleep_time) return self @@ -97,7 +96,9 @@ def __init__(self, rate_limiters: List[AsyncRateLimiter]): self.rate_limiters = rate_limiters async def __aenter__(self) -> "AsyncRateLimiterManager": - [await manager.__aenter__() for manager in self.rate_limiters] + for manager in self.rate_limiters: + await manager.__aenter__() + return self async def __aexit__( @@ -106,4 +107,4 @@ async def __aexit__( exc_val: BaseException, exc_tb: TracebackType, ) -> None: - await asyncio.gather(*[manager.__aexit__(exc_type, exc_val, exc_tb) for manager in self.rate_limiters]) + await asyncio.gather(*(manager.__aexit__(exc_type, exc_val, exc_tb) for manager in self.rate_limiters)) diff --git a/topgg/types.py b/topgg/types.py index 4d704743..46721f8d 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -41,8 +41,7 @@ def camel_to_snake(string: str) -> str: def parse_vote_dict(d: dict) -> dict: data = d.copy() - query = data.get("query", "").lstrip("?") - if query: + if query := data.get("query", "").lstrip("?"): query_dict = {k: v for k, v in [pair.split("=") for pair in query.split("&")]} data["query"] = DataDict(**query_dict) else: @@ -55,8 +54,7 @@ def parse_vote_dict(d: dict) -> dict: data["guild"] = int(data["guild"]) for key, value in data.copy().items(): - converted_key = camel_to_snake(key) - if key != converted_key: + if key != (converted_key := camel_to_snake(key)): del data[key] data[converted_key] = value @@ -67,20 +65,17 @@ def parse_dict(d: dict) -> dict: data = d.copy() for key, value in data.copy().items(): - if "id" in key.lower(): - if value == "": - value = None - else: - if isinstance(value, str) and value.isdigit(): - value = int(value) - else: - continue - elif value == "": + if value == "": value = None + elif "id" in key.lower(): + if isinstance(value, str) and value.isdigit(): + value = int(value) + else: + continue - converted_key = camel_to_snake(key) - if key != converted_key: + if key != (converted_key := camel_to_snake(key)): del data[key] + data[converted_key] = value return data @@ -89,13 +84,14 @@ def parse_dict(d: dict) -> dict: def parse_bot_dict(d: dict) -> dict: data = parse_dict(d.copy()) - if data.get("date") and not isinstance(data["date"], datetime): - data["date"] = datetime.fromisoformat(data["date"].replace("Z", "+00:00")) + if (date := data.get("date")) and not isinstance(date, datetime): + data["date"] = datetime.fromisoformat(date.replace("Z", "+00:00")) + + if owners := data.get("owners"): + data["owners"] = [int(e) for e in owners] - if data.get("owners"): - data["owners"] = [int(e) for e in data["owners"]] - if data.get("guilds"): - data["guilds"] = [int(e) for e in data["guilds"]] + # TODO: remove this soon + data["guilds"] = [] for key, value in data.copy().items(): converted_key = camel_to_snake(key) From e13d619ace511f6100cc9e59d8de0ff87090f4f7 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 13:05:13 +0700 Subject: [PATCH 63/71] feat: add deprecation warnings for posting shard-related data --- docs/whats-new.rst | 1 - topgg/autopost.py | 8 +++----- topgg/client.py | 24 +++++++++++++----------- topgg/http.py | 37 +++++++++++-------------------------- 4 files changed, 27 insertions(+), 43 deletions(-) diff --git a/docs/whats-new.rst b/docs/whats-new.rst index 6faad770..a548ba9e 100644 --- a/docs/whats-new.rst +++ b/docs/whats-new.rst @@ -10,7 +10,6 @@ v2.0.1 * Dropped Python 3.6 and 3.7 support (:issue:`75`) * Removed the need to manually set a ``default_bot_id`` property * :attr:`.BotData.def_avatar` is now an optional string -* :meth:`.DBLClient.get_bots` is now deprecated * :meth:`.DBLClient.get_guild_count` no longer accepts a ``bot_id`` argument * :meth:`.DBLClient.get_bot_votes` no longer raises a :class:`.ClientException` without a ``default_bot_id`` property * :meth:`.DBLClient.get_bot_info` no longer raises a :class:`.ClientException` without a ``default_bot_id`` property diff --git a/topgg/autopost.py b/topgg/autopost.py index 49199826..2df1d439 100644 --- a/topgg/autopost.py +++ b/topgg/autopost.py @@ -264,12 +264,11 @@ async def _internal_loop(self) -> None: if isinstance(err, errors.Unauthorized): raise err from None else: - on_success = getattr(self, "_success", None) - if on_success: + if on_success := getattr(self, "_success", None): await self.client._invoke_callback(on_success) if self._stopping: - return None + return await asyncio.sleep(self.interval) finally: @@ -305,7 +304,7 @@ def stop(self) -> None: because this will post once before stopping as opposed to cancel immediately. """ if not self.is_running: - return None + return self._stopping = True @@ -322,4 +321,3 @@ def cancel(self) -> None: self._task.cancel() self._refresh_state() - return None diff --git a/topgg/client.py b/topgg/client.py index f6ae2886..57ca47a5 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -154,13 +154,22 @@ async def post_guild_count( If the client has been closed. """ if stats: + warnings.warn( + "Using stats no longer has a use by Top.gg API v0. Soon, all you need is just your bot's server count.", + DeprecationWarning, + ) + guild_count = stats.guild_count - shard_count = stats.shard_count - shard_id = stats.shard_id + + if stats.shard_count or stats.shard_id: + warnings.warn("Posting shard-related data no longer has a use by Top.gg API v0.", DeprecationWarning) elif guild_count is None: - raise TypeError("stats or guild_count must be provided.") + raise TypeError("guild_count must be provided.") + elif shard_count or shard_id: + warnings.warn("Posting shard-related data no longer has a use by Top.gg API v0.", DeprecationWarning) + await self._ensure_session() - await self.http.post_guild_count(guild_count, shard_count, shard_id) + await self.http.post_guild_count(guild_count) async def get_guild_count(self) -> types.BotStatsData: """Gets this bot's guild count and shard info from Top.gg. @@ -225,13 +234,6 @@ async def get_bots( search: t.Optional[t.Dict[str, t.Any]] = None, fields: t.Optional[t.List[str]] = None, ) -> types.DataDict[str, t.Any]: - """ - Warning: - This function is deprecated. - """ - - warnings.warn("get_bots is now deprecated.", DeprecationWarning) - sort = sort or "" search = search or {} fields = fields or [] diff --git a/topgg/http.py b/topgg/http.py index 87d7bf2c..1b05c280 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -30,7 +30,7 @@ import logging import sys import warnings -from datetime import datetime +from time import time from typing import Any, Coroutine, Dict, Iterable, List, Optional, Sequence, Union, cast, Tuple import aiohttp @@ -175,20 +175,9 @@ async def close(self) -> None: if self._own_session: await self.session.close() - async def post_guild_count( - self, - guild_count: Optional[Union[int, List[int]]], - shard_count: Optional[int], - shard_id: Optional[int], - ) -> None: + async def post_guild_count(self, guild_count: Optional[Union[int, List[int]]]) -> None: """Posts bot's guild count and shards info on Top.gg.""" - payload = {"server_count": guild_count} - if shard_count: - payload["shard_count"] = shard_count - if shard_id: - payload["shard_id"] = shard_id - - await self.request("POST", "/bots/stats", json=payload) + await self.request("POST", "/bots/stats", json={"server_count": guild_count}) def get_weekend_status(self) -> Coroutine[Any, Any, dict]: """Gets the weekend status from Top.gg.""" @@ -214,16 +203,9 @@ def get_bots( search: Dict[str, str], fields: Sequence[str], ) -> Coroutine[Any, Any, dict]: - """ - Warning: - This function is deprecated. - """ - - warnings.warn("get_bots is now deprecated.", DeprecationWarning) - limit = min(limit, 500) fields = ", ".join(fields) - search = " ".join([f"{field}: {value}" for field, value in search.items()]) + search = " ".join(f"{field}: {value}" for field, value in search.items()) return self.request( "GET", @@ -248,13 +230,16 @@ def get_user_vote(self, bot_id: int, user_id: int) -> Coroutine[Any, Any, dict]: async def _rate_limit_handler(until: float) -> None: """Handles the displayed message when we are ratelimited.""" - duration = round(until - datetime.utcnow().timestamp()) + duration = round(until - time()) mins = duration / 60 fmt = "We have exhausted a ratelimit quota. Retrying in %.2f seconds (%.3f minutes)." _LOGGER.warning(fmt, duration, mins) def to_json(obj: Any) -> str: - if json.__name__ == "ujson": - return json.dumps(obj, ensure_ascii=True) - return json.dumps(obj, separators=(",", ":"), ensure_ascii=True) + kwargs = {"ensure_ascii": True} + + if json.__name__ != "ujson": + kwargs["separators"] = (",", ":") + + return json.dumps(obj, **kwargs) From 48e9c970d014e79f88afd69273c8e00df9e19e1d Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 13:20:16 +0700 Subject: [PATCH 64/71] feat: deprecated shard-related things --- examples/hikari_example/callbacks/autopost.py | 2 +- tests/test_type.py | 7 +--- topgg/autopost.py | 2 +- topgg/client.py | 14 +++---- topgg/data.py | 2 +- topgg/types.py | 38 +++++++++++++------ 6 files changed, 35 insertions(+), 30 deletions(-) diff --git a/examples/hikari_example/callbacks/autopost.py b/examples/hikari_example/callbacks/autopost.py index d50080e2..e09a0165 100644 --- a/examples/hikari_example/callbacks/autopost.py +++ b/examples/hikari_example/callbacks/autopost.py @@ -58,4 +58,4 @@ def on_autopost_error( def stats(app: hikari.GatewayBot = topgg.data(hikari.GatewayBot)): - return topgg.StatsWrapper(guild_count=len(app.cache.get_guilds_view()), shard_count=app.shard_count) + return topgg.StatsWrapper(guild_count=len(app.cache.get_guilds_view())) diff --git a/tests/test_type.py b/tests/test_type.py index 8cea66c5..c66c81bf 100644 --- a/tests/test_type.py +++ b/tests/test_type.py @@ -173,12 +173,7 @@ def test_server_vote_data_fields(server_vote_data: types.BotVoteData) -> None: def test_bot_stats_data_attrs(bot_stats_data: types.BotStatsData) -> None: - for count in ("server_count", "shard_count"): - assert isinstance(bot_stats_data[count], int) or bot_stats_data[count] is None - assert isinstance(bot_stats_data.shards, list) - if bot_stats_data.shards: - for shard in bot_stats_data.shards: - assert isinstance(shard, int) + assert isinstance(bot_stats_data["server_count"], int) or bot_stats_data["server_count"] is None def test_user_data_attrs(user_data: types.UserData) -> None: diff --git a/topgg/autopost.py b/topgg/autopost.py index 2df1d439..28a2abf8 100644 --- a/topgg/autopost.py +++ b/topgg/autopost.py @@ -191,7 +191,7 @@ def stats(self, callback: t.Any = None) -> t.Any: @autopost.stats() def get_stats(client: Client = topgg.data(Client)): - return topgg.StatsWrapper(guild_count=len(client.guilds), shard_count=len(client.shards)) + return topgg.StatsWrapper(guild_count=len(client.guilds)) # somewhere after the event loop has started diff --git a/topgg/client.py b/topgg/client.py index 57ca47a5..b3e5eb4d 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -127,7 +127,7 @@ async def post_guild_count( shard_count: t.Any = None, shard_id: t.Any = None, ) -> None: - """Posts your bot's guild count and shards info to Top.gg. + """Posts your bot's guild count to Top.gg. .. _0 based indexing : https://en.wikipedia.org/wiki/Zero-based_numbering @@ -136,16 +136,12 @@ async def post_guild_count( Args: stats (:obj:`~.types.StatsWrapper`) - An instance of StatsWrapper containing guild_count, shard_count, and shard_id. + An instance of StatsWrapper containing guild_count. Keyword Arguments: guild_count (Optional[Union[:obj:`int`, List[:obj:`int`]]]) - Number of guilds the bot is in. Applies the number to a shard instead if shards are specified. + Number of guilds the bot is in. If not specified, length of provided client's property `.guilds` will be posted. - shard_count (Optional[:obj:`int`]) - The total number of shards. - shard_id (Optional[:obj:`int`]) - The index of the current shard. Top.gg uses `0 based indexing`_ for shards. Raises: TypeError @@ -172,11 +168,11 @@ async def post_guild_count( await self.http.post_guild_count(guild_count) async def get_guild_count(self) -> types.BotStatsData: - """Gets this bot's guild count and shard info from Top.gg. + """Gets this bot's guild count from Top.gg. Returns: :obj:`~.types.BotStatsData`: - The guild count and shards of a bot on Top.gg. + The guild count on Top.gg. Raises: :exc:`~.errors.ClientStateException` diff --git a/topgg/data.py b/topgg/data.py index 0d05668b..b21d0a4c 100644 --- a/topgg/data.py +++ b/topgg/data.py @@ -60,7 +60,7 @@ def data(type_: t.Type[T]) -> T: @autopost.stats() def get_stats(client: Client = topgg.data(Client)): - return topgg.StatsWrapper(guild_count=len(client.guilds), shard_count=len(client.shards)) + return topgg.StatsWrapper(guild_count=len(client.guilds)) """ return t.cast(T, Data(type_)) diff --git a/topgg/types.py b/topgg/types.py index 46721f8d..670b9376 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -26,6 +26,7 @@ import dataclasses import typing as t +import warnings from datetime import datetime KT = t.TypeVar("KT") @@ -115,10 +116,6 @@ def parse_bot_stats_dict(d: dict) -> dict: if "server_count" not in data: data["server_count"] = None - if "shards" not in data: - data["shards"] = [] - if "shard_count" not in data: - data["shard_count"] = None return data @@ -270,20 +267,34 @@ def __init__(self, **kwargs: t.Any): class BotStatsData(DataDict[str, t.Any]): - """Model that contains information about a listed bot's guild and shard count.""" + """Model that contains information about a listed bot's guild count.""" __slots__: t.Tuple[str, ...] = () server_count: t.Optional[int] """The amount of servers the bot is in.""" - shards: t.List[int] - """The amount of servers the bot is in per shard.""" - shard_count: t.Optional[int] - """The amount of shards a bot has.""" def __init__(self, **kwargs: t.Any): super().__init__(**parse_bot_stats_dict(kwargs)) + @property + def shards(self) -> t.List[int]: + """DEPRECATED: No longer supported by Top.gg API v0. At the moment, this will always return an empty array.""" + + warnings.warn( + "No longer supported by Top.gg API v0. At the moment, this will always return an empty array.", + DeprecationWarning, + ) + return [] + + @property + def shard_count(self) -> t.Optional[int]: + """DEPRECATED: No longer supported by Top.gg API v0. At the moment, this will always return None.""" + + warnings.warn( + "No longer supported by Top.gg API v0. At the moment, this will always return None.", DeprecationWarning + ) + class BriefUserData(DataDict[str, t.Any]): """Model that contains brief information about a Top.gg user.""" @@ -405,7 +416,10 @@ class StatsWrapper: """The guild count.""" shard_count: t.Optional[int] = None - """The shard count.""" - shard_id: t.Optional[int] = None - """The shard ID the guild count belongs to.""" + + def __init__(self, guild_count: int, **kwargs): + if kwargs.get("shard_count") or kwargs.get("shard_id"): + warnings.warn("Posting shard-related data no longer has a use by Top.gg API v0.", DeprecationWarning) + + self.guild_count = guild_count From 542f2201e8b48eebc54b99591a24961bb28888cc Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 13:22:05 +0700 Subject: [PATCH 65/71] ci: change pytest exception match --- tests/test_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index fa11c434..b74acea6 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -41,7 +41,7 @@ async def test_HTTPClient_with_no_external_session(session: ClientSession): @pytest.mark.asyncio async def test_DBLClient_post_guild_count_with_no_args(): client = topgg.DBLClient(MOCK_TOKEN) - with pytest.raises(TypeError, match="stats or guild_count must be provided."): + with pytest.raises(TypeError, match="guild_count must be provided."): await client.post_guild_count() From e88d2a86cf6a7eb231e42489e6126f85575fee95 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 14:22:34 +0700 Subject: [PATCH 66/71] feat: deprecate even more legacy API properties --- topgg/types.py | 95 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/topgg/types.py b/topgg/types.py index 670b9376..7ce146f0 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -92,7 +92,10 @@ def parse_bot_dict(d: dict) -> dict: data["owners"] = [int(e) for e in owners] # TODO: remove this soon - data["guilds"] = [] + data.pop("defAvatar", None) + data.pop("discriminator", None) + data.pop("guilds", None) + data.pop("certifiedBot", None) for key, value in data.copy().items(): converted_key = camel_to_snake(key) @@ -106,6 +109,10 @@ def parse_bot_dict(d: dict) -> dict: def parse_user_dict(d: dict) -> dict: data = d.copy() + # TODO: remove this soon + data.pop("discriminator", None) + data.pop("certifiedDev", None) + data["social"] = SocialData(**data.get("social", {})) return data @@ -205,15 +212,9 @@ class BotData(DataDict[str, t.Any]): username: str """The username of the bot.""" - discriminator: str - """The discriminator of the bot.""" - avatar: t.Optional[str] """The avatar hash of the bot.""" - def_avatar: t.Optional[str] - """The avatar hash of the bot's default avatar.""" - prefix: str """The prefix of the bot.""" @@ -238,18 +239,12 @@ class BotData(DataDict[str, t.Any]): owners: t.List[int] """The IDs of the owners of the bot.""" - guilds: t.List[int] - """The guilds the bot is in.""" - invite: t.Optional[str] """The invite URL of the bot.""" date: datetime """The time the bot was added.""" - certified_bot: bool - """Whether or not the bot is certified.""" - vanity: t.Optional[str] """The vanity URL of the bot.""" @@ -265,6 +260,45 @@ class BotData(DataDict[str, t.Any]): def __init__(self, **kwargs: t.Any): super().__init__(**parse_bot_dict(kwargs)) + @property + def def_avatar(self) -> t.Optional[str]: + """DEPRECATED: def_avatar is no longer supported by Top.gg API v0. At the moment, this will always be None.""" + + warnings.warn( + "def_avatar is no longer supported by Top.gg API v0. At the moment, this will always be None.", + DeprecationWarning, + ) + + @property + def discriminator(self) -> str: + """DEPRECATED: Discriminators are no longer supported by Top.gg API v0. At the moment, this will always be '0'.""" + + warnings.warn( + "Discriminators are no longer supported by Top.gg API v0. At the moment, this will always be '0'.", + DeprecationWarning, + ) + return "0" + + @property + def guilds(self) -> t.List[int]: + """DEPRECATED: Guilds list is no longer supported by Top.gg API v0. At the moment, this will always be an empty list.""" + + warnings.warn( + "Guilds list is no longer supported by Top.gg API v0. At the moment, this will always be an empty list.", + DeprecationWarning, + ) + return [] + + @property + def certified_bot(self) -> bool: + """DEPRECATED: Certified bot is no longer supported by Top.gg API v0. At the moment, this will always be false.""" + + warnings.warn( + "Certified bot is no longer supported by Top.gg API v0. At the moment, this will always be false.", + DeprecationWarning, + ) + return False + class BotStatsData(DataDict[str, t.Any]): """Model that contains information about a listed bot's guild count.""" @@ -279,20 +313,21 @@ def __init__(self, **kwargs: t.Any): @property def shards(self) -> t.List[int]: - """DEPRECATED: No longer supported by Top.gg API v0. At the moment, this will always return an empty array.""" + """DEPRECATED: Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return an empty array.""" warnings.warn( - "No longer supported by Top.gg API v0. At the moment, this will always return an empty array.", + "Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return an empty array.", DeprecationWarning, ) return [] @property def shard_count(self) -> t.Optional[int]: - """DEPRECATED: No longer supported by Top.gg API v0. At the moment, this will always return None.""" + """DEPRECATED: Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return None.""" warnings.warn( - "No longer supported by Top.gg API v0. At the moment, this will always return None.", DeprecationWarning + "Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return None.", + DeprecationWarning, ) @@ -343,9 +378,6 @@ class UserData(DataDict[str, t.Any]): username: str """The username of the user.""" - discriminator: str - """The discriminator of the user.""" - social: SocialData """The social data of the user.""" @@ -355,9 +387,6 @@ class UserData(DataDict[str, t.Any]): supporter: bool """Whether or not the user is a supporter.""" - certified_dev: bool - """Whether or not the user is a certified dev.""" - mod: bool """Whether or not the user is a Top.gg mod.""" @@ -370,6 +399,26 @@ class UserData(DataDict[str, t.Any]): def __init__(self, **kwargs: t.Any): super().__init__(**parse_user_dict(kwargs)) + @property + def certified_dev(self) -> bool: + """DEPRECATED: Certified dev is no longer supported by Top.gg API v0. At the moment, this will always be False.""" + + warnings.warn( + "Certified dev is no longer supported by Top.gg API v0. At the moment, this will always be False.", + DeprecationWarning, + ) + return False + + @property + def discriminator(self) -> str: + """DEPRECATED: Discriminators are no longer supported by Top.gg API v0. At the moment, this will always be '0'.""" + + warnings.warn( + "Discriminators are no longer supported by Top.gg API v0. At the moment, this will always be '0'.", + DeprecationWarning, + ) + return "0" + class VoteDataDict(DataDict[str, t.Any]): """Base model that represents received information from Top.gg via webhooks.""" From 15d962a411a304014086f06cd18f5274ed049e39 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 14:29:00 +0700 Subject: [PATCH 67/71] fix: fix tests not working because of guilds list --- tests/test_type.py | 3 +-- topgg/types.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/test_type.py b/tests/test_type.py index c66c81bf..f53abf6c 100644 --- a/tests/test_type.py +++ b/tests/test_type.py @@ -3,7 +3,6 @@ from topgg import types d: dict = { - "defAvatar": "6debd47ed13483642cf09e832ed0bc1b", "invite": "", "website": "https://top.gg", "support": "KYZsaFb", @@ -127,7 +126,7 @@ def test_bot_data_fields(bot_data: types.BotData) -> None: for attr in bot_data: if "id" in attr.lower(): assert isinstance(bot_data[attr], int) or bot_data[attr] is None - elif attr in ("owners", "guilds"): + elif attr == "owners": for item in bot_data[attr]: assert isinstance(item, int) assert bot_data.get(attr) == bot_data[attr] == getattr(bot_data, attr) diff --git a/topgg/types.py b/topgg/types.py index 7ce146f0..bea82509 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -291,10 +291,10 @@ def guilds(self) -> t.List[int]: @property def certified_bot(self) -> bool: - """DEPRECATED: Certified bot is no longer supported by Top.gg API v0. At the moment, this will always be false.""" + """DEPRECATED: Certified bot is no longer supported by Top.gg API v0. At the moment, this will always be False.""" warnings.warn( - "Certified bot is no longer supported by Top.gg API v0. At the moment, this will always be false.", + "Certified bot is no longer supported by Top.gg API v0. At the moment, this will always be False.", DeprecationWarning, ) return False @@ -313,10 +313,10 @@ def __init__(self, **kwargs: t.Any): @property def shards(self) -> t.List[int]: - """DEPRECATED: Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return an empty array.""" + """DEPRECATED: Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return an empty list.""" warnings.warn( - "Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return an empty array.", + "Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return an empty list.", DeprecationWarning, ) return [] From 48e60552e9c0beddac85eb3b5678ea936ee43825 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 14:40:26 +0700 Subject: [PATCH 68/71] feat: notify lib of deprecation as well --- topgg/types.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/topgg/types.py b/topgg/types.py index bea82509..ed162183 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -279,6 +279,15 @@ def discriminator(self) -> str: ) return "0" + @property + def lib(self) -> t.Optional[str]: + """DEPRECATED: lib is no longer supported by Top.gg API v0. At the moment, this will always be None.""" + + warnings.warn( + "lib is no longer supported by Top.gg API v0. At the moment, this will always be None.", + DeprecationWarning, + ) + @property def guilds(self) -> t.List[int]: """DEPRECATED: Guilds list is no longer supported by Top.gg API v0. At the moment, this will always be an empty list.""" From 4a292d89f5063a5df19dfb004ef34f00f1bc4933 Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 15:07:48 +0700 Subject: [PATCH 69/71] doc: add deprecations in whats-new --- docs/whats-new.rst | 13 +++++++++++-- topgg/types.py | 20 +------------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/docs/whats-new.rst b/docs/whats-new.rst index a548ba9e..de17639d 100644 --- a/docs/whats-new.rst +++ b/docs/whats-new.rst @@ -7,9 +7,18 @@ This page keeps a detailed human friendly rendering of what's new and changed in v2.0.1 ====== * Added Python 3.12 support (:issue:`78`) -* Dropped Python 3.6 and 3.7 support (:issue:`75`) +* Dropped Python 3.6, 3.7, and 3.8 support (:issue:`75`) * Removed the need to manually set a ``default_bot_id`` property -* :attr:`.BotData.def_avatar` is now an optional string +* Posting shard-related data is now deprecated +* :attr:`.BotData.certified_bot` is now deprecated +* :attr:`.BotData.def_avatar` is now deprecated +* :attr:`.BotData.discriminator` is now deprecated +* :attr:`.BotData.lib` is now deprecated +* :attr:`.BotData.guilds` is now deprecated +* :attr:`.BotData.shards` is now deprecated +* :attr:`.BotData.shard_count` is now deprecated +* :attr:`.UserData.certified_dev` is now deprecated +* :attr:`.UserData.discriminator` is now deprecated * :meth:`.DBLClient.get_guild_count` no longer accepts a ``bot_id`` argument * :meth:`.DBLClient.get_bot_votes` no longer raises a :class:`.ClientException` without a ``default_bot_id`` property * :meth:`.DBLClient.get_bot_info` no longer raises a :class:`.ClientException` without a ``default_bot_id`` property diff --git a/topgg/types.py b/topgg/types.py index ed162183..42553d11 100644 --- a/topgg/types.py +++ b/topgg/types.py @@ -36,7 +36,7 @@ def camel_to_snake(string: str) -> str: - return "".join(["_" + c.lower() if c.isupper() else c for c in string]).lstrip("_") + return "".join("_" + c.lower() if c.isupper() else c for c in string).lstrip("_") def parse_vote_dict(d: dict) -> dict: @@ -262,8 +262,6 @@ def __init__(self, **kwargs: t.Any): @property def def_avatar(self) -> t.Optional[str]: - """DEPRECATED: def_avatar is no longer supported by Top.gg API v0. At the moment, this will always be None.""" - warnings.warn( "def_avatar is no longer supported by Top.gg API v0. At the moment, this will always be None.", DeprecationWarning, @@ -271,8 +269,6 @@ def def_avatar(self) -> t.Optional[str]: @property def discriminator(self) -> str: - """DEPRECATED: Discriminators are no longer supported by Top.gg API v0. At the moment, this will always be '0'.""" - warnings.warn( "Discriminators are no longer supported by Top.gg API v0. At the moment, this will always be '0'.", DeprecationWarning, @@ -281,8 +277,6 @@ def discriminator(self) -> str: @property def lib(self) -> t.Optional[str]: - """DEPRECATED: lib is no longer supported by Top.gg API v0. At the moment, this will always be None.""" - warnings.warn( "lib is no longer supported by Top.gg API v0. At the moment, this will always be None.", DeprecationWarning, @@ -290,8 +284,6 @@ def lib(self) -> t.Optional[str]: @property def guilds(self) -> t.List[int]: - """DEPRECATED: Guilds list is no longer supported by Top.gg API v0. At the moment, this will always be an empty list.""" - warnings.warn( "Guilds list is no longer supported by Top.gg API v0. At the moment, this will always be an empty list.", DeprecationWarning, @@ -300,8 +292,6 @@ def guilds(self) -> t.List[int]: @property def certified_bot(self) -> bool: - """DEPRECATED: Certified bot is no longer supported by Top.gg API v0. At the moment, this will always be False.""" - warnings.warn( "Certified bot is no longer supported by Top.gg API v0. At the moment, this will always be False.", DeprecationWarning, @@ -322,8 +312,6 @@ def __init__(self, **kwargs: t.Any): @property def shards(self) -> t.List[int]: - """DEPRECATED: Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return an empty list.""" - warnings.warn( "Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return an empty list.", DeprecationWarning, @@ -332,8 +320,6 @@ def shards(self) -> t.List[int]: @property def shard_count(self) -> t.Optional[int]: - """DEPRECATED: Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return None.""" - warnings.warn( "Shard-related data is no longer supported by Top.gg API v0. At the moment, this will always return None.", DeprecationWarning, @@ -410,8 +396,6 @@ def __init__(self, **kwargs: t.Any): @property def certified_dev(self) -> bool: - """DEPRECATED: Certified dev is no longer supported by Top.gg API v0. At the moment, this will always be False.""" - warnings.warn( "Certified dev is no longer supported by Top.gg API v0. At the moment, this will always be False.", DeprecationWarning, @@ -420,8 +404,6 @@ def certified_dev(self) -> bool: @property def discriminator(self) -> str: - """DEPRECATED: Discriminators are no longer supported by Top.gg API v0. At the moment, this will always be '0'.""" - warnings.warn( "Discriminators are no longer supported by Top.gg API v0. At the moment, this will always be '0'.", DeprecationWarning, From 053a4389d7892805b2d3a345748433476afb4a9f Mon Sep 17 00:00:00 2001 From: null8626 Date: Tue, 18 Feb 2025 15:10:13 +0700 Subject: [PATCH 70/71] doc: it's BotStatsData [skip ci] --- docs/whats-new.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/whats-new.rst b/docs/whats-new.rst index de17639d..93082ee5 100644 --- a/docs/whats-new.rst +++ b/docs/whats-new.rst @@ -15,8 +15,8 @@ v2.0.1 * :attr:`.BotData.discriminator` is now deprecated * :attr:`.BotData.lib` is now deprecated * :attr:`.BotData.guilds` is now deprecated -* :attr:`.BotData.shards` is now deprecated -* :attr:`.BotData.shard_count` is now deprecated +* :attr:`.BotStatsData.shards` is now deprecated +* :attr:`.BotStatsData.shard_count` is now deprecated * :attr:`.UserData.certified_dev` is now deprecated * :attr:`.UserData.discriminator` is now deprecated * :meth:`.DBLClient.get_guild_count` no longer accepts a ``bot_id`` argument From a0780770c981624298fba28b34eb9656d60ee7c1 Mon Sep 17 00:00:00 2001 From: null8626 Date: Wed, 19 Feb 2025 01:21:00 +0700 Subject: [PATCH 71/71] doc: update docs [skip ci] --- topgg/client.py | 4 +--- topgg/http.py | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/topgg/client.py b/topgg/client.py index b3e5eb4d..6213c221 100644 --- a/topgg/client.py +++ b/topgg/client.py @@ -129,8 +129,6 @@ async def post_guild_count( ) -> None: """Posts your bot's guild count to Top.gg. - .. _0 based indexing : https://en.wikipedia.org/wiki/Zero-based_numbering - Warning: You can't provide both args and kwargs at once. @@ -139,7 +137,7 @@ async def post_guild_count( An instance of StatsWrapper containing guild_count. Keyword Arguments: - guild_count (Optional[Union[:obj:`int`, List[:obj:`int`]]]) + guild_count (Optional[:obj:`int`]) Number of guilds the bot is in. If not specified, length of provided client's property `.guilds` will be posted. diff --git a/topgg/http.py b/topgg/http.py index 1b05c280..c3411e36 100644 --- a/topgg/http.py +++ b/topgg/http.py @@ -175,8 +175,8 @@ async def close(self) -> None: if self._own_session: await self.session.close() - async def post_guild_count(self, guild_count: Optional[Union[int, List[int]]]) -> None: - """Posts bot's guild count and shards info on Top.gg.""" + async def post_guild_count(self, guild_count: Optional[int]) -> None: + """Posts bot's guild count on Top.gg.""" await self.request("POST", "/bots/stats", json={"server_count": guild_count}) def get_weekend_status(self) -> Coroutine[Any, Any, dict]: