mirror of
https://github.com/SantaSpeen/anixart.git
synced 2026-05-20 00:20:26 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aee5c542b4 | |||
| c740242c3f | |||
| 2f18c5ce85 | |||
| 9c94b68d55 | |||
| bb852d2b3a | |||
| d9a577ee14 | |||
| 52ebb42aa2 | |||
| d726088002 | |||
| 577e8dbf2a | |||
| c68735df97 | |||
| dd550855c0 | |||
| 733da877fa | |||
| 14fb575548 | |||
| 8762cca288 | |||
| d288837bb4 | |||
| 3bd27840e1 | |||
| 87fa813700 | |||
| 29205efe4f | |||
| 68e45336ad | |||
| ae1f97a07c |
@@ -132,3 +132,4 @@ dmypy.json
|
|||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
poetry.lock
|
poetry.lock
|
||||||
|
secrets.txt
|
||||||
+3
-1
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
from .__meta__ import *
|
from .__meta__ import *
|
||||||
|
|
||||||
|
from .api import AnixartAPI
|
||||||
|
from .auth import AnixartAccount, AnixartAccountGuest, AnixartAccountSaved
|
||||||
|
|
||||||
from .endpoints import *
|
from .endpoints import *
|
||||||
from .api.api import AnixartUserAccount, AnixartAPI
|
|
||||||
|
|
||||||
from . import enums
|
from . import enums
|
||||||
from . import exceptions
|
from . import exceptions
|
||||||
|
|||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from .__meta__ import __version__, __build__
|
||||||
|
from .auth import AnixartAccount, AnixartAccountGuest
|
||||||
|
from .enums import AnixartApiErrors
|
||||||
|
from .endpoints import API_URL
|
||||||
|
from .exceptions import AnixartAPIRequestError, AnixartAPIError
|
||||||
|
from .exceptions import AnixartInitError
|
||||||
|
|
||||||
|
|
||||||
|
class AnixartAPI:
|
||||||
|
|
||||||
|
def __init__(self, account: AnixartAccount = None):
|
||||||
|
if account is None:
|
||||||
|
account = AnixartAccountGuest()
|
||||||
|
if not isinstance(account, AnixartAccount):
|
||||||
|
raise AnixartInitError(f'Use class "AnixartAccount" for user. But {type(account)} given.')
|
||||||
|
|
||||||
|
self.use_account(account)
|
||||||
|
|
||||||
|
def use_account(self, account: AnixartAccount):
|
||||||
|
if not isinstance(account, AnixartAccount):
|
||||||
|
raise AnixartInitError(f'Use class "AnixartAccount" for user. But {type(account)} given.')
|
||||||
|
self.__account = account
|
||||||
|
self.__account._set_api(self)
|
||||||
|
self.__account.login()
|
||||||
|
|
||||||
|
self.__token = account.token
|
||||||
|
self._session = account.session
|
||||||
|
self._session.headers = {
|
||||||
|
'User-Agent': f'AnixartPyAPI/{__version__}-{__build__} (Linux; Android 15; AnixartPyAPI Build/{__build__})'
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def account(self):
|
||||||
|
return self.__account
|
||||||
|
|
||||||
|
def __parse_response(self, res: requests.Response):
|
||||||
|
if res.status_code != 200:
|
||||||
|
e = AnixartAPIRequestError("Bad Request: Invalid request parameters.")
|
||||||
|
e.message = (
|
||||||
|
f"Bad Request: Invalid request parameters.\n"
|
||||||
|
f"Request: {res.request.method} {res.url}\n"
|
||||||
|
f"Status code: {res.status_code}\n"
|
||||||
|
f"Response: {res.text}\n"
|
||||||
|
f"Client headers: {self._session.headers}\n"
|
||||||
|
f"Client payload: {res.request.body}"
|
||||||
|
)
|
||||||
|
e.code = 400
|
||||||
|
raise e
|
||||||
|
if not res.text:
|
||||||
|
raise AnixartAPIError("AnixartAPI send unknown error: Empty response. Is provided data correct?")
|
||||||
|
try:
|
||||||
|
response = res.json()
|
||||||
|
except ValueError as e:
|
||||||
|
raise AnixartAPIError("Failed to parse JSON response")
|
||||||
|
|
||||||
|
# print(response)
|
||||||
|
if response['code'] != 0:
|
||||||
|
code = response['code']
|
||||||
|
if code in AnixartApiErrors:
|
||||||
|
e = AnixartAPIError(f"AnixartAPI send error: {AnixartApiErrors(code).name}")
|
||||||
|
e.message = AnixartApiErrors(code).name
|
||||||
|
else:
|
||||||
|
e = AnixartAPIError(f"AnixartAPI send unknown error, code: {response['code']}")
|
||||||
|
e.code = response['code']
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def _post(self, method: str, payload: dict = None, is_json: bool = False, **kwargs):
|
||||||
|
if payload is None:
|
||||||
|
payload = {}
|
||||||
|
url = API_URL + method
|
||||||
|
if payload.get("token") is None:
|
||||||
|
if self.__token is not None:
|
||||||
|
payload.update({"token": self.__token})
|
||||||
|
url += "?token=" + self.__token
|
||||||
|
else:
|
||||||
|
token = kwargs.get("token")
|
||||||
|
if token is not None:
|
||||||
|
payload.update({"token": token})
|
||||||
|
url += "?token=" + token
|
||||||
|
kwargs = {"url": url}
|
||||||
|
if is_json:
|
||||||
|
self._session.headers["Content-Type"] = "application/json; charset=UTF-8"
|
||||||
|
self._session.headers["Content-Length"] = str(len(payload))
|
||||||
|
kwargs.update({"json": payload})
|
||||||
|
else:
|
||||||
|
kwargs.update({"data": payload})
|
||||||
|
res = self._session.post(**kwargs)
|
||||||
|
self._session.headers["Content-Type"] = ""
|
||||||
|
self._session.headers["Content-Length"] = ""
|
||||||
|
return self.__parse_response(res)
|
||||||
|
|
||||||
|
def _get(self, method: str, payload: dict = None, **kwargs):
|
||||||
|
if payload is None:
|
||||||
|
payload = {}
|
||||||
|
if payload.get("token") is None:
|
||||||
|
if self.__token is not None:
|
||||||
|
payload.update({"token": self.__token})
|
||||||
|
else:
|
||||||
|
token = kwargs.get("token")
|
||||||
|
if token is not None:
|
||||||
|
payload.update({"token": token})
|
||||||
|
res = self._session.get(API_URL + method, params=payload)
|
||||||
|
return self.__parse_response(res)
|
||||||
|
|
||||||
|
def execute(self, http_method, endpoint, **kwargs):
|
||||||
|
http_method = http_method.upper()
|
||||||
|
if http_method == "GET":
|
||||||
|
return self._get(endpoint, **kwargs)
|
||||||
|
elif http_method == "POST":
|
||||||
|
return self._post(endpoint, **kwargs)
|
||||||
|
else:
|
||||||
|
raise AnixartAPIRequestError("Allow only GET and POST requests.")
|
||||||
|
|
||||||
|
def get(self, endpoint, *args, **kwargs):
|
||||||
|
return self.execute("GET", endpoint.format(*args), **kwargs)
|
||||||
|
|
||||||
|
def post(self, endpoint, *args, **kwargs):
|
||||||
|
return self.execute("POST", endpoint.format(*args), **kwargs)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'AnixartAPI(account={self.__account!r})'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<{self}>"
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from ..auth import AnixartAuth
|
|
||||||
from ..exceptions import AnixartInitError, AnixartAPIRequestError
|
|
||||||
from ..request_handler import AnixartRequestsHandler
|
|
||||||
|
|
||||||
_log_name = "file:%-29s -> %s" % ("<anixart.api:%-4i>", "%s")
|
|
||||||
|
|
||||||
|
|
||||||
class AnixartUserAccount:
|
|
||||||
def __init__(self, login, password, config_file="anixart_data.json", **kwargs):
|
|
||||||
self.kwargs = kwargs
|
|
||||||
log_level = logging.CRITICAL
|
|
||||||
log_format = '[%(name)-43s] %(levelname)-5s: %(message)s'
|
|
||||||
if kwargs.get("loglevel") is not None:
|
|
||||||
log_level = kwargs.get("loglevel")
|
|
||||||
if kwargs.get("logformat") is not None:
|
|
||||||
log_format = kwargs.get("logformat")
|
|
||||||
logging.basicConfig(level=log_level, format=log_format)
|
|
||||||
init_log = logging.getLogger("anixart.api.AnixUserAccount")
|
|
||||||
init_log.debug(_log_name, 23, "__init__ - INIT")
|
|
||||||
self.login = login
|
|
||||||
self.password = password
|
|
||||||
if not isinstance(login, str) or not isinstance(password, str):
|
|
||||||
raise AnixartInitError("Use normal auth data. In string.")
|
|
||||||
self.token = None
|
|
||||||
self.id = None
|
|
||||||
self.config_file = config_file
|
|
||||||
self.session = requests.Session()
|
|
||||||
init_log.debug(_log_name, 32, f"{str(self)}")
|
|
||||||
init_log.debug(_log_name, 33, "__init__() - OK")
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f'AnixartUserAccount(login="{self.login}", password="{self.password}", ' \
|
|
||||||
f'config_file="{self.config_file}", kwargs="{self.kwargs}")'
|
|
||||||
|
|
||||||
def get_login(self):
|
|
||||||
return self.login
|
|
||||||
|
|
||||||
def get_password(self):
|
|
||||||
return self.password
|
|
||||||
|
|
||||||
def get_token(self):
|
|
||||||
return self.token
|
|
||||||
|
|
||||||
def get_id(self):
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
|
|
||||||
class AnixartAPI:
|
|
||||||
|
|
||||||
def __init__(self, user: AnixartUserAccount):
|
|
||||||
init_log = logging.getLogger("anixart.api.AnixartAPI")
|
|
||||||
init_log.debug(_log_name, 56, "__init__ - INIT")
|
|
||||||
if not isinstance(user, AnixartUserAccount):
|
|
||||||
init_log.critical('Use anixart.api.AnixartUserAccount for user.')
|
|
||||||
raise AnixartInitError('Use class "AnixartUserAccount" for user.')
|
|
||||||
self.auth = AnixartAuth(user)
|
|
||||||
if user.token is None or user.id is None:
|
|
||||||
init_log.debug(_log_name, 62, "Singing in..")
|
|
||||||
self.auth.sing_in()
|
|
||||||
self.user = user
|
|
||||||
init_log.debug(_log_name, 65, "__init__ - OK.")
|
|
||||||
self.http_handler = AnixartRequestsHandler(user.token, user.session)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f'AnixAPI({self.user})'
|
|
||||||
|
|
||||||
def execute(self, http_method, endpoint, **kwargs):
|
|
||||||
http_method = http_method.upper()
|
|
||||||
if http_method == "GET":
|
|
||||||
return self.http_handler.get(endpoint, **kwargs)
|
|
||||||
elif http_method == "POST":
|
|
||||||
return self.http_handler.post(endpoint, **kwargs)
|
|
||||||
else:
|
|
||||||
raise AnixartAPIRequestError("Allow only GET and POST requests.")
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import requests
|
|
||||||
|
|
||||||
from ..auth import AnixartAuth
|
|
||||||
from ..request_handler import AnixartRequestsHandler
|
|
||||||
|
|
||||||
|
|
||||||
class AnixartUserAccount:
|
|
||||||
def __init__(self, login, password, config_file="anixart_data.json", **kwargs):
|
|
||||||
"""
|
|
||||||
Info:
|
|
||||||
Anixart login class object.
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Usage:
|
|
||||||
>>> user = AnixartUserAccount("login", "password", config_file="anixart_data.json")
|
|
||||||
>>> print(user.__login)
|
|
||||||
Availible params:
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
* login -> Your anixart nick
|
|
||||||
* password -> Your anixart password
|
|
||||||
* need_reg -> If you need new account, set True
|
|
||||||
* mail -> Real email for registration.
|
|
||||||
* config_file -> Patch to anixart login cache
|
|
||||||
:param login: Your anixart nick
|
|
||||||
:param password: Anixart password
|
|
||||||
:param need_reg: If you need new account, set True
|
|
||||||
:param email: Real email for registration
|
|
||||||
:param config_file: Patch to anixart login cache
|
|
||||||
:type login: str
|
|
||||||
:type password: str
|
|
||||||
:type need_reg: bool
|
|
||||||
:type email: str
|
|
||||||
:type config_file: str
|
|
||||||
:return: :class:`AnixUserAccount <anixart.api.AnixUserAccount>` object
|
|
||||||
"""
|
|
||||||
self.kwargs: dict = kwargs
|
|
||||||
self.login: str = login
|
|
||||||
self.password: str = password
|
|
||||||
self.config_file: str = config_file
|
|
||||||
self.token: str = None
|
|
||||||
self.id: int = None
|
|
||||||
self.session: requests.Session = requests.Session()
|
|
||||||
|
|
||||||
def __str__(self) -> str: ...
|
|
||||||
def get_login(self) -> str: ...
|
|
||||||
def get_password(self) -> str: ...
|
|
||||||
def get_token(self) -> str: ...
|
|
||||||
def get_id(self) -> int: ...
|
|
||||||
|
|
||||||
class AnixartAPI:
|
|
||||||
def __init__(self, user: AnixartUserAccount):
|
|
||||||
"""
|
|
||||||
Info:
|
|
||||||
Anixart API class object.
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
Usage:
|
|
||||||
>>> user = AnixartUserAccount("login", "password", config_file="anixart_data.json")
|
|
||||||
>>> anix = AnixartAPI(user)
|
|
||||||
:param user: :class:`AnixUserAccount <anixart.api.AnixUserAccount>` object
|
|
||||||
:return: :class:`AnixAPIRequests <anixart.api.AnixAPIRequests>` object
|
|
||||||
"""
|
|
||||||
self.auth = AnixartAuth(user)
|
|
||||||
self.user = user
|
|
||||||
self.http_handler = AnixartRequestsHandler(user.token, user.session)
|
|
||||||
def __str__(self) -> str: ...
|
|
||||||
def execute(self, http_method: str, endpoint: str) -> requests.Request: ...
|
|
||||||
@@ -1 +1 @@
|
|||||||
from .account import AnixartAccount, AnixartAccountToken
|
from .account import AnixartAccount, AnixartAccountSaved, AnixartAccountGuest
|
||||||
|
|||||||
+32
-18
@@ -3,19 +3,31 @@ from pathlib import Path
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from anixart import endpoints
|
||||||
from anixart.exceptions import AnixartInitError
|
from anixart.exceptions import AnixartInitError
|
||||||
|
|
||||||
|
|
||||||
class AnixartAccount:
|
class AnixartAccount:
|
||||||
|
guest = False
|
||||||
def __init__(self, username: str, password: str):
|
def __init__(self, username: str, password: str):
|
||||||
self._username = username
|
self._username = username
|
||||||
self._password = password
|
self._password = password
|
||||||
if not isinstance(username, str) or not isinstance(password, str):
|
if not isinstance(username, str) or not isinstance(password, str):
|
||||||
raise AnixartInitError("Auth data must be strings.")
|
raise AnixartInitError("Auth data must be strings.")
|
||||||
|
|
||||||
|
self._id = None
|
||||||
self._token = None
|
self._token = None
|
||||||
self._session = requests.Session()
|
self._session = requests.Session()
|
||||||
|
|
||||||
|
self._api = None
|
||||||
|
|
||||||
|
def _set_api(self, api):
|
||||||
|
self._api = api
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self._id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def username(self):
|
def username(self):
|
||||||
return self._username
|
return self._username
|
||||||
@@ -28,14 +40,14 @@ class AnixartAccount:
|
|||||||
def token(self):
|
def token(self):
|
||||||
return self._token
|
return self._token
|
||||||
|
|
||||||
def to_file(self, filename: str | Path):
|
def to_file(self, filename: str | Path) -> "AnixartAccountSaved":
|
||||||
"""Save the account information to a file."""
|
"""Save the account information to a file."""
|
||||||
acc = AnixartAccountSaved.from_account(filename, self)
|
acc = AnixartAccountSaved.from_account(filename, self)
|
||||||
acc.save()
|
acc.save()
|
||||||
return acc
|
return acc
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_file(cls, filename: str | Path):
|
def from_file(cls, filename: str | Path) -> "AnixartAccountSaved":
|
||||||
"""Load the account information from a file."""
|
"""Load the account information from a file."""
|
||||||
acc = AnixartAccountSaved(filename)
|
acc = AnixartAccountSaved(filename)
|
||||||
acc.load()
|
acc.load()
|
||||||
@@ -43,7 +55,12 @@ class AnixartAccount:
|
|||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
"""Login to Anixart and return the token."""
|
"""Login to Anixart and return the token."""
|
||||||
# TODO: Implement login logic here
|
payload = {"login": self.username, "password": self._password}
|
||||||
|
res = self._api.post(endpoints.SING_IN, payload)
|
||||||
|
uid = res["profile"]["id"]
|
||||||
|
token = res["profileToken"]["token"]
|
||||||
|
self._id = uid
|
||||||
|
self._token = token
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'AnixartAccount(login={self._username!r}, password={"*" * len(self._password)!r})'
|
return f'AnixartAccount(login={self._username!r}, password={"*" * len(self._password)!r})'
|
||||||
@@ -60,8 +77,7 @@ class AnixartAccountSaved(AnixartAccount):
|
|||||||
def save(self):
|
def save(self):
|
||||||
"""Save the account information to a file."""
|
"""Save the account information to a file."""
|
||||||
data = {
|
data = {
|
||||||
"username": self._username,
|
"id": self._id,
|
||||||
"password": self._password,
|
|
||||||
"token": self._token
|
"token": self._token
|
||||||
}
|
}
|
||||||
with open(self._file, 'w') as f:
|
with open(self._file, 'w') as f:
|
||||||
@@ -73,11 +89,11 @@ class AnixartAccountSaved(AnixartAccount):
|
|||||||
raise AnixartInitError(f"Account file {self._file} does not exist.")
|
raise AnixartInitError(f"Account file {self._file} does not exist.")
|
||||||
with open(self._file, 'r') as f:
|
with open(self._file, 'r') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
|
self._id = data.get("id")
|
||||||
self._username = data.get("username")
|
self._username = data.get("username")
|
||||||
self._password = data.get("password")
|
|
||||||
self._token = data.get("token")
|
self._token = data.get("token")
|
||||||
if not self._username or not self._password:
|
if not self._id or not self._token:
|
||||||
raise AnixartInitError("Login and password must be provided in the account file.")
|
raise AnixartInitError("id and token must be provided in the account file.")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_account(cls, account_file: str | Path, account: AnixartAccount):
|
def from_account(cls, account_file: str | Path, account: AnixartAccount):
|
||||||
@@ -97,16 +113,14 @@ class AnixartAccountSaved(AnixartAccount):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'AnixartAccountSaved(account_file={self._file!r}")'
|
return f'AnixartAccountSaved(account_file={self._file!r}")'
|
||||||
|
|
||||||
class AnixartAccountToken(AnixartAccount):
|
class AnixartAccountGuest(AnixartAccount):
|
||||||
|
guest = True
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("", "")
|
||||||
|
self._token = ""
|
||||||
|
|
||||||
def __init__(self, token):
|
def login(self): ...
|
||||||
super().__init__("mradx", "") # Пасхалка)
|
|
||||||
self._token = token
|
|
||||||
|
|
||||||
def login(self):
|
|
||||||
"""Login to Anixart and return information about the tokens."""
|
|
||||||
# TODO: Implement login logic here
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'AnixartAccountToken(token={self._token!r}")'
|
return f'AnixartAccountGuest(token={self._token!r}")'
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
class AnixartAuthErrors(IntEnum):
|
||||||
|
INCORRECT_LOGIN = 2
|
||||||
|
INCORRECT_PASSWORD = 3
|
||||||
|
|
||||||
|
|
||||||
|
def errors_handler(error):
|
||||||
|
"""Handle errors and return a JSON response."""
|
||||||
|
pass
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
from enum import IntEnum
|
|
||||||
|
|
||||||
|
|
||||||
class AnixartAuthError(IntEnum):
|
|
||||||
""" Error codes for AnixartApi authentication."""
|
|
||||||
INCORRECT_LOGIN = 1
|
|
||||||
INCORRECT_PASSWORD = 2
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import json
|
|
||||||
import logging
|
|
||||||
import os.path
|
|
||||||
|
|
||||||
from .endpoints import SING_IN, CHANGE_PASSWORD, PROFILE
|
|
||||||
from .exceptions import AnixartAuthError
|
|
||||||
from .request_handler import AnixartRequestsHandler
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_response(data):
|
|
||||||
ready = data.json()
|
|
||||||
ready.update({"status_code": data.status_code})
|
|
||||||
code = ready['code']
|
|
||||||
if code != 0:
|
|
||||||
if code == 2:
|
|
||||||
raise AnixartAuthError("Incorrect login.")
|
|
||||||
if code == 3:
|
|
||||||
raise AnixartAuthError("Incorrect password.")
|
|
||||||
print("\n\n" + data.text + "\n\n")
|
|
||||||
raise AnixartAuthError("Unknown auth error.")
|
|
||||||
return ready
|
|
||||||
|
|
||||||
|
|
||||||
class AnixartAuth(AnixartRequestsHandler):
|
|
||||||
|
|
||||||
def __init__(self, user):
|
|
||||||
super(AnixartAuth, self).__init__(None, user.session, "anixart.auth.AnixAuth")
|
|
||||||
self.user = user
|
|
||||||
self.filename = user.config_file
|
|
||||||
|
|
||||||
def _save_config(self, data):
|
|
||||||
with open(self.filename, "w") as f:
|
|
||||||
json.dump(data, f)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _open_config(self):
|
|
||||||
if os.path.isfile(self.filename):
|
|
||||||
with open(self.filename, "r") as read_file:
|
|
||||||
data = json.load(read_file)
|
|
||||||
return data
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def sing_in(self):
|
|
||||||
config = self._open_config()
|
|
||||||
if config:
|
|
||||||
uid = config.get("id")
|
|
||||||
token = config.get("token")
|
|
||||||
if not self.get(PROFILE.format(uid), {"token": token}).json().get("is_my_profile") or \
|
|
||||||
self.user.__login != config.get("login"):
|
|
||||||
logging.getLogger("anixart.api.AnixAPI").debug("Invalid config file. Re login.")
|
|
||||||
else:
|
|
||||||
self.user.id = uid
|
|
||||||
self.user.__token = token
|
|
||||||
return config
|
|
||||||
payload = {"login": self.user.__login, "password": self.user.__password}
|
|
||||||
res = self.post(SING_IN, payload)
|
|
||||||
ready = _parse_response(res)
|
|
||||||
uid = ready["profile"]["id"]
|
|
||||||
token = ready["profileToken"]["token"]
|
|
||||||
self.user.id = uid
|
|
||||||
self.user.__token = token
|
|
||||||
self._save_config({"id": uid, "token": token, "login": self.user.__login})
|
|
||||||
return ready
|
|
||||||
|
|
||||||
def change_password(self, old, new):
|
|
||||||
return self.get(CHANGE_PASSWORD, {"current": old, "new": new, "token": self.user.token})
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProfileToken:
|
||||||
|
id: int
|
||||||
|
token: str
|
||||||
@@ -20,8 +20,8 @@ _AUTH_SING_IN_WITH_VK = "/auth/vk" # {vkAccessToken}
|
|||||||
# SETTINGS_RELEASE_TYPE
|
# SETTINGS_RELEASE_TYPE
|
||||||
|
|
||||||
# GET
|
# GET
|
||||||
PROFILE = "/profile/{}" # + profile id
|
PROFILE = "/profile/{}" # + profile id (Токен нужен только что бы был is_my_profile)
|
||||||
PROFILE_NICK_HISTORY = "/profile/login/history/all/{}/{}" # profile id / page
|
PROFILE_NICK_HISTORY = "/profile/login/history/all/{}/{}" # profile id / page (Токен не нужен)
|
||||||
|
|
||||||
PROFILE_BLACKLIST = "/profile/blocklist/all/{}" # page
|
PROFILE_BLACKLIST = "/profile/blocklist/all/{}" # page
|
||||||
PROFILE_BLACKLIST_ADD = "/profile/blocklist/add/{}" # profile id
|
PROFILE_BLACKLIST_ADD = "/profile/blocklist/add/{}" # profile id
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
|
||||||
|
class AnixartApiErrors(IntEnum):
|
||||||
|
""" Error codes for AnixartApi authentication."""
|
||||||
|
INCORRECT_LOGIN = 2
|
||||||
|
INCORRECT_PASSWORD = 3
|
||||||
|
|
||||||
class AnixartComment(IntEnum):
|
class AnixartComment(IntEnum):
|
||||||
DISLIKE = 1
|
DISLIKE = 1
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ class AnixartBasError(Exception): ...
|
|||||||
class AnixartInitError(AnixartBasError, TypeError): ...
|
class AnixartInitError(AnixartBasError, TypeError): ...
|
||||||
|
|
||||||
# API errors
|
# API errors
|
||||||
class AnixartAPIError(AnixartBasError): ...
|
class AnixartAPIError(AnixartBasError):
|
||||||
|
message = "unknown error"
|
||||||
|
code = 0
|
||||||
|
|
||||||
class AnixartAuthError(AnixartAPIError): ...
|
class AnixartAuthError(AnixartAPIError): ...
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
from .objects import Profile, ProfileVote, ProfileRoles, ProfileHistory, ProfileFriendsPreview
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
class AnixartProfileErrors(IntEnum):
|
||||||
|
""" Error codes for AnixartApi authentication."""
|
||||||
|
PROFILE_NOT_FOUND = 2
|
||||||
|
|
||||||
|
|
||||||
|
def errors_handler(error):
|
||||||
|
"""Handle errors and return a JSON response."""
|
||||||
|
pass
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProfileHistory:
|
||||||
|
# TODO: Надо ещё изучить релизы
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProfileVote:
|
||||||
|
# TODO: Надо ещё изучить релизы
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProfileRoles:
|
||||||
|
# TODO: Надо ещё изучить роли (У меня их нет(()
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProfileFriendsPreview:
|
||||||
|
id: int
|
||||||
|
avatar: str
|
||||||
|
login: str
|
||||||
|
friend_count: int
|
||||||
|
friend_status: int
|
||||||
|
is_sponsor: bool
|
||||||
|
is_online: bool
|
||||||
|
is_verified: bool
|
||||||
|
is_social: bool
|
||||||
|
badge_id: None
|
||||||
|
badge_name: None
|
||||||
|
badge_type: None
|
||||||
|
badge_url: None
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Profile:
|
||||||
|
id: int
|
||||||
|
login: str
|
||||||
|
avatar: str
|
||||||
|
status: str
|
||||||
|
rating_score: int
|
||||||
|
history: list[ProfileHistory]
|
||||||
|
votes: list[ProfileVote]
|
||||||
|
roles: list[ProfileRoles]
|
||||||
|
friends_preview: list[ProfileFriendsPreview]
|
||||||
|
collections_preview: list # TODO: Коллекции изучить
|
||||||
|
release_comments_preview: list # Тут вообще не понял
|
||||||
|
comments_preview: list # Тут типа превью к коментам
|
||||||
|
release_videos_preview: list # Тут вообще не понял
|
||||||
|
watch_dynamics: list # Тут вообще не понял
|
||||||
|
last_activity_time: int
|
||||||
|
register_date: int
|
||||||
|
vk_page: str
|
||||||
|
tg_page: str
|
||||||
|
inst_page: str
|
||||||
|
tt_page: str
|
||||||
|
discord_page: str
|
||||||
|
ban_expires: int
|
||||||
|
ban_reason: str
|
||||||
|
privilege_level: int
|
||||||
|
watching_count: int
|
||||||
|
plan_count: int
|
||||||
|
completed_count: int
|
||||||
|
hold_on_count: int
|
||||||
|
dropped_count: int
|
||||||
|
favorite_count: int
|
||||||
|
comment_count: int
|
||||||
|
collection_count: int
|
||||||
|
video_count: int
|
||||||
|
friend_count: int
|
||||||
|
subscription_count: int
|
||||||
|
watched_episode_count: int
|
||||||
|
watched_time: int
|
||||||
|
sponsorshipExpires: int
|
||||||
|
is_private: bool
|
||||||
|
is_sponsor: bool
|
||||||
|
is_banned: bool
|
||||||
|
is_perm_banned: bool
|
||||||
|
is_bookmarks_transferred: bool
|
||||||
|
is_sponsor_transferred: bool
|
||||||
|
is_vk_bound: bool
|
||||||
|
is_google_bound: bool
|
||||||
|
is_release_type_notifications_enabled: bool
|
||||||
|
is_episode_notifications_enabled: bool
|
||||||
|
is_first_episode_notification_enabled: bool
|
||||||
|
is_related_release_notifications_enabled: bool
|
||||||
|
is_report_process_notifications_enabled: bool
|
||||||
|
is_comment_notifications_enabled: bool
|
||||||
|
is_my_collection_comment_notifications_enabled: bool
|
||||||
|
is_my_article_comment_notifications_enabled: bool
|
||||||
|
is_verified: bool
|
||||||
|
is_blocked: bool
|
||||||
|
is_me_blocked: bool
|
||||||
|
is_stats_hidden: bool
|
||||||
|
is_counts_hidden: bool
|
||||||
|
is_social_hidden: bool
|
||||||
|
is_friend_requests_disallowed: bool
|
||||||
|
is_online: bool
|
||||||
|
badge: None
|
||||||
|
friend_status: None
|
||||||
|
|
||||||
|
is_my_profile: bool = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_response(cls, response: dict) -> "Profile":
|
||||||
|
profile = {"is_my_profile": response['is_my_profile'], **response['profile']}
|
||||||
|
return cls(**profile)
|
||||||
|
|
||||||
|
class _login:
|
||||||
|
pos_id: int
|
||||||
|
id: int
|
||||||
|
newLogin: str
|
||||||
|
timestamp: int
|
||||||
|
|
||||||
|
class ProfileLoginsHistory:
|
||||||
|
content: list[_login]
|
||||||
|
total_count: int
|
||||||
|
# total_page_count: int
|
||||||
|
# current_page: int
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from .__meta__ import __version__, __build__
|
|
||||||
from .endpoints import API_URL
|
|
||||||
from .exceptions import AnixartAPIRequestError, AnixartAPIError
|
|
||||||
_log_name = "file:%-28s -> %s" % ("<Anixart.request_handler:%-3i>", "%s")
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_res_code(res, payload, http_method, http_headers):
|
|
||||||
json = res.json()
|
|
||||||
error = json.get("error")
|
|
||||||
code = json.get("code")
|
|
||||||
if res.status_code >= 400:
|
|
||||||
raise AnixartAPIRequestError(f"\n\nAnixartPyAPIWrapper: ERROR\n"
|
|
||||||
f"Send this info to author: https://t.me/SantaSpeen\n"
|
|
||||||
f"URL: {http_method} {res.url}\n"
|
|
||||||
f"Status code: {res.status_code}\n"
|
|
||||||
f"Res headers: {res.headers}\n"
|
|
||||||
f"Req headers: {http_headers}\n"
|
|
||||||
f"Server res: {json}\n"
|
|
||||||
f"Client req: {payload}\n")
|
|
||||||
if error:
|
|
||||||
raise AnixartAPIRequestError(f"Internal server error: {error}; Payload: {payload}")
|
|
||||||
if code:
|
|
||||||
if code == 0:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
raise AnixartAPIError(f"AnixartAPI send error code: {code}; Json: {json}")
|
|
||||||
|
|
||||||
|
|
||||||
class AnixartRequestsHandler:
|
|
||||||
|
|
||||||
def __init__(self, token: str = None, session: requests.Session = None,
|
|
||||||
_log_class="Anixart.request_handler.AnixartRequestsHandler"):
|
|
||||||
self.__log = logging.getLogger(_log_class)
|
|
||||||
self.__log.debug(_log_name, 44, f"__init__ - INIT from {self}")
|
|
||||||
if session:
|
|
||||||
self.__session = session
|
|
||||||
else:
|
|
||||||
self.__log.debug(_log_name, 48, "Create new session.")
|
|
||||||
self.__session = requests.Session()
|
|
||||||
self.__session.headers = {
|
|
||||||
'User-Agent': f'AnixartPyAPIWrapper/{__version__}-{__build__}'
|
|
||||||
f' (Linux; Android 12; SantaSpeen AnixartPyAPIWrapper Build/{__build__})'}
|
|
||||||
self.__token = token
|
|
||||||
|
|
||||||
def post(self, method: str, payload: dict = None, is_json: bool = False, **kwargs):
|
|
||||||
if payload is None:
|
|
||||||
payload = {}
|
|
||||||
url = API_URL + method
|
|
||||||
if payload.get("token") is None:
|
|
||||||
if self.__token is not None:
|
|
||||||
payload.update({"token": self.__token})
|
|
||||||
url += "?token=" + self.__token
|
|
||||||
else:
|
|
||||||
token = kwargs.get("token")
|
|
||||||
if token is not None:
|
|
||||||
payload.update({"token": token})
|
|
||||||
url += "?token=" + token
|
|
||||||
kwargs = {"url": url}
|
|
||||||
if is_json:
|
|
||||||
self.__session.headers.update({"Content-Type": "application/json; charset=UTF-8"})
|
|
||||||
self.__session.headers.update({"Content-Length": str(len(str(payload)))})
|
|
||||||
kwargs.update({"json": payload})
|
|
||||||
else:
|
|
||||||
kwargs.update({"data": payload})
|
|
||||||
self.__log.debug(_log_name, 79, f"{'json' if is_json else ''} POST {method}; {payload}")
|
|
||||||
res = self.__session.post(**kwargs)
|
|
||||||
_parse_res_code(res, payload, "POST", self.__session.headers)
|
|
||||||
self.__session.headers["Content-Type"] = ""
|
|
||||||
self.__session.headers["Content-Length"] = ""
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get(self, method: str, payload: dict = None, **kwargs):
|
|
||||||
if payload is None:
|
|
||||||
payload = {}
|
|
||||||
if payload.get("token") is None:
|
|
||||||
if self.__token is not None:
|
|
||||||
payload.update({"token": self.__token})
|
|
||||||
else:
|
|
||||||
token = kwargs.get("token")
|
|
||||||
if token is not None:
|
|
||||||
payload.update({"token": token})
|
|
||||||
self.__log.debug(_log_name, 101, f"GET {method}; {payload}")
|
|
||||||
res = self.__session.get(API_URL + method, params=payload)
|
|
||||||
_parse_res_code(res, payload, "GET", self.__session.headers)
|
|
||||||
return res
|
|
||||||
+26
-4
@@ -2,8 +2,31 @@
|
|||||||
|
|
||||||
## Anixart API Wrapper
|
## Anixart API Wrapper
|
||||||
|
|
||||||
|
### 02.04.2025
|
||||||
|
#### Version: 0.3.0.1
|
||||||
|
|
||||||
|
##### Changes:
|
||||||
|
|
||||||
|
* Глобальная переборка всего проекта по `SDK-like` принципу
|
||||||
|
* Переработан блок `API`:
|
||||||
|
* `AnixartAPI` - Теперь является основным классом для работы с API.
|
||||||
|
* Добавлен блок `auth`:
|
||||||
|
* Работа с аккаунтом:
|
||||||
|
* `AnixartAccount` - Основной класс взаимодействия с аккаунтом (логин, пароль)
|
||||||
|
* `AnixartAccountSaved` - Работа с сохранённым аккаунтом
|
||||||
|
* `AnixartAccountGuest` - Авторизация без логина и пароля (Как гость)
|
||||||
|
* Добавлены ошибки
|
||||||
|
* `INCORRECT_LOGIN` - Неверный логин
|
||||||
|
* `INCORRECT_PASSWORD` - Неверный пароль
|
||||||
|
* Добавлен блок `profile`:
|
||||||
|
* Добавлены объекты для работы с `/profile/`:
|
||||||
|
* `Profile` - ДатаКласс для работы с профилем (`endpoints.PROFILE`) __не закончены подклассы__
|
||||||
|
* `ProfileLoginsHistory` - ДатаКласс для работы с историей логинов (`endpoints.PROFILE_NICK_HISTORY`) __не закончен__
|
||||||
|
* Добавлены ошибки
|
||||||
|
* `PROFILE_NOT_FOUND` - Профиль не найден (или невалидный)
|
||||||
|
|
||||||
### 28.09.2022
|
### 28.09.2022
|
||||||
#### Version: 0.2.1, Build: 3
|
#### Version: 0.2.1
|
||||||
|
|
||||||
##### Changes:
|
##### Changes:
|
||||||
|
|
||||||
@@ -29,8 +52,7 @@ _Из прошлых версий_
|
|||||||
- Выявить и удалить не используемые
|
- Выявить и удалить не используемые
|
||||||
|
|
||||||
### 27.09.2022
|
### 27.09.2022
|
||||||
[//]: # ( Да, я не билдил, это не ошибка )
|
#### Version: 0.1.0
|
||||||
#### Version: 0.1.0, Build: 1
|
|
||||||
|
|
||||||
##### Changes:
|
##### Changes:
|
||||||
|
|
||||||
@@ -46,7 +68,7 @@ _Из прошлых версий_
|
|||||||
|
|
||||||
|
|
||||||
### 27.09.2022
|
### 27.09.2022
|
||||||
#### Version: 0.0.1, Build: 1
|
#### Version: 0.0.1
|
||||||
|
|
||||||
##### Changes:
|
##### Changes:
|
||||||
|
|
||||||
|
|||||||
+14
-5
@@ -1,9 +1,18 @@
|
|||||||
from anixart import AnixartAPI, AnixartUserAccount, PROFILE
|
from anixart import AnixartAPI, AnixartAccount
|
||||||
|
from anixart import endpoints
|
||||||
|
from anixart.exceptions import AnixartAPIRequestError
|
||||||
|
from anixart.profile import Profile
|
||||||
|
|
||||||
user = AnixartUserAccount("SantaSpeen", "I_H@ve_Very_Secret_P@ssw0rd!")
|
anix = AnixartAPI() # По умолчанию используется гость
|
||||||
anix = AnixartAPI(user)
|
|
||||||
|
|
||||||
|
# acc = AnixartAccount("SantaSpeen", "I_H@ve_Very_Secret_P@ssw0rd!")
|
||||||
|
# # id у аккаунта появляется только после
|
||||||
|
# anix.use_account(acc)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
me = anix.execute("GET", PROFILE.format(user.id))
|
try:
|
||||||
print(me.json())
|
raw = anix.get(endpoints.PROFILE, 1)
|
||||||
|
print(Profile.from_response(raw))
|
||||||
|
except AnixartAPIRequestError as e:
|
||||||
|
print(e.message)
|
||||||
|
print(e.code)
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "anixart"
|
name = "anixart"
|
||||||
version = "0.2.2"
|
version = "0.3.0.1"
|
||||||
description = "Wrapper for using the Anixart API."
|
description = "Wrapper for using the Anixart API."
|
||||||
authors = [
|
authors = [
|
||||||
{name = "SantaSpeen",email = "santaspeen@gmail.com"}
|
{name = "SantaSpeen",email = "santaspeen@gmail.com"}
|
||||||
@@ -16,7 +16,7 @@ repository = "https://github.com/SantaSpeen/anixart"
|
|||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 3 - Alpha",
|
"Development Status :: 2 - Pre-Alpha",
|
||||||
"Topic :: Software Development :: Libraries :: Python Modules"
|
"Topic :: Software Development :: Libraries :: Python Modules"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user