mirror of
https://github.com/deesiigneer/pyspapi.git
synced 2026-04-20 12:35:26 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
931b1a8621 | ||
|
|
e9765e8b6a | ||
|
|
047dbb38d0 | ||
|
|
6da906e0d1 | ||
|
|
4fc530caeb | ||
|
|
6e77bac3ba |
42
.github/workflows/python-publish.yml
vendored
42
.github/workflows/python-publish.yml
vendored
@@ -17,22 +17,28 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [ 3.9 ]
|
python-version: [3.12]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
|
||||||
uses: actions/setup-python@v2
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
with:
|
uses: actions/setup-python@v2
|
||||||
python-version: ${{ matrix.python-version }}
|
with:
|
||||||
- name: Install dependencies
|
python-version: ${{ matrix.python-version }}
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
- name: Install Poetry
|
||||||
pip install twine
|
run: |
|
||||||
- name: Compile package
|
curl -sSL https://install.python-poetry.org | python3 -
|
||||||
run: |
|
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||||
python3 setup.py sdist
|
|
||||||
- name: Publish package
|
- name: Install dependencies
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
run: poetry install --no-interaction
|
||||||
with:
|
|
||||||
user: __token__
|
- name: Build package
|
||||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
run: poetry build
|
||||||
|
|
||||||
|
- name: Publish package
|
||||||
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
|
with:
|
||||||
|
user: __token__
|
||||||
|
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
version: 2
|
version: 2
|
||||||
formats: []
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
os: ubuntu-lts-latest
|
os: ubuntu-lts-latest
|
||||||
tools:
|
tools:
|
||||||
python: '3.8'
|
python: '3.12'
|
||||||
|
|
||||||
|
|
||||||
sphinx:
|
sphinx:
|
||||||
configuration: docs/conf.py
|
configuration: docs/conf.py
|
||||||
fail_on_warning: false
|
|
||||||
builder: html
|
builder: html
|
||||||
|
|
||||||
python:
|
python:
|
||||||
@@ -18,4 +16,3 @@ python:
|
|||||||
path: .
|
path: .
|
||||||
extra_requirements:
|
extra_requirements:
|
||||||
- docs
|
- docs
|
||||||
- requirements: docs/requirements.txt
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
include README.rst
|
|
||||||
include LICENSE
|
|
||||||
include requirements.txt
|
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
# from the environment for the first two.
|
# from the environment for the first two.
|
||||||
SPHINXOPTS ?=
|
SPHINXOPTS ?=
|
||||||
SPHINXBUILD ?= sphinx-build
|
SPHINXBUILD ?= sphinx-build
|
||||||
SOURCEDIR = source
|
SOURCEDIR = .
|
||||||
BUILDDIR = build
|
BUILDDIR = _build
|
||||||
|
|
||||||
# Put it first so that "make" without argument is like "make help".
|
# Put it first so that "make" without argument is like "make help".
|
||||||
help:
|
help:
|
||||||
|
|||||||
16
docs/api.rst
16
docs/api.rst
@@ -1,25 +1,25 @@
|
|||||||
.. currentmodule:: pyspapi
|
.. currentmodule:: pyspapi
|
||||||
|
|
||||||
API Reference
|
Справочник API
|
||||||
===============
|
===============
|
||||||
|
|
||||||
The following section outlines the API of pyspapi.
|
В следующем разделе описывается API pyspapi.
|
||||||
|
|
||||||
Version Info
|
Информация о версии
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
There are two main ways to query version information.
|
Существует два основных способа запроса информации о версии.
|
||||||
|
|
||||||
.. data:: version_info
|
.. data:: version_info
|
||||||
|
|
||||||
A named tuple that is similar to :obj:`py:sys.version_info`.
|
Именованный кортеж, аналогичный :obj:`py:sys.version_info`.
|
||||||
|
|
||||||
Just like :obj:`py:sys.version_info` the valid values for ``releaselevel`` are
|
Как и в :obj:`py:sys.version_info`, допустимые значения для ``releaselevel`` это
|
||||||
'alpha', 'beta', 'candidate' and 'final'.
|
'alpha', 'beta', 'candidate' и 'final'.
|
||||||
|
|
||||||
.. data:: __version__
|
.. data:: __version__
|
||||||
|
|
||||||
A string representation of the version.
|
Строковое представление версии.
|
||||||
|
|
||||||
``pyspapi``
|
``pyspapi``
|
||||||
-----------
|
-----------
|
||||||
|
|||||||
68
docs/conf.py
68
docs/conf.py
@@ -1,59 +1,44 @@
|
|||||||
from re import search, MULTILINE
|
from importlib.metadata import version as pkg_version
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
|
project = "pyspapi"
|
||||||
|
author = "deesiigneer"
|
||||||
|
copyright = "2022, deesiigneer"
|
||||||
|
|
||||||
project = 'pyspapi'
|
version = pkg_version("pyspapi")
|
||||||
copyright = '2022, deesiigneer'
|
|
||||||
author = 'deesiigneer'
|
|
||||||
with open("../pyspapi/__init__.py") as f:
|
|
||||||
match = search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), MULTILINE)
|
|
||||||
|
|
||||||
if not match or match.group(1) is None:
|
|
||||||
raise RuntimeError("The version could not be resolved")
|
|
||||||
|
|
||||||
version = match.group(1)
|
|
||||||
|
|
||||||
# The full version, including alpha/beta/rc tags.
|
|
||||||
release = version
|
release = version
|
||||||
|
|
||||||
# -- General configuration
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath(".."))
|
|
||||||
|
|
||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
'sphinx.ext.duration',
|
"sphinx.ext.duration",
|
||||||
'sphinx.ext.doctest',
|
"sphinx.ext.doctest",
|
||||||
'sphinx.ext.autodoc',
|
"sphinx.ext.autodoc",
|
||||||
'sphinx.ext.autosummary',
|
"sphinx.ext.autosummary",
|
||||||
'sphinx.ext.intersphinx',
|
"sphinx.ext.intersphinx",
|
||||||
]
|
]
|
||||||
|
|
||||||
autodoc_member_order = "bysource"
|
autosummary_generate = True
|
||||||
autodoc_typehinta = "none"
|
|
||||||
|
|
||||||
intersphinx_mapping = {
|
|
||||||
'python': ('https://docs.python.org/3/', None),
|
|
||||||
'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
|
|
||||||
}
|
|
||||||
|
|
||||||
version_match = os.environ.get("READTHEDOCS_VERSION")
|
version_match = os.environ.get("READTHEDOCS_VERSION")
|
||||||
json_url = f"https://pyspapi.readthedocs.io/ru/{version_match}/_static/switcher.json"
|
json_url = f"https://pyspapi.readthedocs.io/ru/{version_match}/_static/switcher.json"
|
||||||
|
|
||||||
intersphinx_disabled_domains = ['std']
|
|
||||||
language = 'en'
|
language = "ru"
|
||||||
locale_dirs = ["locale/"]
|
|
||||||
exclude_patterns = []
|
exclude_patterns = []
|
||||||
html_static_path = ["_static"]
|
html_static_path = ["_static"]
|
||||||
|
|
||||||
html_theme = "pydata_sphinx_theme"
|
html_theme = "pydata_sphinx_theme"
|
||||||
html_logo = "./images/logo.png"
|
html_logo = "./images/logo.png"
|
||||||
html_favicon = "./images/logo.ico"
|
html_favicon = "./images/logo.ico"
|
||||||
|
|
||||||
html_theme_options = {
|
html_theme_options = {
|
||||||
"external_links": [
|
"external_links": [
|
||||||
{
|
{
|
||||||
"url": "https://github.com/deesiigneer/pyspapi/releases",
|
"url": "https://github.com/deesiigneer/pyspapi/releases",
|
||||||
"name": "Changelog",
|
"name": "Changelog",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/sp-worlds/api-docs/wiki",
|
||||||
|
"name": "SPWorlds API Docs",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon_links": [
|
"icon_links": [
|
||||||
@@ -61,24 +46,27 @@ html_theme_options = {
|
|||||||
"name": "GitHub",
|
"name": "GitHub",
|
||||||
"url": "https://github.com/deesiigneer/pyspapi",
|
"url": "https://github.com/deesiigneer/pyspapi",
|
||||||
"icon": "fab fa-brands fa-github",
|
"icon": "fab fa-brands fa-github",
|
||||||
"type": "fontawesome"
|
"type": "fontawesome",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Discord",
|
"name": "Discord",
|
||||||
"url": "https://discord.gg/VbyHaKRAaN",
|
"url": "https://discord.gg/VbyHaKRAaN",
|
||||||
"icon": "fab fa-brands fa-discord",
|
"icon": "fab fa-brands fa-discord",
|
||||||
"type": "fontawesome"
|
"type": "fontawesome",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "PyPi",
|
"name": "PyPI",
|
||||||
"url": "https://pypi.org/project/pyspapi/",
|
"url": "https://pypi.org/project/pyspapi/",
|
||||||
"icon": "fab fa-brands fa-python",
|
"icon": "fab fa-brands fa-python",
|
||||||
"type": "fontawesome"
|
"type": "fontawesome",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"header_links_before_dropdown": 4,
|
"header_links_before_dropdown": 4,
|
||||||
"show_toc_level": 1,
|
"show_toc_level": 1,
|
||||||
"navbar_start": ["navbar-logo"],
|
"navbar_start": ["navbar-logo"],
|
||||||
"navigation_with_keys": True,
|
"navigation_with_keys": True,
|
||||||
|
"switcher": {
|
||||||
|
"json_url": json_url,
|
||||||
|
"version_match": version_match,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
html_css_files = ["custom.css"]
|
|
||||||
|
|||||||
@@ -1,32 +1,31 @@
|
|||||||
:theme_html_remove_secondary_sidebar:
|
:theme_html_remove_secondary_sidebar:
|
||||||
|
|
||||||
Welcome to pyspapi
|
Добро пожаловать в pyspapi
|
||||||
===================
|
===========================
|
||||||
|
|
||||||
API wrapper for SP servers written in Python.
|
Обертка API для SPWorlds серверов, написанная на Python.
|
||||||
|
|
||||||
Getting started
|
Начало работы
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Is this your first time using the library? This is the place to get started!
|
Вы впервые используете библиотеку? Это место, с которого нужно начать!
|
||||||
|
|
||||||
- **First steps:** :ref:`Quickstart <quickstart>`
|
- **Первые шаги:** :ref:`Быстрый старт <quickstart>`
|
||||||
- **Examples:** Many examples are available in the `examples directory <https://github.com/deesiigneer/pyspapi/tree/main/examples/>`_.
|
- **Примеры:** Много примеров доступно в `папке примеров <https://github.com/deesiigneer/pyspapi/tree/main/examples/>`_.
|
||||||
|
|
||||||
Getting help
|
Получение помощи
|
||||||
------------
|
------------------
|
||||||
|
|
||||||
If you're having trouble with something, these resources might help.
|
Если у вас есть проблемы с чем-то, эти ресурсы могут помочь.
|
||||||
|
|
||||||
- Ask questions in `Discord <https://discord.gg/VbyHaKRAaN>`_ server.
|
- Задавайте вопросы на сервере `Discord <https://discord.gg/VbyHaKRAaN>`_.
|
||||||
- If you're looking for something specific, try the :ref:`searching <search>`.
|
- Если вы ищете что-то конкретное, попробуйте :ref:`поиск <search>`.
|
||||||
- Report bugs in the `issue tracker <https://github.com/deesiigneer/pyspapi/issues>`_.
|
- Сообщайте об ошибках в `трекер проблем <https://github.com/deesiigneer/pyspapi/issues>`_.
|
||||||
- Ask in `GitHub discussions page <https://github.com/deesiigneer/pyspapi/discussions>`_.
|
|
||||||
|
|
||||||
Manuals
|
Руководства
|
||||||
-------
|
-----------
|
||||||
|
|
||||||
These pages go into great detail about everything the API can do.
|
Эти страницы подробно описывают все, что может сделать API.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
|
|
||||||
.. currentmodule:: pyspapi
|
.. currentmodule:: pyspapi
|
||||||
|
|
||||||
Quickstart
|
Быстрый старт
|
||||||
==========
|
==============
|
||||||
|
|
||||||
This page gives a brief introduction to the library.
|
На этой странице дается краткое введение в библиотеку.
|
||||||
|
|
||||||
Checking balance
|
Проверка баланса
|
||||||
----------------
|
-----------------
|
||||||
|
|
||||||
Let's output the amount of money remaining in the card account to the console.
|
Выведем количество денег, оставшихся на счету карты, на консоль.
|
||||||
|
|
||||||
It looks something like this:
|
Это выглядит примерно так:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@@ -30,7 +30,6 @@ It looks something like this:
|
|||||||
loop = get_event_loop()
|
loop = get_event_loop()
|
||||||
loop.run_until_complete(main())
|
loop.run_until_complete(main())
|
||||||
|
|
||||||
Make sure not to name it ``pyspapi`` as that'll conflict with the library.
|
Убедитесь, что вы не называете его ``pyspapi``, так как это вызовет конфликт с библиотекой.
|
||||||
|
|
||||||
|
Вы можете найти больше примеров в `папке примеров <https://github.com/deesiigneer/pyspapi/tree/main/examples/>`_ на GitHub.
|
||||||
You can find more examples in the `examples directory <https://github.com/deesiigneer/pyspapi/tree/main/examples/>`_ on GitHub.
|
|
||||||
1826
poetry.lock
generated
Normal file
1826
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
pyproject.toml
Normal file
29
pyproject.toml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
[project]
|
||||||
|
name = "pyspapi"
|
||||||
|
version = "3.3.0"
|
||||||
|
description = "API wrapper for SP servers written in Python."
|
||||||
|
readme = "README.rst"
|
||||||
|
license = { text = "MIT" }
|
||||||
|
authors = [{ name = "deesiigneer", email = "xdeesiigneerx@gmail.com" }]
|
||||||
|
requires-python = ">=3.12,<3.15"
|
||||||
|
dependencies = ["aiohttp>=3.9,<4.0"]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Homepage = "https://pyspapi.deesiigneer.ru"
|
||||||
|
Documentation = "https://pyspapi.readthedocs.org"
|
||||||
|
Repository = "https://github.com/deesiigneer/pyspapi"
|
||||||
|
"Issue Tracker" = "https://github.com/deesiigneer/pyspapi/issues"
|
||||||
|
Discord = "https://discord.com/invite/VbyHaKRAaN"
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
docs = [
|
||||||
|
"sphinx>=7,<9",
|
||||||
|
"sphinx-autobuild>=2025.8.25,<2026.0.0",
|
||||||
|
"pydata-sphinx-theme>=0.16.1,<0.17.0",
|
||||||
|
]
|
||||||
|
dev = ["ruff>=0.14,<0.15", "toml-sort>=0.24,<0.25"]
|
||||||
|
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
from .api import *
|
"""pyspapi - API wrapper for SP servers written in Python
|
||||||
from .spworlds import *
|
TODO: заполнить описание"""
|
||||||
from .types import *
|
|
||||||
|
|
||||||
__author__ = 'deesiigneer'
|
import importlib.metadata
|
||||||
__url__ = 'https://github.com/deesiigneer/pyspapi'
|
from .spworlds import SPAPI
|
||||||
__description__ = 'API wrapper for SP servers written in Python.'
|
|
||||||
__license__ = 'MIT'
|
__all__ = [SPAPI]
|
||||||
__version__ = "3.2.0"
|
|
||||||
|
__author__: str = "deesiigneer"
|
||||||
|
__url__: str = "https://github.com/deesiigneer/pyspapi"
|
||||||
|
__description__: str = "API wrapper for SP servers written in Python."
|
||||||
|
__license__: str = "MIT"
|
||||||
|
__version__: str = importlib.metadata.version("pyspapi")
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from .api import APISession
|
from .api import APISession
|
||||||
|
|
||||||
|
__all__ = [APISession]
|
||||||
|
|||||||
@@ -8,17 +8,20 @@ import aiohttp
|
|||||||
|
|
||||||
from ..exceptions import ValidationError, SPAPIError
|
from ..exceptions import ValidationError, SPAPIError
|
||||||
|
|
||||||
log = getLogger('pyspapi')
|
log = getLogger("pyspapi")
|
||||||
|
|
||||||
|
|
||||||
class APISession(object):
|
class APISession(object):
|
||||||
|
def __init__(
|
||||||
def __init__(self, card_id: str,
|
self,
|
||||||
token: str,
|
card_id: str,
|
||||||
timeout: int = 5,
|
token: str,
|
||||||
sleep_time: float = 0.2,
|
timeout: int = 5,
|
||||||
retries: int = 0,
|
sleep_time: float = 0.2,
|
||||||
raise_exception: bool = False):
|
retries: int = 0,
|
||||||
|
raise_exception: bool = False,
|
||||||
|
proxy: str = None,
|
||||||
|
):
|
||||||
self.__url = "https://spworlds.ru/api/public/"
|
self.__url = "https://spworlds.ru/api/public/"
|
||||||
self.__id = card_id
|
self.__id = card_id
|
||||||
self.__token = token
|
self.__token = token
|
||||||
@@ -26,23 +29,29 @@ class APISession(object):
|
|||||||
self.__retries = retries
|
self.__retries = retries
|
||||||
self.__timeout = timeout
|
self.__timeout = timeout
|
||||||
self.__raise_exception = raise_exception
|
self.__raise_exception = raise_exception
|
||||||
|
self.__proxy = proxy
|
||||||
self.session: Optional[aiohttp.ClientSession] = None
|
self.session: Optional[aiohttp.ClientSession] = None
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self):
|
||||||
|
print("proxy=", self.__proxy)
|
||||||
self.session = aiohttp.ClientSession(
|
self.session = aiohttp.ClientSession(
|
||||||
json_serialize=json.dumps,
|
json_serialize=json.dumps,
|
||||||
timeout=aiohttp.ClientTimeout(total=self.__timeout))
|
timeout=aiohttp.ClientTimeout(total=self.__timeout),
|
||||||
|
proxy=self.__proxy,
|
||||||
|
)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
async def __aexit__(self, *err):
|
async def __aexit__(self, *err):
|
||||||
await self.session.close()
|
await self.session.close()
|
||||||
self.session = None
|
self.session = None
|
||||||
|
|
||||||
async def request(self, method: str, endpoint: str, data: Optional[Dict] = None) -> Any:
|
async def request(
|
||||||
|
self, method: str, endpoint: str, data: Optional[Dict] = None
|
||||||
|
) -> Any:
|
||||||
url = self.__url + endpoint
|
url = self.__url + endpoint
|
||||||
headers = {
|
headers = {
|
||||||
'Authorization': f"Bearer {str(b64encode(str(f'{self.__id}:{self.__token}').encode('utf-8')), 'utf-8')}",
|
"Authorization": f"Bearer {str(b64encode(str(f'{self.__id}:{self.__token}').encode('utf-8')), 'utf-8')}",
|
||||||
'User-Agent': 'https://github.com/deesiigneer/pyspapi',
|
"User-Agent": "https://github.com/deesiigneer/pyspapi",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,9 +59,13 @@ class APISession(object):
|
|||||||
while True:
|
while True:
|
||||||
attempt += 1
|
attempt += 1
|
||||||
if attempt > 1:
|
if attempt > 1:
|
||||||
log.warning(f'[pyspapi] Repeat attempt {attempt}: {method.upper()} {url}')
|
log.warning(
|
||||||
|
f"[pyspapi] Repeat attempt {attempt}: {method.upper()} {url}"
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
async with self.session.request(method, url, json=data, headers=headers) as resp:
|
async with self.session.request(
|
||||||
|
method, url, json=data, headers=headers
|
||||||
|
) as resp:
|
||||||
if resp.status == 422:
|
if resp.status == 422:
|
||||||
errors = await resp.json()
|
errors = await resp.json()
|
||||||
log.error(f"[pyspapi] Validation error: {errors}")
|
log.error(f"[pyspapi] Validation error: {errors}")
|
||||||
@@ -69,7 +82,7 @@ class APISession(object):
|
|||||||
|
|
||||||
return await resp.json()
|
return await resp.json()
|
||||||
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
|
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
|
||||||
log.exception(f"[pyspapi] Connection error: {e}")
|
log.exception(f"[pyspapi] Connection error: {e} \n attempt {attempt}")
|
||||||
if attempt > self.__retries:
|
if attempt > self.__retries:
|
||||||
return None
|
return None
|
||||||
await asyncio.sleep(self.__sleep_timeout)
|
await asyncio.sleep(self.__sleep_timeout)
|
||||||
|
|||||||
@@ -4,27 +4,31 @@ from hmac import new, compare_digest
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from .api import APISession
|
from .api import APISession
|
||||||
from .types import User
|
from pyspapi.types import User
|
||||||
from .types.me import Account
|
from pyspapi.types.me import Account
|
||||||
from .types.payment import Item
|
from pyspapi.types.payment import Item
|
||||||
|
|
||||||
__all__ = ['SPAPI']
|
__all__ = ["SPAPI"]
|
||||||
|
|
||||||
|
|
||||||
class SPAPI(APISession):
|
class SPAPI(APISession):
|
||||||
"""
|
"""
|
||||||
pyspapi — высокоуровневый клиент для взаимодействия с SPWorldsAPI.
|
pyspapi — высокоуровневый клиент для взаимодействия с SPWorlds API.
|
||||||
|
|
||||||
Предоставляет удобные методы для работы с балансом карты, вебхуками,
|
Предоставляет удобные методы для работы с балансом карты, вебхуками,
|
||||||
информацией о пользователе, транзакциями и платежами, а также верификацией вебхуков.
|
информацией о пользователе, транзакциями и платежами, а также верификацией вебхуков.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, card_id: str,
|
def __init__(
|
||||||
token: str,
|
self,
|
||||||
timeout: int = 5,
|
card_id: str,
|
||||||
sleep_time: float = 0.2,
|
token: str,
|
||||||
retries: int = 0,
|
timeout: int = 5,
|
||||||
raise_exception: bool = False):
|
sleep_time: float = 0.2,
|
||||||
|
retries: int = 0,
|
||||||
|
raise_exception: bool = False,
|
||||||
|
proxy: str = None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Инициализирует объект SPAPI.
|
Инициализирует объект SPAPI.
|
||||||
|
|
||||||
@@ -40,8 +44,12 @@ class SPAPI(APISession):
|
|||||||
:type retries: int
|
:type retries: int
|
||||||
:param raise_exception: Поднимать исключения при ошибке, если True.
|
:param raise_exception: Поднимать исключения при ошибке, если True.
|
||||||
:type raise_exception: bool
|
:type raise_exception: bool
|
||||||
|
:param proxy: Прокся!
|
||||||
|
:type proxy: str
|
||||||
"""
|
"""
|
||||||
super().__init__(card_id, token, timeout, sleep_time, retries, raise_exception)
|
super().__init__(
|
||||||
|
card_id, token, timeout, sleep_time, retries, raise_exception, proxy
|
||||||
|
)
|
||||||
self.__card_id = card_id
|
self.__card_id = card_id
|
||||||
self.__token = token
|
self.__token = token
|
||||||
|
|
||||||
@@ -59,7 +67,7 @@ class SPAPI(APISession):
|
|||||||
:return: Текущий баланс карты.
|
:return: Текущий баланс карты.
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
return int((await super().get('card'))['balance'])
|
return int((await super().get("card"))["balance"])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
async def webhook(self) -> Optional[str]:
|
async def webhook(self) -> Optional[str]:
|
||||||
@@ -69,7 +77,7 @@ class SPAPI(APISession):
|
|||||||
:return: URL вебхука.
|
:return: URL вебхука.
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
return str((await super().get('card'))['webhook'])
|
return str((await super().get("card"))["webhook"])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
async def me(self) -> Optional[Account]:
|
async def me(self) -> Optional[Account]:
|
||||||
@@ -79,16 +87,17 @@ class SPAPI(APISession):
|
|||||||
:return: Объект Account, представляющий аккаунт текущего пользователя.
|
:return: Объект Account, представляющий аккаунт текущего пользователя.
|
||||||
:rtype: :class:`Account`
|
:rtype: :class:`Account`
|
||||||
"""
|
"""
|
||||||
me = await super().get('accounts/me')
|
me = await super().get("accounts/me")
|
||||||
return Account(
|
return Account(
|
||||||
account_id=me['id'],
|
account_id=me["id"],
|
||||||
username=me['username'],
|
username=me["username"],
|
||||||
minecraftuuid=me['minecraftUUID'],
|
minecraftuuid=me["minecraftUUID"],
|
||||||
status=me['status'],
|
status=me["status"],
|
||||||
roles=me['roles'],
|
roles=me["roles"],
|
||||||
cities=me['cities'],
|
cities=me["cities"],
|
||||||
cards=me['cards'],
|
cards=me["cards"],
|
||||||
created_at=me['createdAt'])
|
created_at=me["createdAt"],
|
||||||
|
)
|
||||||
|
|
||||||
async def get_user(self, discord_id: int) -> Optional[User]:
|
async def get_user(self, discord_id: int) -> Optional[User]:
|
||||||
"""
|
"""
|
||||||
@@ -100,11 +109,16 @@ class SPAPI(APISession):
|
|||||||
:return: Объект User, представляющий пользователя.
|
:return: Объект User, представляющий пользователя.
|
||||||
:rtype: :class:`User`
|
:rtype: :class:`User`
|
||||||
"""
|
"""
|
||||||
user = await super().get(f'users/{discord_id}')
|
user = await super().get(f"users/{discord_id}")
|
||||||
cards = await super().get(f"accounts/{user['username']}/cards")
|
if user:
|
||||||
return User(user['username'], user['uuid'], cards)
|
cards = await super().get(f"accounts/{user['username']}/cards")
|
||||||
|
return User(user["username"], user["uuid"], cards)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
async def create_transaction(self, receiver: str, amount: int, comment: str) -> Optional[int]:
|
async def create_transaction(
|
||||||
|
self, receiver: str, amount: int, comment: str
|
||||||
|
) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Создает транзакцию.
|
Создает транзакцию.
|
||||||
|
|
||||||
@@ -118,15 +132,13 @@ class SPAPI(APISession):
|
|||||||
:return: Баланс после транзакции.
|
:return: Баланс после транзакции.
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {"receiver": receiver, "amount": amount, "comment": comment}
|
||||||
'receiver': receiver,
|
|
||||||
'amount': amount,
|
|
||||||
'comment': comment
|
|
||||||
}
|
|
||||||
|
|
||||||
return int((await super().post('transactions', data))['balance'])
|
return int((await super().post("transactions", data))["balance"])
|
||||||
|
|
||||||
async def create_payment(self, webhook_url: str, redirect_url: str, data: str, items: list[Item]) -> Optional[str]:
|
async def create_payment(
|
||||||
|
self, webhook_url: str, redirect_url: str, data: str, items: list[Item]
|
||||||
|
) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Создает платеж.
|
Создает платеж.
|
||||||
|
|
||||||
@@ -142,13 +154,13 @@ class SPAPI(APISession):
|
|||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {
|
||||||
'items': items,
|
"items": items,
|
||||||
'redirectUrl': redirect_url,
|
"redirectUrl": redirect_url,
|
||||||
'webhookUrl': webhook_url,
|
"webhookUrl": webhook_url,
|
||||||
'data': data
|
"data": data,
|
||||||
}
|
}
|
||||||
|
|
||||||
return str((await super().post('payments', data))['url'])
|
return str((await super().post("payments", data))["url"])
|
||||||
|
|
||||||
async def update_webhook(self, url: str) -> Optional[dict]:
|
async def update_webhook(self, url: str) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
@@ -157,9 +169,9 @@ class SPAPI(APISession):
|
|||||||
:param url: Новый URL вебхука.
|
:param url: Новый URL вебхука.
|
||||||
:return: Ответ API в виде словаря или None при ошибке.
|
:return: Ответ API в виде словаря или None при ошибке.
|
||||||
"""
|
"""
|
||||||
data = {'url': url}
|
data = {"url": url}
|
||||||
|
|
||||||
return await super().put('card/webhook', data)
|
return await super().put("card/webhook", data)
|
||||||
|
|
||||||
def webhook_verify(self, data: str, header: str) -> bool:
|
def webhook_verify(self, data: str, header: str) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -172,8 +184,8 @@ class SPAPI(APISession):
|
|||||||
:return: True, если заголовок из вебхука достоверен, иначе False.
|
:return: True, если заголовок из вебхука достоверен, иначе False.
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
hmac_data = b64encode(new(self.__token.encode('utf-8'), data, sha256).digest())
|
hmac_data = b64encode(new(self.__token.encode("utf-8"), data, sha256).digest())
|
||||||
return compare_digest(hmac_data, header.encode('utf-8'))
|
return compare_digest(hmac_data, header.encode("utf-8"))
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
from .payment import Item
|
|
||||||
from .users import User
|
|
||||||
from .me import Account
|
from .me import Account
|
||||||
|
from .payment import Item
|
||||||
|
from .users import Cards, User
|
||||||
|
|
||||||
|
__all__ = [Account, Item, Cards, User]
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
aiohttp>=3.8.0
|
|
||||||
43
setup.py
43
setup.py
@@ -1,43 +0,0 @@
|
|||||||
import re
|
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
|
||||||
|
|
||||||
requirements = []
|
|
||||||
with open("requirements.txt") as f:
|
|
||||||
requirements = f.read().splitlines()
|
|
||||||
|
|
||||||
version = ""
|
|
||||||
with open("pyspapi/__init__.py") as f:
|
|
||||||
match = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE)
|
|
||||||
if match is None or match.group(1) is None:
|
|
||||||
raise RuntimeError('Version is not set')
|
|
||||||
|
|
||||||
version = match.group(1)
|
|
||||||
|
|
||||||
if not version:
|
|
||||||
raise RuntimeError("Version is not set")
|
|
||||||
|
|
||||||
readme = ""
|
|
||||||
with open("README.rst") as f:
|
|
||||||
readme = f.read()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='pyspapi',
|
|
||||||
license='MIT',
|
|
||||||
author='deesiigneer',
|
|
||||||
version=version,
|
|
||||||
url='https://github.com/deesiigneer/pyspapi',
|
|
||||||
project_urls={
|
|
||||||
"Documentation": "https://pyspapi.readthedocs.io/ru/latest/",
|
|
||||||
"GitHub": "https://github.com/deesiigneer/pyspapi",
|
|
||||||
"Discord": "https://discord.com/invite/VbyHaKRAaN"
|
|
||||||
},
|
|
||||||
description='API wrapper for SP servers written in Python',
|
|
||||||
long_description=readme,
|
|
||||||
long_description_content_type='text/x-rst',
|
|
||||||
packages=find_packages(),
|
|
||||||
package_data={'pyspapi': ['types/*', 'api/*']}, # Включаем дополнительные файлы и папки
|
|
||||||
include_package_data=True,
|
|
||||||
install_requires=requirements,
|
|
||||||
python_requires='>=3.8.0',
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user