mirror of
https://github.com/yawaflua/Py-SPW.git
synced 2025-12-10 04:19:31 +02:00
upload v 1.4.1
This commit is contained in:
45
pyspw/Parameters.py
Normal file
45
pyspw/Parameters.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
class PaymentParameters:
|
||||||
|
def __init__(self, amount: int, redirectUrl: str, webhookUrl: str, data: str):
|
||||||
|
"""
|
||||||
|
Создание параметров ссылки на оплату
|
||||||
|
:param amount: Стоимость покупки в АРах.
|
||||||
|
:param redirectUrl: URL страницы, на которую попадет пользователь после оплаты.
|
||||||
|
:param webhookUrl: URL, куда наш сервер направит запрос, чтобы оповестить ваш сервер об успешной оплате.
|
||||||
|
:param data: Строка до 100 символов, сюда можно помеcтить любые полезные данных.
|
||||||
|
:return: Str ссылка на страницу оплаты, на которую стоит перенаправить пользователя.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.amount = amount
|
||||||
|
self.redirectUrl = redirectUrl
|
||||||
|
self.webhookUrl = webhookUrl
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'''
|
||||||
|
amount: {self.amount}
|
||||||
|
redirectUrl: {self.redirectUrl}
|
||||||
|
webhookUrl: {self.webhookUrl}
|
||||||
|
data: {self.data}
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionParameters:
|
||||||
|
def __init__(self, receiver: str, amount: int, comment: str = 'No comment'):
|
||||||
|
"""
|
||||||
|
Отправка транзакции
|
||||||
|
:param receiver: Номер карты на которую будет совершена транзакция.
|
||||||
|
:param amount: Сумма транзакции.
|
||||||
|
:param comment: Комментарий к транзакции.
|
||||||
|
:return: None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.receiver = receiver
|
||||||
|
self.amount = amount
|
||||||
|
self.comment = comment
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'''
|
||||||
|
receiver: {self.receiver}
|
||||||
|
amount: {str(self.amount)}
|
||||||
|
comment: {self.comment}
|
||||||
|
'''
|
||||||
100
pyspw/Skin.py
Normal file
100
pyspw/Skin.py
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import requests as rq
|
||||||
|
from mojang import MojangAPI
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from . import errors as err
|
||||||
|
|
||||||
|
|
||||||
|
class SkinPart:
|
||||||
|
def __init__(self, url: str):
|
||||||
|
self.__skin_part_url = url
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.get_url()
|
||||||
|
|
||||||
|
def __bytes__(self):
|
||||||
|
return self.get_image()
|
||||||
|
|
||||||
|
def get_url(self) -> str:
|
||||||
|
return self.__skin_part_url
|
||||||
|
|
||||||
|
def get_image(self) -> bytes:
|
||||||
|
try:
|
||||||
|
visage_surgeplay_response = rq.get(self.__skin_part_url)
|
||||||
|
if visage_surgeplay_response.status_code != 200:
|
||||||
|
raise err.SurgeplayApiError(f'HTTP status: {visage_surgeplay_response.status_code}')
|
||||||
|
return visage_surgeplay_response.content
|
||||||
|
|
||||||
|
except rq.exceptions.ConnectionError as error:
|
||||||
|
raise err.SurgeplayApiError(error)
|
||||||
|
|
||||||
|
|
||||||
|
class Cape:
|
||||||
|
def __init__(self, url: Optional[str]):
|
||||||
|
self.__skin_part_url = url
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.__skin_part_url is None:
|
||||||
|
return 'None'
|
||||||
|
|
||||||
|
return self.get_url()
|
||||||
|
|
||||||
|
def __bytes__(self):
|
||||||
|
image = self.get_image()
|
||||||
|
if image is None:
|
||||||
|
return bytes(0)
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
def get_url(self) -> Optional[str]:
|
||||||
|
if self.__skin_part_url is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.__skin_part_url
|
||||||
|
|
||||||
|
def get_image(self) -> Optional[bytes]:
|
||||||
|
if self.__skin_part_url is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
visage_surgeplay_response = rq.get(self.__skin_part_url)
|
||||||
|
if visage_surgeplay_response.status_code != 200:
|
||||||
|
raise err.SurgeplayApiError(f'HTTP status: {visage_surgeplay_response.status_code}')
|
||||||
|
return visage_surgeplay_response.content
|
||||||
|
|
||||||
|
except rq.exceptions.ConnectionError as error:
|
||||||
|
raise err.SurgeplayApiError(error)
|
||||||
|
|
||||||
|
|
||||||
|
class Skin:
|
||||||
|
__visage_surgeplay_url = 'https://visage.surgeplay.com/'
|
||||||
|
|
||||||
|
def __init__(self, uuid: str):
|
||||||
|
self.__uuid = uuid
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.get_skin().__str__()
|
||||||
|
|
||||||
|
def get_face(self, image_size: int = 64) -> SkinPart:
|
||||||
|
return SkinPart(f'https://visage.surgeplay.com/face/{image_size}/{self.__uuid}')
|
||||||
|
|
||||||
|
def get_front(self, image_size: int = 64) -> SkinPart:
|
||||||
|
return SkinPart(f'https://visage.surgeplay.com/front/{image_size}/{self.__uuid}')
|
||||||
|
|
||||||
|
def get_front_full(self, image_size: int = 64) -> SkinPart:
|
||||||
|
return SkinPart(f'https://visage.surgeplay.com/frontfull/{image_size}/{self.__uuid}')
|
||||||
|
|
||||||
|
def get_head(self, image_size: int = 64) -> SkinPart:
|
||||||
|
return SkinPart(f'https://visage.surgeplay.com/head/{image_size}/{self.__uuid}')
|
||||||
|
|
||||||
|
def get_bust(self, image_size: int = 64) -> SkinPart:
|
||||||
|
return SkinPart(f'https://visage.surgeplay.com/bust/{image_size}/{self.__uuid}')
|
||||||
|
|
||||||
|
def get_full(self, image_size: int = 64) -> SkinPart:
|
||||||
|
return SkinPart(f'https://visage.surgeplay.com/full/{image_size}/{self.__uuid}')
|
||||||
|
|
||||||
|
def get_skin(self, image_size: int = 64) -> SkinPart:
|
||||||
|
return SkinPart(f'https://visage.surgeplay.com/skin/{image_size}/{self.__uuid}')
|
||||||
|
|
||||||
|
def get_cape(self) -> Cape:
|
||||||
|
return Cape(MojangAPI.get_profile(self.__uuid).cape_url)
|
||||||
37
pyspw/User.py
Normal file
37
pyspw/User.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from typing import List, Dict, Any, Optional
|
||||||
|
from mojang import MojangAPI
|
||||||
|
|
||||||
|
from .Skin import Skin
|
||||||
|
|
||||||
|
|
||||||
|
class User:
|
||||||
|
def __init__(self, nickname: str | None, use_mojang_api: bool = True):
|
||||||
|
self.nickname = nickname
|
||||||
|
|
||||||
|
if self.nickname is not None:
|
||||||
|
self.access = True
|
||||||
|
|
||||||
|
if use_mojang_api:
|
||||||
|
self.uuid = MojangAPI.get_uuid(nickname)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.uuid = None
|
||||||
|
self.access = False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.nickname is None:
|
||||||
|
return 'None'
|
||||||
|
|
||||||
|
return self.nickname
|
||||||
|
|
||||||
|
def get_skin(self) -> Optional[Skin]:
|
||||||
|
if self.uuid is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return Skin(self.uuid)
|
||||||
|
|
||||||
|
def get_nickname_history(self) -> Optional[List[Dict[str, Any]]]:
|
||||||
|
if self.uuid is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return MojangAPI.get_name_history(self.uuid)
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
from . import api
|
from . import api
|
||||||
from . import errors
|
from . import errors
|
||||||
|
from . import User
|
||||||
|
from . import Skin
|
||||||
|
from . import Parameters
|
||||||
|
|
||||||
__all__ = ["Api", "errors"]
|
__all__ = ["SpApi", "errors", "User", "Skin"]
|
||||||
__version__ = 1.0
|
|
||||||
|
|
||||||
|
|
||||||
class Api(api.sp_api_base):
|
class SpApi(api.Py_SPW):
|
||||||
pass
|
pass
|
||||||
|
|||||||
236
pyspw/api.py
236
pyspw/api.py
@@ -3,28 +3,31 @@ from hashlib import sha256
|
|||||||
import hmac
|
import hmac
|
||||||
import requests as rq
|
import requests as rq
|
||||||
import time
|
import time
|
||||||
|
from typing import Optional, List
|
||||||
|
import logging
|
||||||
|
from mojang import MojangAPI
|
||||||
|
|
||||||
from . import errors as err
|
from . import errors as err
|
||||||
|
from .User import User
|
||||||
|
from .Parameters import PaymentParameters, TransactionParameters
|
||||||
|
|
||||||
accessed_body_part = ['face', 'front', 'frontfull', 'head', 'bust', 'full', 'skin']
|
# deesiigneer stole some of my ideas and improved them. But I didn't lose my head and improved what deesiigneer improved :)
|
||||||
|
|
||||||
|
|
||||||
class sp_api_base:
|
class Py_SPW:
|
||||||
|
__spworlds_api_url = 'https://spworlds.ru/api/public'
|
||||||
|
|
||||||
def __init__(self, card_id: str, card_token: str):
|
def __init__(self, card_id: str, card_token: str):
|
||||||
self.card_token = card_token
|
self.__card_token = card_token
|
||||||
self.authorization = f"Bearer {str(b64encode(str(f'{card_id}:{card_token}').encode('utf-8')), 'utf-8')}"
|
self.__authorization = f"Bearer {str(b64encode(str(f'{card_id}:{card_token}').encode('utf-8')), 'utf-8')}"
|
||||||
self.host = 'https://spworlds.ru/api/public'
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __get(self, path: str = None, ignore_status_code: bool = False) -> rq.Response:
|
def __get(self, path: str = None, ignore_status_code: bool = False) -> rq.Response:
|
||||||
headers = {
|
headers = {
|
||||||
'Authorization': self.authorization,
|
'Authorization': self.__authorization,
|
||||||
'User-Agent': 'Py-SPW'
|
'User-Agent': 'Py-SPW'
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
response = rq.get(url=self.host + path, headers=headers)
|
response = rq.get(url=self.__spworlds_api_url + path, headers=headers)
|
||||||
|
|
||||||
except rq.exceptions.ConnectionError as error:
|
except rq.exceptions.ConnectionError as error:
|
||||||
raise err.SpwApiError(error)
|
raise err.SpwApiError(error)
|
||||||
@@ -44,11 +47,11 @@ class sp_api_base:
|
|||||||
|
|
||||||
def __post(self, path: str = None, body: dict = None) -> rq.Response:
|
def __post(self, path: str = None, body: dict = None) -> rq.Response:
|
||||||
headers = {
|
headers = {
|
||||||
'Authorization': self.authorization,
|
'Authorization': self.__authorization,
|
||||||
'User-Agent': 'Py-SPW'
|
'User-Agent': 'Py-SPW'
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
response = rq.post(url=self.host + path, headers=headers, json=body)
|
response = rq.post(url=self.__spworlds_api_url + path, headers=headers, json=body)
|
||||||
|
|
||||||
except rq.exceptions.ConnectionError as error:
|
except rq.exceptions.ConnectionError as error:
|
||||||
raise err.SpwApiError(error)
|
raise err.SpwApiError(error)
|
||||||
@@ -62,19 +65,21 @@ class sp_api_base:
|
|||||||
else:
|
else:
|
||||||
raise err.SpwApiError(f'HTTP: {response.status_code} {response.json()["error"]}. Message: {response.json()["message"]}')
|
raise err.SpwApiError(f'HTTP: {response.status_code} {response.json()["error"]}. Message: {response.json()["message"]}')
|
||||||
|
|
||||||
def get_user(self, discord_id: str) -> str | None:
|
def get_user(self, discord_id: str, use_mojang_api: bool = True) -> User:
|
||||||
"""
|
"""
|
||||||
Получение ника пользователя.
|
Получение пользователя
|
||||||
|
:param use_mojang_api: Если True то будет обращаться к Mojang API для получения UUID, иначе обращаться не будет
|
||||||
:param discord_id: ID пользователя дискорда.
|
:param discord_id: ID пользователя дискорда.
|
||||||
:return: Str если пользователь найден, None если пользователь не найден. В str содержиться никнейм пользователя
|
:return: Class pyspw.User.User
|
||||||
"""
|
"""
|
||||||
|
|
||||||
response = self.__get(f'/users/{discord_id}', True)
|
response = self.__get(f'/users/{discord_id}', True)
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
return response.json()['username']
|
return User(response.json()['username'], use_mojang_api)
|
||||||
|
|
||||||
elif response.status_code == 404:
|
elif response.status_code == 404:
|
||||||
return None
|
return User(None, use_mojang_api)
|
||||||
|
|
||||||
elif response.status_code >= 500:
|
elif response.status_code >= 500:
|
||||||
raise err.SpwApiError(f'HTTP: {response.status_code}, Server Error.')
|
raise err.SpwApiError(f'HTTP: {response.status_code}, Server Error.')
|
||||||
@@ -82,69 +87,64 @@ class sp_api_base:
|
|||||||
else:
|
else:
|
||||||
raise err.SpwApiError(f'HTTP: {response.status_code} {response.json()["error"]}. Message: {response.json()["message"]}')
|
raise err.SpwApiError(f'HTTP: {response.status_code} {response.json()["error"]}. Message: {response.json()["message"]}')
|
||||||
|
|
||||||
|
def get_users(self, discord_ids: List[str], delay: float = 0.5, use_mojang_api: bool = True) -> List[User]:
|
||||||
|
"""
|
||||||
|
Получение пользователей
|
||||||
|
:param use_mojang_api: Если True то будет обращаться к Mojang API для получения UUID, иначе обращаться не будет
|
||||||
|
:param delay: Значение задержки между запросами, указывается в секундах
|
||||||
|
:param discord_ids: List с IDs пользователей дискорда.
|
||||||
|
:return: List содержащий Classes pyspw.User.User
|
||||||
|
"""
|
||||||
|
|
||||||
|
users = []
|
||||||
|
|
||||||
|
if len(discord_ids) > 100 and delay < 0.5:
|
||||||
|
logging.warning('You send DOS attack to SPWorlds API. Please set the delay to greater than or equal to 0.5')
|
||||||
|
|
||||||
|
for discord_id in discord_ids:
|
||||||
|
users.append(self.get_user(discord_id, False))
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
|
if use_mojang_api:
|
||||||
|
nicknames = [user.nickname for user in users]
|
||||||
|
uuids = MojangAPI.get_uuids(nicknames)
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
user.uuid = uuids[user.nickname]
|
||||||
|
|
||||||
|
return users
|
||||||
|
|
||||||
def check_access(self, discord_id: str) -> bool:
|
def check_access(self, discord_id: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Получение статуса проходки.
|
Получение статуса проходки
|
||||||
:param discord_id: ID пользователя дискорда.
|
:param discord_id: ID пользователя дискорда.
|
||||||
:return: Bool - False если у пользователя не имеется проходки, True если у пользователя есть проходка
|
:return: Bool True если у пользователя есть проходка, иначе False
|
||||||
"""
|
"""
|
||||||
return False if self.get_user(discord_id) is None else True
|
return self.get_user(discord_id, False).access
|
||||||
|
|
||||||
def get_user_uuid(self, discord_id: str) -> str | None:
|
def check_accesses(self, discord_ids: List[str], delay: float = 0.5) -> List[bool]:
|
||||||
username = self.get_user(discord_id)
|
|
||||||
if username is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
mojang_response = rq.get(f'https://api.mojang.com/users/profiles/minecraft/{username}')
|
|
||||||
if mojang_response.status_code != 200:
|
|
||||||
raise err.MojangApiError(f'HTTP status: {mojang_response.status_code}')
|
|
||||||
return mojang_response.json()['id']
|
|
||||||
|
|
||||||
except rq.exceptions.ConnectionError as error:
|
|
||||||
raise err.MojangApiError(error)
|
|
||||||
|
|
||||||
except rq.exceptions.JSONDecodeError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_user_skin_url(self, discord_id: str, body_part: str, image_size: int = 64) -> str | None:
|
|
||||||
"""
|
"""
|
||||||
Получение изображения части скина.
|
Получение статуса проходок
|
||||||
:param discord_id: ID пользователя дискорда.
|
:param delay: Значение задержки между запросами, указывается в секундах
|
||||||
:param body_part: Часть тела для получения. Допустимые значения - https://visage.surgeplay.com/index.html
|
:param discord_ids: List с IDs пользователей дискорда.
|
||||||
:param image_size: Размер получаемого изображения.
|
:return: List содержащий bool со значением статуса проходки
|
||||||
:return: Str если пользователь найден, None если пользователь не найден. В str содержится ссылка на изображение профиля
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if body_part not in accessed_body_part:
|
accesses = []
|
||||||
raise err.BadSkinPartName(f'"{body_part}" is not a part of the skin')
|
|
||||||
|
|
||||||
uuid = self.get_user_uuid(discord_id)
|
users = self.get_users(discord_ids, delay, False)
|
||||||
if uuid is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return f'https://visage.surgeplay.com/{body_part}/{image_size}/{uuid}'
|
if len(discord_ids) > 100 and delay < 0.5:
|
||||||
|
logging.warning('You send DOS attack to SPWorlds API. Please set the delay to greater than or equal to 0.5')
|
||||||
|
|
||||||
def get_user_skin(self, discord_id: str, body_part: str, image_size: int = 64) -> bytes | None:
|
for user in users:
|
||||||
"""
|
if user is not None:
|
||||||
Получение изображения части скина.
|
accesses.append(True)
|
||||||
:param discord_id: ID пользователя дискорда.
|
|
||||||
:param body_part: Часть тела для получения. Допустимые значения - https://visage.surgeplay.com/index.html
|
|
||||||
:param image_size: Размер получаемого изображения.
|
|
||||||
:return: Bytes если пользователь найден, None если пользователь не найден. В bytes содержиться изображение профиля
|
|
||||||
"""
|
|
||||||
url = self.get_user_skin_url(discord_id, body_part, image_size)
|
|
||||||
if url is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
else:
|
||||||
surgeplay_response = rq.get(url)
|
accesses.append(False)
|
||||||
if surgeplay_response.status_code != 200:
|
|
||||||
raise err.SurgeplayApiError(f'HTTP status: {surgeplay_response.status_code}')
|
|
||||||
return surgeplay_response.content
|
|
||||||
|
|
||||||
except rq.exceptions.ConnectionError as error:
|
return accesses
|
||||||
raise err.SurgeplayApiError(error)
|
|
||||||
|
|
||||||
def check_webhook(self, webhook_data: str, X_Body_Hash: str) -> bool:
|
def check_webhook(self, webhook_data: str, X_Body_Hash: str) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -153,98 +153,78 @@ class sp_api_base:
|
|||||||
:param X_Body_Hash: Хэдер X-Body-Hash из webhook.
|
:param X_Body_Hash: Хэдер X-Body-Hash из webhook.
|
||||||
:return: Bool True если вебхук пришел от верифицированного сервера, иначе False
|
:return: Bool True если вебхук пришел от верифицированного сервера, иначе False
|
||||||
"""
|
"""
|
||||||
hmac_data = hmac.new(self.card_token.encode('utf-8'), webhook_data.encode('utf-8'), sha256).digest()
|
|
||||||
|
hmac_data = hmac.new(self.__card_token.encode('utf-8'), webhook_data.encode('utf-8'), sha256).digest()
|
||||||
base64_data = b64encode(hmac_data)
|
base64_data = b64encode(hmac_data)
|
||||||
return hmac.compare_digest(base64_data, X_Body_Hash.encode('utf-8'))
|
return hmac.compare_digest(base64_data, X_Body_Hash.encode('utf-8'))
|
||||||
|
|
||||||
def create_payment(self, amount: int, redirectUrl: str, webhookUrl: str, data: str = '') -> str:
|
def create_payment(self, params: PaymentParameters) -> str:
|
||||||
"""
|
"""
|
||||||
Создание ссылки на оплату
|
Создание ссылки на оплату
|
||||||
:param amount: Стоимость покупки в АРах.
|
:param params: class PaymentParams параметров оплаты
|
||||||
:param redirectUrl: URL страницы, на которую попадет пользователь после оплаты.
|
|
||||||
:param webhookUrl: URL, куда наш сервер направит запрос, чтобы оповестить ваш сервер об успешной оплате.
|
|
||||||
:param data: Строка до 100 символов, сюда можно пометить любые полезные данных.
|
|
||||||
:return: Str ссылка на страницу оплаты, на которую стоит перенаправить пользователя.
|
:return: Str ссылка на страницу оплаты, на которую стоит перенаправить пользователя.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
body = {
|
body = {
|
||||||
'amount': amount,
|
'amount': params.amount,
|
||||||
'redirectUrl': redirectUrl,
|
'redirectUrl': params.redirectUrl,
|
||||||
'webhookUrl': webhookUrl,
|
'webhookUrl': params.webhookUrl,
|
||||||
'data': data
|
'data': params.data
|
||||||
}
|
}
|
||||||
return self.__post('/payment', body).json()['url']
|
return self.__post('/payment', body).json()['url']
|
||||||
|
|
||||||
def create_payments(self, payments: tuple, request_delay: float = 0.1) -> list:
|
def create_payments(self, payments: List[PaymentParameters], delay: float = 0.5) -> list:
|
||||||
"""
|
"""
|
||||||
Создание ссылок на оплату
|
Создание ссылок на оплату
|
||||||
:param request_delay: Значение задержки между запросами, указывается в секундах
|
:param payments: Список содержащий classes PaymentParams
|
||||||
:param payments: Кортеж содержащий словари со следующими параметрами:
|
:param delay: Значение задержки между запросами, указывается в секундах
|
||||||
:parameter amount: Стоимость покупки в АРах.
|
:return: List со ссылками на страницы оплаты, в том порядке, в котором они были в кортеже payments
|
||||||
:parameter redirectUrl: URL страницы, на которую попадет пользователь после оплаты.
|
|
||||||
:parameter webhookUrl: URL, куда наш сервер направит запрос, чтобы оповестить ваш сервер об успешной оплате.
|
|
||||||
:parameter data: Строка до 100 символов, сюда можно пометить любые полезные данных.
|
|
||||||
:return: List с ссылками на страницы оплаты, в том порядке, в котором они были в кортеже payments
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
answer = []
|
answer = []
|
||||||
|
|
||||||
|
if len(payments) > 100 and delay < 0.5:
|
||||||
|
logging.warning('You send DOS attack to SPWorlds API. Please set the delay to greater than or equal to 0.5')
|
||||||
|
|
||||||
for payment in payments:
|
for payment in payments:
|
||||||
try:
|
answer.append(self.create_payment(payment))
|
||||||
answer.append(self.create_payment(
|
time.sleep(delay)
|
||||||
int(payment['amount']),
|
|
||||||
str(payment['redirectUrl']),
|
|
||||||
str(payment['webhookUrl']),
|
|
||||||
str(payment['data'])
|
|
||||||
))
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
raise err.BadParameter('Amount must be int')
|
|
||||||
|
|
||||||
except KeyError as error:
|
|
||||||
raise err.BadParameter(f'Missing parameter {error}')
|
|
||||||
|
|
||||||
time.sleep(request_delay)
|
|
||||||
|
|
||||||
return answer
|
return answer
|
||||||
|
|
||||||
def send_transaction(self, receiver: str, amount: int, comment: str = 'Нет комментария') -> None:
|
def send_transaction(self, params: TransactionParameters) -> None:
|
||||||
"""
|
"""
|
||||||
Отправка транзакции
|
Отправка транзакции
|
||||||
:param receiver: Номер карты на которую будет совершена транзакция.
|
:param params: class TransactionParameters параметры транзакции
|
||||||
:param amount: Сумма транзакции.
|
|
||||||
:param comment: Комментарий к транзакции.
|
|
||||||
:return: None.
|
:return: None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
body = {
|
body = {
|
||||||
'receiver': receiver,
|
'receiver': params.receiver,
|
||||||
'amount': amount,
|
'amount': params.amount,
|
||||||
'comment': comment
|
'comment': params.comment
|
||||||
}
|
}
|
||||||
self.__post('/transactions', body)
|
self.__post('/transactions', body)
|
||||||
|
|
||||||
def send_transactions(self, transactions: tuple, request_delay: float = 0.1) -> None:
|
def send_transactions(self, transactions: List[TransactionParameters], delay: float = 0.1) -> None:
|
||||||
"""
|
"""
|
||||||
Отправка транзакций
|
Отправка транзакций
|
||||||
:param request_delay: Значение задержки между запросами, указывается в секундах
|
:param delay: Значение задержки между запросами, указывается в секундах
|
||||||
:param transactions: Кортеж содержащий словари со следующими параметрами:
|
:param transactions: Список содержащий classes TransactionParameters
|
||||||
:param receiver: Номер карты на которую будет совершена транзакция.
|
:return: List со ссылками на страницы оплаты, в том порядке, в котором они были в кортеже payments
|
||||||
:param amount: Сумма транзакции.
|
|
||||||
:param comment: Комментарий к транзакции.
|
|
||||||
:return: List с ссылками на страницы оплаты, в том порядке, в котором они были в кортеже payments
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if len(transactions) > 100 and delay < 0.5:
|
||||||
|
logging.warning('You send DOS attack to SPWorlds API. Please set the delay to greater than or equal to 0.5')
|
||||||
|
|
||||||
for transaction in transactions:
|
for transaction in transactions:
|
||||||
try:
|
self.send_transaction(transaction)
|
||||||
self.send_transaction(
|
time.sleep(delay)
|
||||||
str(transaction['receiver']),
|
|
||||||
int(transaction['amount']),
|
|
||||||
str(transaction['comment'])
|
|
||||||
)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
raise err.BadParameter('Amount must be int')
|
|
||||||
|
|
||||||
except KeyError as error:
|
|
||||||
raise err.BadParameter(f'Missing parameter {error}')
|
|
||||||
|
|
||||||
time.sleep(request_delay)
|
|
||||||
|
|
||||||
def get_balance(self) -> int:
|
def get_balance(self) -> int:
|
||||||
|
"""
|
||||||
|
Получение баланса
|
||||||
|
:return: Int со значением баланса
|
||||||
|
"""
|
||||||
|
|
||||||
return self.__get('/card').json()['balance']
|
return self.__get('/card').json()['balance']
|
||||||
|
|||||||
15
setup.py
15
setup.py
@@ -6,11 +6,14 @@ this_directory = path.abspath(path.dirname(__file__))
|
|||||||
with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f:
|
with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f:
|
||||||
description_md = f.read()
|
description_md = f.read()
|
||||||
|
|
||||||
requires = ['requests==2.28.1']
|
requirements = [
|
||||||
|
'requests==2.28.1',
|
||||||
|
'mojang==0.2.0'
|
||||||
|
]
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='Py-SPW',
|
name='Py-SPW',
|
||||||
version='1.3',
|
version='1.4.1',
|
||||||
packages=['pyspw'],
|
packages=['pyspw'],
|
||||||
url='https://github.com/teleport2/Py-SPW',
|
url='https://github.com/teleport2/Py-SPW',
|
||||||
license='MIT License',
|
license='MIT License',
|
||||||
@@ -18,5 +21,11 @@ setup(
|
|||||||
author_email='stepan@m.khoz.ru',
|
author_email='stepan@m.khoz.ru',
|
||||||
description='Python library for spworlds API',
|
description='Python library for spworlds API',
|
||||||
long_description=description_md,
|
long_description=description_md,
|
||||||
long_description_content_type='text/markdown'
|
long_description_content_type='text/markdown',
|
||||||
|
install_requires=requirements,
|
||||||
|
python_requires='>=3.10.5',
|
||||||
|
project_urls={
|
||||||
|
"Docs": "https://github.com/teleport2/Py-SPW/wiki",
|
||||||
|
"GitHub": "https://github.com/teleport2/Py-SPW"
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user