diff options
-rw-r--r-- | .editorconfig | 15 | ||||
-rw-r--r-- | .gitignore | 126 | ||||
-rw-r--r-- | .token | 1 | ||||
-rw-r--r-- | README.md | 0 | ||||
-rw-r--r-- | discord_ban_list/__init__.py | 9 | ||||
-rw-r--r-- | discord_ban_list/api.py | 39 | ||||
-rw-r--r-- | discord_ban_list/result.py | 109 | ||||
-rw-r--r-- | discord_ban_list/version.py | 31 | ||||
-rw-r--r-- | pylintrc.cfg | 2 | ||||
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | setup.cfg | 5 | ||||
-rw-r--r-- | setup.py | 39 | ||||
-rw-r--r-- | tests/test_login.py | 32 |
13 files changed, 409 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f8fb98d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +max_line_length = 79 + +[*.py] +indent_style = space +indent_size = 4 +spaces_around_operators = hybrid +spaces_around_brackets = none +quote_type = single diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5e1e51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,126 @@ + +# Created by https://www.gitignore.io/api/python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +### Python Patch ### +.venv/ + +### Python.VirtualEnv Stack ### +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +pip-selfcheck.json + + +# End of https://www.gitignore.io/api/python @@ -0,0 +1 @@ +4_zlAypGMldMW3EUcIqFFz_zTTbepiVDRTff1Gy4eI0
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/README.md diff --git a/discord_ban_list/__init__.py b/discord_ban_list/__init__.py new file mode 100644 index 0000000..56976b2 --- /dev/null +++ b/discord_ban_list/__init__.py @@ -0,0 +1,9 @@ +from .api import DiscordBanList +from .result import BanABC, NoBan, Ban +from .version import VersionInfo, version + +__all__ = ( + 'VersionInfo', 'version', + 'DiscordBanList', + 'BanABC', 'Ban', 'NoBan', +) diff --git a/discord_ban_list/api.py b/discord_ban_list/api.py new file mode 100644 index 0000000..a9aec10 --- /dev/null +++ b/discord_ban_list/api.py @@ -0,0 +1,39 @@ +from asyncio import Lock +from typing import Union + +from aiohttp import ClientSession + +from .result import Ban, NoBan, BanABC + +CHECK_URL = 'https://bans.discord.id/api/check.php' + + +class DiscordBanList(object): + __slots__ = ('token', '_client', '_client_lock') + + def __init__(self, token: str, _client: ClientSession = None): + self.token = token + self._client = _client + self._client_lock = Lock() + + async def login(self): + await self._guarantee_client() + + async def check(self, user_id: Union[int, str]) -> BanABC: + resp = await self._get('{base}?user_id={user_id}'.format(base=CHECK_URL, user_id=user_id)) + if resp['banned'] == "0": + return NoBan(user_id) + return Ban(user_id, resp['reason'], resp['case_id'], resp['proof']) + + async def _get(self, url): + async with self._client.get(url, + headers={'Authorization': self.token}) as resp: + resp.raise_for_status() + return await resp.json(content_type=None) + + async def _guarantee_client(self): + if self._client is None: + async with self._client_lock: + if self._client is None: + self._client = ClientSession() + return self._client diff --git a/discord_ban_list/result.py b/discord_ban_list/result.py new file mode 100644 index 0000000..bde3e2b --- /dev/null +++ b/discord_ban_list/result.py @@ -0,0 +1,109 @@ +from abc import ABC, abstractproperty, abstractmethod +from typing import Union, Optional + + +class BanABC(ABC): + __slots__ = () + + @abstractproperty + def user_id(self) -> int: + pass + + @abstractproperty + def banned(self) -> bool: + pass + + @abstractproperty + def reason(self) -> Optional[str]: + pass + + @abstractproperty + def case_id(self) -> Optional[int]: + pass + + @abstractproperty + def proof(self) -> Optional[str]: + pass + + @abstractmethod + def __eq__(self, other): + pass + + +class Ban(BanABC): + __slots__ = ('_proof', '_user_id', '_case_id', '_reason') + + def __init__(self, user_id: Union[int, str], reason: str, case_id: Union[str, int], proof: str): + self._user_id = int(user_id) + self._reason = reason + self._case_id = int(case_id) + self._proof = proof + + @property + def user_id(self) -> Optional[int]: + return self._user_id + + @property + def banned(self) -> bool: + return True + + @property + def reason(self) -> Optional[str]: + return self._reason + + @property + def case_id(self) -> Optional[int]: + return self._case_id + + @property + def proof(self) -> Optional[str]: + return self._proof + + def __str__(self): + return '<DBan banned: True, User: {user}, Case: {case}, reason: {reason!r}, proof: {proof!r}>'.format( + user=self.user_id, + case=self.case_id, + reason=self.reason, + proof=self.proof, + ) + + def __eq__(self, other): + if not isinstance(other, Ban): + return False + return other.user_id == self.user_id \ + and other.case_id == self.case_id + + +class NoBan(BanABC): + __slots__ = ('_user_id',) + + def __init__(self, user_id: Union[str, int]): + self._user_id = int(user_id) + + @property + def user_id(self) -> int: + return self._user_id + + @property + def banned(self) -> bool: + return False + + @property + def reason(self) -> Optional[str]: + return None + + @property + def case_id(self) -> Optional[int]: + return None + + @property + def proof(self) -> Optional[str]: + return None + + def __str__(self): + return '<DBan banned: False, User: {user}>'.format(user=self.user_id) + + def __eq__(self, other): + if not isinstance(other, NoBan): + return False + return other.user_id == self.user_id diff --git a/discord_ban_list/version.py b/discord_ban_list/version.py new file mode 100644 index 0000000..3fc3d54 --- /dev/null +++ b/discord_ban_list/version.py @@ -0,0 +1,31 @@ +"""versioninfo for discord_ban_list""" + + +# pylint: disable=too-few-public-methods +class VersionInfo: + """Version info dataclass""" + + __slots__ = ('major', 'minor', 'build', 'level', 'serial') + + # pylint: disable=too-many-arguments + def __init__(self, major: int, minor: int, build: int, level: str, serial: int): + self.major = major + self.minor = minor + self.build = build + self.level = level + self.serial = serial + + def __str__(self): + return '{major}.{minor}.{build}{level}{serial}'.format( + major=self.major, + minor=self.minor, + build=self.build, + level=self.level, + serial=self.serial, + ) + + def __repr__(self): + return str(self) + + +version = VersionInfo(1, 0, 0, 'a', 0) diff --git a/pylintrc.cfg b/pylintrc.cfg new file mode 100644 index 0000000..facac26 --- /dev/null +++ b/pylintrc.cfg @@ -0,0 +1,2 @@ +[MASTER] +ignore=tests, setup.py diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ee4ba4f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +aiohttp diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..bdd3e25 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[aliases] +test = pytest + +[tool:pytest] +addopts = --pylint --pylint-rcfile=pylintrc.cfg diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..2bece50 --- /dev/null +++ b/setup.py @@ -0,0 +1,39 @@ +from setuptools import setup + +with open('discord_ban_list/version.py') as fp: + _loc, _glob = {}, {} + exec(fp.read(), _loc, _glob) + version = {**_loc, **_glob}['version'] + +with open('requirements.txt') as fp: + requirements = fp.read().splitlines() + +with open('README.md') as fp: + readme = fp.read() + +if not version: + raise RuntimeError('Version is not set in discord_ban_list/version.py') + +setup( + name='discord_ban_list', + author='romangraef', + url='https://github.com/romangraef/discord_ban_list', + version=str(version), + install_requires=requirements, + long_description=readme, + setup_requires=['pytest-runner', 'pytest-pylint'], + tests_require=['pytest', 'pylint'], + license='MIT', + packages=['discord_ban_list'], + description='Asyncio Python Wrapper for the discord.id ban list.', + classifiers=[ + 'Topic :: Discord', + 'Operating System :: OS Independent', + 'Topic :: Internet', + 'Topic :: Software Development :: Libraries', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Utilities', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + ] +) diff --git a/tests/test_login.py b/tests/test_login.py new file mode 100644 index 0000000..fb8f086 --- /dev/null +++ b/tests/test_login.py @@ -0,0 +1,32 @@ +import os +from asyncio import get_event_loop +from unittest import TestCase + +from discord_ban_list import DiscordBanList + + +async def a_test_valid_login(): + ban_list = DiscordBanList(os.environ['token']) + await ban_list.login() + ban = await ban_list.check(123) + assert not ban.banned + + +async def a_test_banned(): + user = 123456789123456789 + ban_list = DiscordBanList(os.environ['token']) + await ban_list.login() + ban = await ban_list.check(user) + assert ban.banned + assert ban.user_id == user + assert ban.case_id == 9999 + assert ban.reason == "THIS IS A VIRTUAL DBANS API TESTING ACCOUNT" + assert ban.proof == "https://yourmom-is.gae" + + +class TestSomething(TestCase): + def test_not_banned(self): + get_event_loop().run_until_complete(a_test_valid_login()) + + def test_banned(self): + get_event_loop().run_until_complete(a_test_banned()) |