mirror of
https://github.com/deesiigneer/pyspapi.git
synced 2026-04-20 12:35:26 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
931b1a8621 | ||
|
|
e9765e8b6a | ||
|
|
047dbb38d0 | ||
|
|
6da906e0d1 | ||
|
|
4fc530caeb | ||
|
|
6e77bac3ba | ||
|
|
d36ecfca36 | ||
|
|
c086954c25 | ||
|
|
8d60472b9a | ||
|
|
14166cf519 | ||
|
|
ab60b4f104 | ||
|
|
b738db3252 | ||
|
|
b3d56a6059 | ||
| 18c1ff1daf | |||
|
|
eff14052fd | ||
|
|
3f935a060b | ||
|
|
3ecf1fff8a | ||
|
|
74a46277f8 | ||
|
|
63ee509067 | ||
|
|
4ecafef192 | ||
|
|
d44199c4cc | ||
|
|
056ebce615 | ||
|
|
b00b2b76cd | ||
|
|
da86771e3c | ||
|
|
479a02b95f | ||
|
|
5c905ea097 | ||
|
|
83d9663da3 | ||
|
|
93760d2d87 | ||
|
|
108d30ebac | ||
|
|
b4ddd1fd93 | ||
|
|
5449659566 | ||
|
|
85ef993011 | ||
|
|
8e6664bc51 | ||
|
|
0d46f0a126 | ||
|
|
f5484d3900 | ||
|
|
1c35daf142 | ||
|
|
91ed017ac6 | ||
|
|
1806ba7929 | ||
|
|
f4623e9204 | ||
|
|
906060a4b7 | ||
|
|
6bf342a9f1 | ||
|
|
a6cbf2adcc | ||
|
|
59d43736c0 | ||
|
|
9429c443c4 | ||
|
|
152b5272c1 | ||
|
|
81d8b1de35 | ||
|
|
0667922c8c | ||
|
|
9a61797253 | ||
|
|
c9c1bc8e81 | ||
|
|
f551a860bc | ||
|
|
1065b5748b | ||
|
|
1d6af2b8ec | ||
|
|
f244e3f4db | ||
|
|
76dc15a332 | ||
|
|
a2a4ac9fab |
49
.github/workflows/python-publish.yml
vendored
49
.github/workflows/python-publish.yml
vendored
@@ -12,28 +12,33 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
regular:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.12]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install build
|
||||
- name: Build package
|
||||
run: python -m build
|
||||
- name: Publish package
|
||||
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install Poetry
|
||||
run: |
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install dependencies
|
||||
run: poetry install --no-interaction
|
||||
|
||||
- name: Build package
|
||||
run: poetry build
|
||||
|
||||
- name: Publish package
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
|
||||
18
.readthedocs.yml
Normal file
18
.readthedocs.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-lts-latest
|
||||
tools:
|
||||
python: '3.12'
|
||||
|
||||
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
builder: html
|
||||
|
||||
python:
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
extra_requirements:
|
||||
- docs
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Aleksey
|
||||
Copyright (c) 2022 deesiigneer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
87
README.md
87
README.md
@@ -1,87 +0,0 @@
|
||||
# pyspapi
|
||||
Фреймворк [API](https://github.com/sp-worlds/api-docs) для серверов СП
|
||||
|
||||
## Установка
|
||||
**Требуется *Python 3.7* или выше**
|
||||
|
||||
*Windows*
|
||||
```commandline
|
||||
pip install pyspapi
|
||||
```
|
||||
*Linux*
|
||||
```commandline
|
||||
sudo apt pip3 install pyspapi
|
||||
```
|
||||
|
||||
## Примеры
|
||||
### [Оплата](https://github.com/sp-worlds/api-docs/blob/main/PAYMENTS.md)
|
||||
|
||||
```Python
|
||||
import spapi
|
||||
|
||||
api = spapi.Api(card_id='CARD_ID',
|
||||
token='TOKEN')
|
||||
|
||||
print(api.payment(amount=1,
|
||||
redirecturl='https://www.google.com/',
|
||||
webhookurl='https://www.yourwebhook.com/',
|
||||
data='Какие-то данные'
|
||||
)
|
||||
)
|
||||
|
||||
```
|
||||
- `amount` - Стоимость покупки в АРах
|
||||
- `redirectUrl` - URL страницы, на которую попадет пользователь после оплаты
|
||||
- `webhookUrl` - URL, куда наш сервер направит запрос, чтобы оповестить ваш сервер об успешной оплате
|
||||
- `data` - Строка до 100 символов, сюда можно поместить любые полезные данных.
|
||||
#### Получение данных об успешной оплате
|
||||
После успешной оплаты на URL указанный в `webhookUrl` придет POST запрос.
|
||||
|
||||
*Тело запроса будет в формате JSON:*
|
||||
|
||||
- `payer` - Ник игрока, который совершил оплату
|
||||
- `amount` - Стоимость покупки
|
||||
- `data` - Данные, которые вы отдали при создании запроса на оплату
|
||||
|
||||
### [Переводы](https://github.com/sp-worlds/api-docs/blob/main/TRANSACTIONS.md)
|
||||
|
||||
```Python
|
||||
import spapi
|
||||
|
||||
api = spapi.Api(card_id='CARD_ID',
|
||||
token='TOKEN')
|
||||
|
||||
print(api.transaction(receiver='12345',
|
||||
amount=1,
|
||||
comment="test"
|
||||
)
|
||||
)
|
||||
|
||||
```
|
||||
- `receiver` - Номер карты получателя
|
||||
- `amount` - Количество АР для перевода
|
||||
- `comment` - Комментарий к переводу
|
||||
-
|
||||
### [Проверка наличия проходки](https://github.com/sp-worlds/api-docs/blob/main/USERS.md)
|
||||
|
||||
```Python
|
||||
import spapi
|
||||
|
||||
api = spapi.Api(card_id='CARD_ID',
|
||||
token='TOKEN')
|
||||
|
||||
print(api.check_user(discord_user_id=123456789012345678)
|
||||
)
|
||||
|
||||
```
|
||||
- `discord_user_id` - ID пользователя в Discord.
|
||||
|
||||
*В ответ вы получите JSON:*
|
||||
|
||||
- `username` - Ник пользователя или null, если у пользователя нет входа на сервер.
|
||||
|
||||
|
||||
## Ссылки
|
||||
|
||||
- [Discord сервер разработчика](https://discord.gg/sJYtYnhN)
|
||||
- [Документация API сайтов СП](https://github.com/sp-worlds/api-docs)
|
||||
69
README.rst
Normal file
69
README.rst
Normal file
@@ -0,0 +1,69 @@
|
||||
.. image:: https://raw.githubusercontent.com/deesiigneer/pyspapi/main/assets/repo-banner.png
|
||||
:alt: pyspapi
|
||||
|
||||
.. image:: https://img.shields.io/discord/850091193190973472?color=5865F2&label=discord
|
||||
:target: https://discord.gg/VbyHaKRAaN
|
||||
:alt: Discord server invite
|
||||
.. image:: https://img.shields.io/github/v/release/deesiigneer/pyspapi?include_prereleases&label=github%20release
|
||||
:target: https://github.com/deesiigneer/pyspapi/
|
||||
:alt: GitHub release (latest by date including pre-releases)
|
||||
.. image:: https://img.shields.io/pypi/v/pyspapi.svg
|
||||
:target: https://pypi.org/project/pyspapi/
|
||||
:alt: PyPI downloads info
|
||||
.. image:: https://img.shields.io/pypi/dm/pyspapi?color=informational&label=pypi%20downloads
|
||||
:target: https://pypi.org/project/pyspapi/
|
||||
:alt: PyPI version info
|
||||
.. image:: https://img.shields.io/readthedocs/pyspapi
|
||||
:target: https://pyspapi.readthedocs.io/
|
||||
:alt: pyspapi documentation
|
||||
|
||||
pyspapi
|
||||
========
|
||||
|
||||
`API <https://github.com/sp-worlds/api-docs>`_ wrapper for SP servers written in Python.
|
||||
|
||||
Installation
|
||||
-------------
|
||||
**Requires Python 3.8 or higher**
|
||||
|
||||
*Windows*
|
||||
|
||||
|
||||
.. code:: sh
|
||||
|
||||
pip install pyspapi
|
||||
|
||||
*Linux/macOS*
|
||||
|
||||
.. code:: sh
|
||||
|
||||
pip3 install pyspapi
|
||||
|
||||
Quick example
|
||||
--------------
|
||||
|
||||
Checking the balance
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
.. code:: py
|
||||
|
||||
from pyspapi import SPAPI
|
||||
from asyncio import get_event_loop
|
||||
|
||||
spapi = SPAPI(card_id='CARD_ID', token='TOKEN')
|
||||
|
||||
|
||||
async def main():
|
||||
print(await spapi.balance)
|
||||
|
||||
loop = get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
|
||||
More examples can be found in the `examples <https://github.com/deesiigneer/pyspapi/tree/main/examples>`_
|
||||
|
||||
Links
|
||||
------
|
||||
|
||||
- `Discord server <https://discord.gg/VbyHaKRAaN>`_
|
||||
- `pyspapi documentation <https://pyspapi.readthedocs.io/>`_
|
||||
- `PyPi <https://pypi.org/project/pyspapi/>`_
|
||||
- `API documentation for SP sites <https://github.com/sp-worlds/api-docs>`_
|
||||
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
assets/pyspapi_repo_banner.png
Normal file
BIN
assets/pyspapi_repo_banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
assets/repo-banner.png
Normal file
BIN
assets/repo-banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
BIN
docs/_static/404.png
vendored
Normal file
BIN
docs/_static/404.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
19
docs/_static/custom.css
vendored
Normal file
19
docs/_static/custom.css
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/* Background of stable should be green */
|
||||
.version-switcher__container a[data-version-name*="stable"] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.version-switcher__container a[data-version-name*="stable"] span {
|
||||
color: var(--pst-color-success);
|
||||
}
|
||||
|
||||
.version-switcher__container a[data-version-name*="stable"] span:before {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: var(--pst-color-success);
|
||||
opacity: 0.1;
|
||||
}
|
||||
17
docs/_static/switcher.json
vendored
Normal file
17
docs/_static/switcher.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"name": "latest",
|
||||
"version": "latest",
|
||||
"url": "https://pyspapi.readthedocs.io/ru/latest/"
|
||||
},
|
||||
{
|
||||
"name": "stable",
|
||||
"version": "stable",
|
||||
"url": "https://pyspapi.readthedocs.io/ru/stable/"
|
||||
},
|
||||
{
|
||||
"name": "v3-asyncio",
|
||||
"version": "v3-asyncio",
|
||||
"url": "https://pyspapi.readthedocs.io/ru/v3-asyncio/"
|
||||
}
|
||||
]
|
||||
30
docs/api.rst
Normal file
30
docs/api.rst
Normal file
@@ -0,0 +1,30 @@
|
||||
.. currentmodule:: pyspapi
|
||||
|
||||
Справочник API
|
||||
===============
|
||||
|
||||
В следующем разделе описывается API pyspapi.
|
||||
|
||||
Информация о версии
|
||||
---------------------
|
||||
|
||||
Существует два основных способа запроса информации о версии.
|
||||
|
||||
.. data:: version_info
|
||||
|
||||
Именованный кортеж, аналогичный :obj:`py:sys.version_info`.
|
||||
|
||||
Как и в :obj:`py:sys.version_info`, допустимые значения для ``releaselevel`` это
|
||||
'alpha', 'beta', 'candidate' и 'final'.
|
||||
|
||||
.. data:: __version__
|
||||
|
||||
Строковое представление версии.
|
||||
|
||||
``pyspapi``
|
||||
-----------
|
||||
|
||||
``SPAPI``
|
||||
~~~~~~~~~
|
||||
.. autoclass:: SPAPI
|
||||
:members:
|
||||
72
docs/conf.py
Normal file
72
docs/conf.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from importlib.metadata import version as pkg_version
|
||||
import os
|
||||
|
||||
project = "pyspapi"
|
||||
author = "deesiigneer"
|
||||
copyright = "2022, deesiigneer"
|
||||
|
||||
version = pkg_version("pyspapi")
|
||||
release = version
|
||||
|
||||
extensions = [
|
||||
"sphinx.ext.duration",
|
||||
"sphinx.ext.doctest",
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.autosummary",
|
||||
"sphinx.ext.intersphinx",
|
||||
]
|
||||
|
||||
autosummary_generate = True
|
||||
|
||||
version_match = os.environ.get("READTHEDOCS_VERSION")
|
||||
json_url = f"https://pyspapi.readthedocs.io/ru/{version_match}/_static/switcher.json"
|
||||
|
||||
|
||||
language = "ru"
|
||||
exclude_patterns = []
|
||||
html_static_path = ["_static"]
|
||||
|
||||
html_theme = "pydata_sphinx_theme"
|
||||
html_logo = "./images/logo.png"
|
||||
html_favicon = "./images/logo.ico"
|
||||
|
||||
html_theme_options = {
|
||||
"external_links": [
|
||||
{
|
||||
"url": "https://github.com/deesiigneer/pyspapi/releases",
|
||||
"name": "Changelog",
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/sp-worlds/api-docs/wiki",
|
||||
"name": "SPWorlds API Docs",
|
||||
}
|
||||
],
|
||||
"icon_links": [
|
||||
{
|
||||
"name": "GitHub",
|
||||
"url": "https://github.com/deesiigneer/pyspapi",
|
||||
"icon": "fab fa-brands fa-github",
|
||||
"type": "fontawesome",
|
||||
},
|
||||
{
|
||||
"name": "Discord",
|
||||
"url": "https://discord.gg/VbyHaKRAaN",
|
||||
"icon": "fab fa-brands fa-discord",
|
||||
"type": "fontawesome",
|
||||
},
|
||||
{
|
||||
"name": "PyPI",
|
||||
"url": "https://pypi.org/project/pyspapi/",
|
||||
"icon": "fab fa-brands fa-python",
|
||||
"type": "fontawesome",
|
||||
},
|
||||
],
|
||||
"header_links_before_dropdown": 4,
|
||||
"show_toc_level": 1,
|
||||
"navbar_start": ["navbar-logo"],
|
||||
"navigation_with_keys": True,
|
||||
"switcher": {
|
||||
"json_url": json_url,
|
||||
"version_match": version_match,
|
||||
},
|
||||
}
|
||||
BIN
docs/images/logo.ico
Normal file
BIN
docs/images/logo.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
BIN
docs/images/logo.png
Normal file
BIN
docs/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
34
docs/index.rst
Normal file
34
docs/index.rst
Normal file
@@ -0,0 +1,34 @@
|
||||
:theme_html_remove_secondary_sidebar:
|
||||
|
||||
Добро пожаловать в pyspapi
|
||||
===========================
|
||||
|
||||
Обертка API для SPWorlds серверов, написанная на Python.
|
||||
|
||||
Начало работы
|
||||
---------------
|
||||
|
||||
Вы впервые используете библиотеку? Это место, с которого нужно начать!
|
||||
|
||||
- **Первые шаги:** :ref:`Быстрый старт <quickstart>`
|
||||
- **Примеры:** Много примеров доступно в `папке примеров <https://github.com/deesiigneer/pyspapi/tree/main/examples/>`_.
|
||||
|
||||
Получение помощи
|
||||
------------------
|
||||
|
||||
Если у вас есть проблемы с чем-то, эти ресурсы могут помочь.
|
||||
|
||||
- Задавайте вопросы на сервере `Discord <https://discord.gg/VbyHaKRAaN>`_.
|
||||
- Если вы ищете что-то конкретное, попробуйте :ref:`поиск <search>`.
|
||||
- Сообщайте об ошибках в `трекер проблем <https://github.com/deesiigneer/pyspapi/issues>`_.
|
||||
|
||||
Руководства
|
||||
-----------
|
||||
|
||||
Эти страницы подробно описывают все, что может сделать API.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
api
|
||||
quickstart
|
||||
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
35
docs/quickstart.rst
Normal file
35
docs/quickstart.rst
Normal file
@@ -0,0 +1,35 @@
|
||||
:orphan:
|
||||
|
||||
.. _quickstart:
|
||||
|
||||
.. currentmodule:: pyspapi
|
||||
|
||||
Быстрый старт
|
||||
==============
|
||||
|
||||
На этой странице дается краткое введение в библиотеку.
|
||||
|
||||
Проверка баланса
|
||||
-----------------
|
||||
|
||||
Выведем количество денег, оставшихся на счету карты, на консоль.
|
||||
|
||||
Это выглядит примерно так:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyspapi import SPAPI
|
||||
from asyncio import get_event_loop
|
||||
|
||||
spapi = SPAPI(card_id='CARD_ID', token='TOKEN')
|
||||
|
||||
|
||||
async def main():
|
||||
print(await spapi.balance)
|
||||
|
||||
loop = get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
|
||||
Убедитесь, что вы не называете его ``pyspapi``, так как это вызовет конфликт с библиотекой.
|
||||
|
||||
Вы можете найти больше примеров в `папке примеров <https://github.com/deesiigneer/pyspapi/tree/main/examples/>`_ на GitHub.
|
||||
1
docs/requirements.txt
Normal file
1
docs/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
pydata_sphinx_theme
|
||||
15
examples/get_user.py
Normal file
15
examples/get_user.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from pyspapi import SPAPI
|
||||
from asyncio import get_event_loop
|
||||
|
||||
spapi = SPAPI(card_id='CARD_ID', token='TOKEN')
|
||||
|
||||
|
||||
async def main():
|
||||
user = await spapi.get_user(262632724928397312)
|
||||
print(user.username, user.uuid)
|
||||
for card in user.cards:
|
||||
print(card.name, card.number)
|
||||
|
||||
|
||||
loop = get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
12
examples/me.py
Normal file
12
examples/me.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from pyspapi import SPAPI
|
||||
from asyncio import get_event_loop
|
||||
|
||||
spapi = SPAPI(card_id='CARD_ID', token='TOKEN')
|
||||
|
||||
|
||||
async def main():
|
||||
me = await spapi.me
|
||||
print(me)
|
||||
|
||||
loop = get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
21
examples/payments.py
Normal file
21
examples/payments.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from pyspapi import SPAPI
|
||||
from pyspapi.types import Item
|
||||
from asyncio import get_event_loop
|
||||
|
||||
spapi = SPAPI(card_id='CARD_ID', token='TOKEN')
|
||||
|
||||
items = [Item('first item', 1, 2, 'first item comment').to_json(),
|
||||
Item('second item', 3, 4, 'second item comment').to_json()]
|
||||
|
||||
|
||||
async def main():
|
||||
print(await spapi.create_payment(items=items,
|
||||
redirect_url='https://www.google.com/',
|
||||
webhook_url='https://www.google.com/',
|
||||
data='some-data'
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
loop = get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
15
examples/transaction.py
Normal file
15
examples/transaction.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from asyncio import get_event_loop
|
||||
|
||||
from pyspapi import SPAPI
|
||||
|
||||
spapi = SPAPI(card_id='CARD_ID', token='TOKEN')
|
||||
|
||||
|
||||
async def main():
|
||||
new_balance = await spapi.create_transaction(receiver='77552',
|
||||
amount=1,
|
||||
comment='test')
|
||||
print(new_balance)
|
||||
|
||||
loop = get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
13
examples/webhook.py
Normal file
13
examples/webhook.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from pyspapi import SPAPI
|
||||
from asyncio import get_event_loop
|
||||
|
||||
spapi = SPAPI(card_id='CARD_ID', token='TOKEN')
|
||||
|
||||
# print(spapi.webhook_verify(data='webhook_data', header='webhook_header'))
|
||||
|
||||
|
||||
async def main():
|
||||
print(await spapi.update_webhook(url='https://example.com/webhook'))
|
||||
|
||||
loop = get_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
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"
|
||||
13
pyspapi/__init__.py
Normal file
13
pyspapi/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""pyspapi - API wrapper for SP servers written in Python
|
||||
TODO: заполнить описание"""
|
||||
|
||||
import importlib.metadata
|
||||
from .spworlds import SPAPI
|
||||
|
||||
__all__ = [SPAPI]
|
||||
|
||||
__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")
|
||||
3
pyspapi/api/__init__.py
Normal file
3
pyspapi/api/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .api import APISession
|
||||
|
||||
__all__ = [APISession]
|
||||
100
pyspapi/api/api.py
Normal file
100
pyspapi/api/api.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import asyncio
|
||||
import json
|
||||
from base64 import b64encode
|
||||
from logging import getLogger
|
||||
from typing import Optional, Any, Dict
|
||||
|
||||
import aiohttp
|
||||
|
||||
from ..exceptions import ValidationError, SPAPIError
|
||||
|
||||
log = getLogger("pyspapi")
|
||||
|
||||
|
||||
class APISession(object):
|
||||
def __init__(
|
||||
self,
|
||||
card_id: str,
|
||||
token: str,
|
||||
timeout: int = 5,
|
||||
sleep_time: float = 0.2,
|
||||
retries: int = 0,
|
||||
raise_exception: bool = False,
|
||||
proxy: str = None,
|
||||
):
|
||||
self.__url = "https://spworlds.ru/api/public/"
|
||||
self.__id = card_id
|
||||
self.__token = token
|
||||
self.__sleep_timeout = sleep_time
|
||||
self.__retries = retries
|
||||
self.__timeout = timeout
|
||||
self.__raise_exception = raise_exception
|
||||
self.__proxy = proxy
|
||||
self.session: Optional[aiohttp.ClientSession] = None
|
||||
|
||||
async def __aenter__(self):
|
||||
print("proxy=", self.__proxy)
|
||||
self.session = aiohttp.ClientSession(
|
||||
json_serialize=json.dumps,
|
||||
timeout=aiohttp.ClientTimeout(total=self.__timeout),
|
||||
proxy=self.__proxy,
|
||||
)
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *err):
|
||||
await self.session.close()
|
||||
self.session = None
|
||||
|
||||
async def request(
|
||||
self, method: str, endpoint: str, data: Optional[Dict] = None
|
||||
) -> Any:
|
||||
url = self.__url + endpoint
|
||||
headers = {
|
||||
"Authorization": f"Bearer {str(b64encode(str(f'{self.__id}:{self.__token}').encode('utf-8')), 'utf-8')}",
|
||||
"User-Agent": "https://github.com/deesiigneer/pyspapi",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
attempt = 0
|
||||
while True:
|
||||
attempt += 1
|
||||
if attempt > 1:
|
||||
log.warning(
|
||||
f"[pyspapi] Repeat attempt {attempt}: {method.upper()} {url}"
|
||||
)
|
||||
try:
|
||||
async with self.session.request(
|
||||
method, url, json=data, headers=headers
|
||||
) as resp:
|
||||
if resp.status == 422:
|
||||
errors = await resp.json()
|
||||
log.error(f"[pyspapi] Validation error: {errors}")
|
||||
if self.__raise_exception:
|
||||
raise ValidationError(errors)
|
||||
return None
|
||||
|
||||
if resp.status >= 400:
|
||||
content = await resp.text()
|
||||
log.error(f"[pyspapi] API error {resp.status}: {content}")
|
||||
if self.__raise_exception:
|
||||
raise SPAPIError(resp.status, content)
|
||||
return None
|
||||
|
||||
return await resp.json()
|
||||
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
|
||||
log.exception(f"[pyspapi] Connection error: {e} \n attempt {attempt}")
|
||||
if attempt > self.__retries:
|
||||
return None
|
||||
await asyncio.sleep(self.__sleep_timeout)
|
||||
|
||||
async def get(self, endpoint: str) -> Any:
|
||||
async with self:
|
||||
return await self.request("GET", endpoint)
|
||||
|
||||
async def post(self, endpoint: str, data: Optional[Dict] = None) -> Any:
|
||||
async with self:
|
||||
return await self.request("POST", endpoint, data)
|
||||
|
||||
async def put(self, endpoint: str, data: Optional[Dict] = None) -> Any:
|
||||
async with self:
|
||||
return await self.request("PUT", endpoint, data)
|
||||
25
pyspapi/exceptions.py
Normal file
25
pyspapi/exceptions.py
Normal file
@@ -0,0 +1,25 @@
|
||||
class SPAPIError(Exception):
|
||||
"""
|
||||
Базовая ошибка для всех исключений, связанных с API SPWorlds.
|
||||
"""
|
||||
|
||||
def __init__(self, status_code: int, message: str):
|
||||
self.status_code = status_code
|
||||
self.message = message
|
||||
super().__init__(f"[{status_code}] {message}")
|
||||
|
||||
def __str__(self):
|
||||
return f"SPAPIError: [{self.status_code}] {self.message}"
|
||||
|
||||
|
||||
class ValidationError(SPAPIError):
|
||||
"""
|
||||
Ошибка валидации.
|
||||
"""
|
||||
|
||||
def __init__(self, errors):
|
||||
self.errors = errors
|
||||
super().__init__(422, f"Validation failed: {errors}")
|
||||
|
||||
def __str__(self):
|
||||
return f"ValidationError: {self.errors}"
|
||||
197
pyspapi/spworlds.py
Normal file
197
pyspapi/spworlds.py
Normal file
@@ -0,0 +1,197 @@
|
||||
from base64 import b64encode
|
||||
from hashlib import sha256
|
||||
from hmac import new, compare_digest
|
||||
from typing import Optional
|
||||
|
||||
from .api import APISession
|
||||
from pyspapi.types import User
|
||||
from pyspapi.types.me import Account
|
||||
from pyspapi.types.payment import Item
|
||||
|
||||
__all__ = ["SPAPI"]
|
||||
|
||||
|
||||
class SPAPI(APISession):
|
||||
"""
|
||||
pyspapi — высокоуровневый клиент для взаимодействия с SPWorlds API.
|
||||
|
||||
Предоставляет удобные методы для работы с балансом карты, вебхуками,
|
||||
информацией о пользователе, транзакциями и платежами, а также верификацией вебхуков.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
card_id: str,
|
||||
token: str,
|
||||
timeout: int = 5,
|
||||
sleep_time: float = 0.2,
|
||||
retries: int = 0,
|
||||
raise_exception: bool = False,
|
||||
proxy: str = None,
|
||||
):
|
||||
"""
|
||||
Инициализирует объект SPAPI.
|
||||
|
||||
:param card_id: Идентификатор карты.
|
||||
:type card_id: str
|
||||
:param token: Токен API.
|
||||
:type token: str
|
||||
:param timeout: Таймаут для запросов API в секундах. По умолчанию 5.
|
||||
:type timeout: int
|
||||
:param sleep_time: Время ожидания между повторными запросами в секундах. По умолчанию 0.2.
|
||||
:type sleep_time: float
|
||||
:param retries: Количество повторных попыток для неудачных запросов. По умолчанию 0.
|
||||
:type retries: int
|
||||
:param raise_exception: Поднимать исключения при ошибке, если True.
|
||||
:type raise_exception: bool
|
||||
:param proxy: Прокся!
|
||||
:type proxy: str
|
||||
"""
|
||||
super().__init__(
|
||||
card_id, token, timeout, sleep_time, retries, raise_exception, proxy
|
||||
)
|
||||
self.__card_id = card_id
|
||||
self.__token = token
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Возвращает строковое представление объекта SPAPI.
|
||||
"""
|
||||
return f"{self.__class__.__name__}({vars(self)})"
|
||||
|
||||
@property
|
||||
async def balance(self) -> Optional[int]:
|
||||
"""
|
||||
Получает текущий баланс карты.
|
||||
|
||||
:return: Текущий баланс карты.
|
||||
:rtype: int
|
||||
"""
|
||||
return int((await super().get("card"))["balance"])
|
||||
|
||||
@property
|
||||
async def webhook(self) -> Optional[str]:
|
||||
"""
|
||||
Получает URL вебхука, связанного с картой.
|
||||
|
||||
:return: URL вебхука.
|
||||
:rtype: str
|
||||
"""
|
||||
return str((await super().get("card"))["webhook"])
|
||||
|
||||
@property
|
||||
async def me(self) -> Optional[Account]:
|
||||
"""
|
||||
Получает информацию об аккаунте текущего пользователя.
|
||||
|
||||
:return: Объект Account, представляющий аккаунт текущего пользователя.
|
||||
:rtype: :class:`Account`
|
||||
"""
|
||||
me = await super().get("accounts/me")
|
||||
return Account(
|
||||
account_id=me["id"],
|
||||
username=me["username"],
|
||||
minecraftuuid=me["minecraftUUID"],
|
||||
status=me["status"],
|
||||
roles=me["roles"],
|
||||
cities=me["cities"],
|
||||
cards=me["cards"],
|
||||
created_at=me["createdAt"],
|
||||
)
|
||||
|
||||
async def get_user(self, discord_id: int) -> Optional[User]:
|
||||
"""
|
||||
Получает информацию о пользователе по его ID в Discord.
|
||||
|
||||
:param discord_id: ID пользователя в Discord.
|
||||
:type discord_id: int
|
||||
|
||||
:return: Объект User, представляющий пользователя.
|
||||
:rtype: :class:`User`
|
||||
"""
|
||||
user = await super().get(f"users/{discord_id}")
|
||||
if user:
|
||||
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]:
|
||||
"""
|
||||
Создает транзакцию.
|
||||
|
||||
:param receiver: Получатель транзакции.
|
||||
:type receiver: str
|
||||
:param amount: Сумма транзакции.
|
||||
:type amount: int
|
||||
:param comment: Комментарий к транзакции.
|
||||
:type comment: str
|
||||
|
||||
:return: Баланс после транзакции.
|
||||
:rtype: int
|
||||
"""
|
||||
data = {"receiver": receiver, "amount": amount, "comment": comment}
|
||||
|
||||
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]:
|
||||
"""
|
||||
Создает платеж.
|
||||
|
||||
:param webhook_url: URL вебхука для платежа.
|
||||
:type webhook_url: str
|
||||
:param redirect_url: URL для перенаправления после платежа.
|
||||
:type redirect_url: str
|
||||
:param data: Дополнительные данные для платежа.
|
||||
:type data: str
|
||||
:param items: Элементы, включаемые в платеж.
|
||||
|
||||
:return: URL для платежа или None при ошибке.
|
||||
:rtype: str
|
||||
"""
|
||||
data = {
|
||||
"items": items,
|
||||
"redirectUrl": redirect_url,
|
||||
"webhookUrl": webhook_url,
|
||||
"data": data,
|
||||
}
|
||||
|
||||
return str((await super().post("payments", data))["url"])
|
||||
|
||||
async def update_webhook(self, url: str) -> Optional[dict]:
|
||||
"""
|
||||
Обновляет URL вебхука, связанного с картой.
|
||||
|
||||
:param url: Новый URL вебхука.
|
||||
:return: Ответ API в виде словаря или None при ошибке.
|
||||
"""
|
||||
data = {"url": url}
|
||||
|
||||
return await super().put("card/webhook", data)
|
||||
|
||||
def webhook_verify(self, data: str, header: str) -> bool:
|
||||
"""
|
||||
Проверяет достоверность вебхука.
|
||||
|
||||
:param data: Данные из вебхука.
|
||||
:type data: str
|
||||
:param header: Заголовок X-Body-Hash из вебхука.
|
||||
|
||||
:return: True, если заголовок из вебхука достоверен, иначе False.
|
||||
:rtype: bool
|
||||
"""
|
||||
hmac_data = b64encode(new(self.__token.encode("utf-8"), data, sha256).digest())
|
||||
return compare_digest(hmac_data, header.encode("utf-8"))
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""
|
||||
Преобразует объект SPAPI в словарь.
|
||||
|
||||
:return: Словарное представление объекта SPAPI.
|
||||
:rtype: dict
|
||||
"""
|
||||
return self.__dict__.copy()
|
||||
5
pyspapi/types/__init__.py
Normal file
5
pyspapi/types/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .me import Account
|
||||
from .payment import Item
|
||||
from .users import Cards, User
|
||||
|
||||
__all__ = [Account, Item, Cards, User]
|
||||
152
pyspapi/types/me.py
Normal file
152
pyspapi/types/me.py
Normal file
@@ -0,0 +1,152 @@
|
||||
class City:
|
||||
def __init__(self, city_id=None, name=None, x=None, z=None, nether_x=None, nether_z=None, lane=None, role=None,
|
||||
created_at=None):
|
||||
self._id = city_id
|
||||
self._name = name
|
||||
self._x = x
|
||||
self._z = z
|
||||
self._nether_x = nether_x
|
||||
self._nether_z = nether_z
|
||||
self._lane = lane
|
||||
self._role = role
|
||||
self._created_at = created_at
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self._x
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
return self._z
|
||||
|
||||
@property
|
||||
def nether_x(self):
|
||||
return self._nether_x
|
||||
|
||||
@property
|
||||
def nether_z(self):
|
||||
return self._nether_z
|
||||
|
||||
@property
|
||||
def lane(self):
|
||||
return self._lane
|
||||
|
||||
@property
|
||||
def role(self):
|
||||
return self._role
|
||||
|
||||
@property
|
||||
def created_at(self):
|
||||
return self._created_at
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"City(id={self._id}, name={self._name}, x={self._x}, z={self._z}, "
|
||||
f"nether_x={self._nether_x}, nether_z={self._nether_z}, lane={self._lane}, role={self._role}, "
|
||||
f"created_at={self._created_at})"
|
||||
)
|
||||
|
||||
|
||||
class Card:
|
||||
def __init__(self, card_id=None, name=None, number=None, color=None):
|
||||
self._id = card_id
|
||||
self._name = name
|
||||
self._number = number
|
||||
self._color = color
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def number(self):
|
||||
return self._number
|
||||
|
||||
@property
|
||||
def color(self):
|
||||
return self._color
|
||||
|
||||
def __repr__(self):
|
||||
return f"Card(id={self._id}, name={self._name}, number={self._number}, color={self._color})"
|
||||
|
||||
|
||||
class Account:
|
||||
def __init__(self, account_id, username, minecraftuuid, status, roles, created_at, cards, cities):
|
||||
self._id = account_id
|
||||
self._username = username
|
||||
self._minecraftuuid = minecraftuuid
|
||||
self._status = status
|
||||
self._roles = roles
|
||||
self._cities = [
|
||||
City(
|
||||
city_id=city['city_id'],
|
||||
name=city['name'],
|
||||
x=city['x'],
|
||||
z=city['z'],
|
||||
nether_x=city['nether_x'],
|
||||
nether_z=city['nether_z'],
|
||||
lane=city['lane'],
|
||||
role=city['role'],
|
||||
created_at=city['created_at'],
|
||||
)
|
||||
for city in cities
|
||||
]
|
||||
self._cards = [
|
||||
Card(
|
||||
card_id=card["id"],
|
||||
name=card["name"],
|
||||
number=card["number"],
|
||||
color=card["color"],
|
||||
)
|
||||
for card in cards
|
||||
]
|
||||
self._created_at = created_at
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
return self._username
|
||||
|
||||
@property
|
||||
def minecraftuuid(self):
|
||||
return self._minecraftuuid
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
return self._status
|
||||
|
||||
@property
|
||||
def roles(self):
|
||||
return self._roles
|
||||
|
||||
@property
|
||||
def cities(self):
|
||||
return self._cities
|
||||
|
||||
@property
|
||||
def cards(self):
|
||||
return self._cards
|
||||
|
||||
@property
|
||||
def created_at(self):
|
||||
return self._created_at
|
||||
|
||||
def __repr__(self):
|
||||
return (f"Account(id={self._id}, username={self._username}, minecraftUUID={self._minecraftuuid}, "
|
||||
f"status={self._status}, roles={self._roles}, cities={self._cities}, cards={self._cards}, "
|
||||
f"created_at={self._created_at})")
|
||||
18
pyspapi/types/payment.py
Normal file
18
pyspapi/types/payment.py
Normal file
@@ -0,0 +1,18 @@
|
||||
class Item:
|
||||
def __init__(self, name: str, count: int, price: int, comment: str):
|
||||
self._name = name
|
||||
self._count = count
|
||||
self._price = price
|
||||
self._comment = comment
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"name": self._name,
|
||||
"count": self._count,
|
||||
"price": self._price,
|
||||
"comment": self._comment
|
||||
}
|
||||
51
pyspapi/types/users.py
Normal file
51
pyspapi/types/users.py
Normal file
@@ -0,0 +1,51 @@
|
||||
class Cards:
|
||||
|
||||
def __init__(self, name, number):
|
||||
self._name: str = name
|
||||
self._number: str = number
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def number(self):
|
||||
return self._number
|
||||
|
||||
# def __repr__(self):
|
||||
# return "%s(%s)" % (
|
||||
# self.__class__.__name__,
|
||||
# self.__dict__
|
||||
# )
|
||||
|
||||
|
||||
class User:
|
||||
|
||||
def __init__(self, username, uuid, cards):
|
||||
self._username: str = username
|
||||
self._uuid: str = uuid
|
||||
self._cards = [
|
||||
Cards(
|
||||
name=card["name"],
|
||||
number=card["number"],
|
||||
)
|
||||
for card in cards
|
||||
]
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
return self._username
|
||||
|
||||
@property
|
||||
def uuid(self):
|
||||
return self._uuid
|
||||
|
||||
@property
|
||||
def cards(self):
|
||||
return self._cards
|
||||
|
||||
def __repr__(self):
|
||||
return "%s(%s)" % (
|
||||
self.__class__.__name__,
|
||||
self.__dict__
|
||||
)
|
||||
20
setup.py
20
setup.py
@@ -1,20 +0,0 @@
|
||||
from setuptools import setup
|
||||
|
||||
from os import path
|
||||
this_directory = path.abspath(path.dirname(__file__))
|
||||
with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f:
|
||||
description = f.read()
|
||||
|
||||
requires = ['requests==2.25.1']
|
||||
|
||||
setup(
|
||||
name='pyspapi',
|
||||
version='1.0.0a',
|
||||
description='Framework for SP API',
|
||||
long_description=description,
|
||||
long_description_content_type='text/markdown',
|
||||
author='deesiigneer',
|
||||
author_email='xdeesiigneerx@gmail.com',
|
||||
packages=['spapi'],
|
||||
install_requires=requires,
|
||||
)
|
||||
@@ -1,76 +0,0 @@
|
||||
# pyspapi by deesiigneer
|
||||
#
|
||||
|
||||
import base64
|
||||
from requests import post, get
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ApiError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class Api:
|
||||
|
||||
def __init__(self, card_id: str, token: str):
|
||||
self.id = card_id
|
||||
self.token = token
|
||||
self.header = {
|
||||
'Authorization': f"Bearer {str(base64.b64encode(str(f'{self.id}:{self.token}').encode('utf-8')), 'utf-8')}",
|
||||
}
|
||||
|
||||
def _fetch(self, path, data):
|
||||
if path is None:
|
||||
result = get(url=f'https://spworlds.ru/api/public/users/{data}',
|
||||
headers=self.header)
|
||||
else:
|
||||
result = post(
|
||||
url=f'https://spworlds.ru/api/public/{path}',
|
||||
headers=self.header,
|
||||
json=data
|
||||
)
|
||||
if result.status_code == [200, 400]:
|
||||
ApiError(f'Ошибка при запросе к API {result.status_code}')
|
||||
|
||||
return result.json()
|
||||
|
||||
def payment(self, amount, redirecturl, webhookurl, data):
|
||||
"""
|
||||
Создание запроса на оплату.
|
||||
|
||||
:param amount: Стоимость покупки в АРах
|
||||
:param redirecturl: URL страницы, на которую попадет пользователь после оплаты
|
||||
:param webhookurl: URL, куда наш сервер направит запрос, чтобы оповестить ваш сервер об успешной оплате
|
||||
:param data: Строка до 100 символов, сюда можно поместить любые полезные данных.
|
||||
|
||||
:return: url - Ссылка на страницу оплаты, на которую стоит перенаправить пользователя.
|
||||
"""
|
||||
return self._fetch('payment', data={'amount': amount,
|
||||
'redirectUrl': redirecturl,
|
||||
'webhookUrl': webhookurl,
|
||||
'data': data})
|
||||
|
||||
def transaction(self, receiver, amount, comment):
|
||||
"""
|
||||
Перевод АР на карту.
|
||||
|
||||
:param receiver : Номер карты получателя
|
||||
|
||||
:param amount: Количество АР для перевода
|
||||
|
||||
:param comment: Комментарий для перевода
|
||||
"""
|
||||
return self._fetch('transactions', data={'receiver': receiver,
|
||||
'amount': amount,
|
||||
'comment': comment})
|
||||
|
||||
def check_user(self, discord_user_id):
|
||||
"""
|
||||
Проверка на наличие проходки
|
||||
:param discord_user_id: Стоимость покупки в АРах
|
||||
|
||||
:return: username - Ник пользователя или null, если у пользователя нет проходки на сервер.
|
||||
"""
|
||||
return self._fetch(None, data=discord_user_id)
|
||||
Reference in New Issue
Block a user