diff --git a/.gitignore b/.gitignore index 301830c..067d9d6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ build enkanetwork.py.egg-info dist test/*.test.py -.pytest_cache \ No newline at end of file +.pytest_cache +.idea \ No newline at end of file diff --git a/enkanetwork/__init__.py b/enkanetwork/__init__.py index 4f74eab..f1e5a43 100644 --- a/enkanetwork/__init__.py +++ b/enkanetwork/__init__.py @@ -1,9 +1,10 @@ -from .client import * # noqa: F403 -from .exception import * # noqa: F403 -from .model import * # noqa: F403 -from .utils import * # noqa: F403 -from .info import * # noqa: F403 -from .cache import * # noqa: F403 +from .client import * +from .exception import * +from .model import * +from .utils import * +from .info import * +from .cache import * +from .enum import * -__VERSION__ = VERSION # noqa: F405 -__AUTHOR__ = AUTHOR # noqa: F405 +__VERSION__ = VERSION +__AUTHOR__ = AUTHOR diff --git a/enkanetwork/assets.py b/enkanetwork/assets.py index 594e0e8..f89bfc1 100644 --- a/enkanetwork/assets.py +++ b/enkanetwork/assets.py @@ -2,13 +2,14 @@ import os import logging -from typing import Dict, Union, List -from io import TextIOWrapper +from typing import Dict, List, TextIO from .enum import Language from .model import assets from .utils import create_ui_path +from typing import Union + PATH = os.path.dirname(os.path.abspath(__file__)) LOGGER = logging.getLogger(__name__) @@ -19,7 +20,7 @@ class Assets: HASH_MAP: Dict[str, dict] = {} LANGS: Language = Language.EN - def __init__(self, lang: Language = Language.EN) -> None: + def __init__(self, lang: Union[str, Language] = Language.EN) -> None: # Set language self._set_language(lang) self.reload_assets() @@ -180,7 +181,9 @@ def __load_assets_data(cls) -> None: FILE_DATA = os.listdir(_PATH) for FILENAME in FILE_DATA: LOGGER.debug(f"Loading data file {FILENAME}...") + cls.DATA[FILENAME.split(".")[0]] = json.load(cls.__load(os.path.join(_PATH, FILENAME))) # noqa: E501 - def __load(path: str) -> TextIOWrapper: + @staticmethod + def __load(path: str) -> TextIO: return open(path, "r", encoding="utf-8") diff --git a/enkanetwork/cache.py b/enkanetwork/cache.py index f24287c..d1f5bf7 100644 --- a/enkanetwork/cache.py +++ b/enkanetwork/cache.py @@ -2,6 +2,7 @@ from cachetools import TTLCache +__all__ = ('Cache',) class Cache: def __init__(self, maxsize, ttl): diff --git a/enkanetwork/client.py b/enkanetwork/client.py index 2a1e10b..94ff926 100644 --- a/enkanetwork/client.py +++ b/enkanetwork/client.py @@ -1,22 +1,25 @@ -import logging -import os -import json - -from typing import Union +from __future__ import annotations +import logging +from .http import HTTPClient from .model import EnkaNetworkResponse -from .exception import VaildateUIDError, UIDNotFounded from .assets import Assets -from .utils import create_path, validate_uid, request from .enum import Language from .cache import Cache +from typing import Union, Optional, Type, TYPE_CHECKING + +if TYPE_CHECKING: + from typing_extensions import Self + from types import TracebackType + +__all__ = ("EnkaNetworkAPI",) class EnkaNetworkAPI: + LOGGER = logging.getLogger(__name__) - RAWDATA = "https://raw.githubusercontent.com/mrwan200/enkanetwork.py-data/{PATH}" # noqa: E501 - def __init__(self, lang: str = "en", debug: bool = False, key: str = "", cache: bool = True, agent: str = "") -> None: # noqa: E501 + def __init__(self, lang: str = "en", *, debug: bool = False, key: str = "", cache: bool = True, agent: str = "") -> None: # noqa: E501 # Logging logging.basicConfig() logging.getLogger("enkanetwork").setLevel(logging.DEBUG if debug else logging.ERROR) # noqa: E501 @@ -28,11 +31,25 @@ def __init__(self, lang: str = "en", debug: bool = False, key: str = "", cache: self._enable_cache = cache self.cache = Cache(1024, 60 * 3) - # Custom User-Agent - self.__agent = agent + # http client + self.__http = HTTPClient(key=key, agent=agent) + self._closed = False - # Key - self.__key = key + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> None: + self._close = True + if self._close: + await self.__http.close() + + def is_closed(self) -> bool: + return self._closed @property def lang(self) -> Language: @@ -50,60 +67,35 @@ async def set_language(self, lang: Language) -> None: async def fetch_user(self, uid: Union[str, int]) -> EnkaNetworkResponse: self.LOGGER.debug(f"Validating with UID {uid}...") - if not validate_uid(str(uid)): - raise VaildateUIDError("Validate UID failed. Please check your UID.") # noqa: E501 + + user = await self.__http.fetch_user(uid) + + data = user["content"] self.LOGGER.debug(f"Fetching user with UID {uid}...") if self._enable_cache: - self.LOGGER.warn("Getting data from cache...") - data = await self.cache.get(uid) + self.LOGGER.debug("Caching data...") + await self.cache.set(uid, data) if data is not None: # Return data self.LOGGER.debug("Parsing data...") return EnkaNetworkResponse.parse_obj(data) - headers = {} - if self.__agent != "": - headers["User-Agent"] = self.__agent - - resp = await request(url=create_path(f"u/{uid}/__data.json" + ("?key={key}" if self.__key else "")), headers=headers) # noqa: E501 - - # Check if status code is not 200 (Ex. 500) - if resp["status"] != 200: - raise UIDNotFounded(f"UID {uid} not found.") - - # Parse JSON data - data = resp["content"] - - if not data: - raise UIDNotFounded(f"UID {uid} not found.") - - self.LOGGER.debug("Got data from EnkaNetwork.") - self.LOGGER.debug(f"Raw data: {data}") - - if self._enable_cache: - self.LOGGER.debug("Caching data...") - await self.cache.set(uid, data) - # Return data self.LOGGER.debug("Parsing data...") return EnkaNetworkResponse.parse_obj(data) async def update_assets(self) -> None: + print("Updating assets...") self.LOGGER.debug("Downloading new content...") - _PATH = Assets._get_path_assets() - for folder in _PATH: - for filename in os.listdir(_PATH[folder]): - self.LOGGER.debug(f"Downloading {folder} file {filename}...") - _json = await request( - url=self.RAWDATA.format(PATH=f"master/exports/{folder}/{filename}") # noqa: E501 - ) - - self.LOGGER.debug(f"Writing {folder} file {filename}...") - with open(os.path.join(_PATH[folder], filename), "w", encoding="utf-8") as f: # noqa: E501 - json.dump(_json["content"], f, ensure_ascii=False, indent=4) # noqa: E501 + + # get path + path = Assets._get_path_assets() + + # update assets + await self.__http.update_asset(path) # Reload config self.assets.reload_assets() diff --git a/enkanetwork/enum.py b/enkanetwork/enum.py index eeb9e4f..0e3b2e4 100644 --- a/enkanetwork/enum.py +++ b/enkanetwork/enum.py @@ -1,5 +1,12 @@ from enum import Enum +__all__ = ( + 'Language', + 'EquipmentsType', + 'DigitType', + 'ElementType', + 'EquipType' +) class Language(str, Enum): EN = "en" diff --git a/enkanetwork/exception.py b/enkanetwork/exception.py index 9c451d3..d16da1c 100644 --- a/enkanetwork/exception.py +++ b/enkanetwork/exception.py @@ -1,6 +1,19 @@ class VaildateUIDError(Exception): + """ Raised when the UID is not valid. """ pass - class UIDNotFounded(Exception): + """ Raised when the UID is not found. """ + pass + +class HTTPException(Exception): + """ Exception that's raised when an HTTP request operation fails. """ + pass + +class Forbidden(HTTPException): + """ Exception that's raised for when status code 403 occurs.""" + pass + +class EnkaServerError(HTTPException): + """ Exception that's raised for when status code 500 occurs.""" pass diff --git a/enkanetwork/http.py b/enkanetwork/http.py new file mode 100644 index 0000000..258d5c4 --- /dev/null +++ b/enkanetwork/http.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +import os +import json +import asyncio +import aiohttp +import logging + +from typing import ( + Any, + ClassVar, + Optional, + TypeVar, + Coroutine, + NoReturn, + Dict, + Union, + TYPE_CHECKING +) +from . import utils +from .utils import MISSING, RETRY_MAX +from .exception import ( + VaildateUIDError, + UIDNotFounded, + HTTPException, + Forbidden, + EnkaServerError +) + +if TYPE_CHECKING: + T = TypeVar('T') + Response = Coroutine[Any, Any, T] + from .types.enkanetwork import EnkaNetwork as EnkaNetworkPayload + +class Route: + + BASE_URL: ClassVar[str] = "https://enka.network{PATH}" + RAW_DATA_URL = "https://raw.githubusercontent.com/mrwan200/enkanetwork.py-data/{PATH}" + + def __init__( + self, + method: str, + path: str, + endpoint: str = 'enka', + uid: Optional[str] = None, + ): + self.method = method + self.uid = uid + self.url = '' + if endpoint == 'enka': + self.url: str = self.BASE_URL.format(PATH=path) + else: + self.url: str = self.RAW_DATA_URL.format(PATH=path) + +class HTTPClient: + + LOGGER = logging.getLogger(__name__) + + def __init__(self, *, key: str = '', agent: str = '') -> None: + self.__session: aiohttp.ClientSession = MISSING + self.__headers: Dict = {} + self.__agent = agent + self.__key = key + + async def close(self) -> None: + if self.__session is not MISSING: + await self.__session.close() + self.__session = MISSING + self.LOGGER.debug('Session closed') + else: + self.LOGGER.debug('Session already closed') + + async def request(self, route: Route, **kwargs: Any) -> Any: + method = route.method + url = route.url + uid = route.uid + + self.__headers.clear() + if not self.__agent != '': + self.__headers['User-Agent'] = self.__agent + + kwargs['headers'] = {**utils.get_default_header(), **self.__headers} + + response: Optional[aiohttp.ClientResponse] = None + data: Optional[Union[Dict[str, Any]]] = None + + if self.__session is MISSING: + self.__session = aiohttp.ClientSession() + + for tries in range(RETRY_MAX): + try: + async with self.__session.request(method, url, **kwargs) as response: + if 300 > response.status >= 200: + data = await utils.to_data(response) + + if not data['content'] or response.status != 200: + raise UIDNotFounded(f"UID {uid} not found.") + + self.LOGGER.debug('%s %s has received %s', method, url, data) + return data + + # we are being rate limited + # if response.status == 429: + # Banned by Cloudflare more than likely. + + if response.status >= 400: + self.LOGGER.warning(f"Failure to fetch {url} ({response.status}) Retry {tries} / {RETRY_MAX}") + if tries > RETRY_MAX: + raise HTTPException(f"Failed to download {url}") + await asyncio.sleep(1 + tries * 2) # 1 + tries * 2 + continue + + if response.status == 403: + raise Forbidden("Forbidden 403") # TODO: คิดไม่ออกจะพิมพ์อะไร + elif response.status >= 500: + raise EnkaServerError("Server error") + else: + raise HTTPException("Unknown error") + + except OSError as e: + # Connection reset by peer + if tries < 4 and e.errno in (54, 10054): + await asyncio.sleep(1 + tries * 2) + continue + raise + + if response is not None: + # We've run out of retries, raise. + if response.status >= 500: + raise EnkaServerError("Server error") + + raise HTTPException("Unknown error") + + raise RuntimeError('Unreachable code in HTTP handling') + + def fetch_user(self, uid: Union[str, int]) -> Response[EnkaNetworkPayload]: + if not utils.validate_uid(str(uid)): + raise VaildateUIDError("Validate UID failed. Please check your UID.") + url = f'/u/{uid}/__data.json' + ("?key={key}" if self.__key else "") + return self.request(Route('GET', url, 'enka', uid)) + + async def update_asset(self, path: dict) -> NoReturn: + + self.LOGGER.debug("Downloading new content...") + + for folder in path: + for filename in os.listdir(path[folder]): + self.LOGGER.debug(f"Downloading {folder} file {filename}...") + + # get new assets + url = f"master/exports/{folder}/{filename}" + data = await self.request(Route('GET', url, 'assets')) + + self.LOGGER.debug(f"Writing {folder} file {filename}...") + + # dumps to json file + with open(os.path.join(path[folder], filename), "w", encoding="utf-8") as f: + json.dump(data["content"], f, ensure_ascii=False, indent=4) diff --git a/enkanetwork/model/__init__.py b/enkanetwork/model/__init__.py index 2927633..c0099e8 100644 --- a/enkanetwork/model/__init__.py +++ b/enkanetwork/model/__init__.py @@ -4,6 +4,7 @@ from .players import PlayerInfo from .character import CharacterInfo +__all__ = ("EnkaNetworkResponse",) class EnkaNetworkResponse(BaseModel): player: PlayerInfo = Field(None, alias="playerInfo") diff --git a/enkanetwork/types/__init__.py b/enkanetwork/types/__init__.py new file mode 100644 index 0000000..58bdb6d --- /dev/null +++ b/enkanetwork/types/__init__.py @@ -0,0 +1,7 @@ +""" +enkanetwork.types +~~~~~~~~~~~~~~ +Typings for the Enka Network API +:copyright: (c) 2022-present M-307 +:license: MIT, see LICENSE for more details. +""" \ No newline at end of file diff --git a/enkanetwork/types/enkanetwork.py b/enkanetwork/types/enkanetwork.py new file mode 100644 index 0000000..c41ed02 --- /dev/null +++ b/enkanetwork/types/enkanetwork.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from typing import Any, Dict, List, TypedDict, Union + +__all__ = ('EnkaNetwork',) + +class profile_picture(TypedDict): + avatarId: int + +class avatar_info(profile_picture): + level: int + +class player_info(TypedDict): + nickname: str + level: int + signature: str + worldLevel: int + nameCardId: int + finishAchievementNum: int + towerFloorIndex: int + towerLevelIndex: int + showAvatarInfoList: List[avatar_info] + showNameCardIdList: List[int] + profilePicture: profile_picture + +class avatar_info_full(TypedDict): + avatarId: int + propMap: Dict[Dict, Dict[str, Any]] + talentIdList: List[int] + fightPropMap: Dict[str, Union[int, float]] + skillDepotId: int + inherentProudSkillList: List[int] + skillLevelMap: Dict[str, int] + equipList: List[Dict[str, Any]] # TODO: check if this is correct + fetterInfo: Dict[str, int] + +class EnkaNetworkResponse(TypedDict): + playerInfo: player_info + avatarInfoList: avatar_info_full + ttl: int + +class EnkaNetwork(TypedDict): + status: int + content: EnkaNetworkResponse + +class Default: + status: int + content: Any diff --git a/enkanetwork/utils.py b/enkanetwork/utils.py index af5439a..f2f6513 100644 --- a/enkanetwork/utils.py +++ b/enkanetwork/utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import re import aiohttp import logging @@ -5,8 +7,13 @@ import json import sys +from typing import Any, Dict, TYPE_CHECKING + from .info import VERSION +if TYPE_CHECKING: + from aiohttp import ClientResponse + LOGGER = logging.getLogger(__name__) # Base URL @@ -45,47 +52,43 @@ def get_default_header(): ), } +class _MissingSentinel: + __slots__ = () -async def request(url: str, headers: dict = None) -> dict: - _url = url.strip(" ") - if headers is None: - headers = {} + def __eq__(self, other): + return False - retry = 0 - async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=30)) as session: # noqa: E501 - """ - From https://gist.github.com/foobarna/19c132304e140bf5031c273f6dc27ece # noqa: E501 - """ + def __bool__(self): + return False - while True: - response = await session.request("GET", _url, headers={**get_default_header(), **headers}) # noqa: E501 + def __hash__(self): + return 0 - if response.status >= 400: - LOGGER.warning(f"Failure to fetch {_url} ({response.status}) Retry {retry} / {RETRY_MAX}") # noqa: E501 - retry += 1 - if retry > RETRY_MAX: - raise Exception(f"Failed to download {url}") + def __repr__(self): + return '...' - await asyncio.sleep(1) - continue - break +MISSING: Any = _MissingSentinel() - data = bytearray() - data_to_read = True - while data_to_read: - red = 0 - while red < CHUNK_SIZE: - chunk = await response.content.read(CHUNK_SIZE - red) +async def to_data(response: ClientResponse) -> Dict[str, Any]: - if not chunk: - data_to_read = False - break + data = bytearray() + data_to_read = True + while data_to_read: + red = 0 + while red < CHUNK_SIZE: + chunk = await response.content.read(CHUNK_SIZE - red) - data.extend(chunk) - red += len(chunk) + if not chunk: + data_to_read = False + break + + data.extend(chunk) + red += len(chunk) + + content = { + "status": response.status, + "content": json.loads(data) + } + return content - return { - "status": response.status, - "content": json.loads(data) - } diff --git a/example/artifacts.py b/example/artifacts.py index 718396b..8af12f6 100644 --- a/example/artifacts.py +++ b/example/artifacts.py @@ -1,31 +1,32 @@ import asyncio from enkanetwork import EnkaNetworkAPI -from enkanetwork.enum import EquipmentsType, DigitType +from enkanetwork import EquipmentsType, DigitType client = EnkaNetworkAPI(lang="th") async def main(): - data = await client.fetch_user(843715177) - for character in data.characters: - print(f"=== Artifacts of {character.name} ===") - for artifact in filter(lambda x: x.type == EquipmentsType.ARTIFACT,character.equipments): - print(f"ID: {artifact.id}") - print(f"Name: {artifact.detail.name}") - print(f"Type: {artifact.detail.artifact_type}") - print(f"Set artifact: {artifact.detail.artifact_name_set}") - print(f"Icon: {artifact.detail.icon}") - print(f"Level: {artifact.level}") - print("--- Main Stats ---") - print(f"Name: {artifact.detail.mainstats.name}") - print(f"Value: {artifact.detail.mainstats.value}{'%' if artifact.detail.mainstats.type == DigitType.PERCENT else ''}") - print("--- Sub Stats ---") - for substate in artifact.detail.substats: - print(f"Name: {substate.name}") - print(f"Value: {substate.value}{'%' if substate.type == DigitType.PERCENT else ''}") - print("-"*18) + async with client: + data = await client.fetch_user(843715177) + for character in data.characters: + print(f"=== Artifacts of {character.name} ===") + for artifact in filter(lambda x: x.type == EquipmentsType.ARTIFACT, character.equipments): + print(f"ID: {artifact.id}") + print(f"Name: {artifact.detail.name}") + print(f"Type: {artifact.detail.artifact_type}") + print(f"Set artifact: {artifact.detail.artifact_name_set}") + print(f"Icon: {artifact.detail.icon}") + print(f"Level: {artifact.level}") + print("--- Main Stats ---") + print(f"Name: {artifact.detail.mainstats.name}") + print(f"Value: {artifact.detail.mainstats.value}{'%' if artifact.detail.mainstats.type == DigitType.PERCENT else ''}") + print("--- Sub Stats ---") + for substate in artifact.detail.substats: + print(f"Name: {substate.name}") + print(f"Value: {substate.value}{'%' if substate.type == DigitType.PERCENT else ''}") + print("-"*18) - print("="*18) + print("="*18) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/cache.py b/example/cache.py index 961d3fc..cdd71dd 100644 --- a/example/cache.py +++ b/example/cache.py @@ -5,11 +5,12 @@ client = EnkaNetworkAPI(lang="th", cache=True) async def main(): - data = await client.fetch_user(843715177) - print("TTL: %s" % data.ttl) - await asyncio.sleep(2) - data_catched = await client.fetch_user(843715177) - print("TTL: %s" % data_catched.ttl) + async with client: + data = await client.fetch_user(843715177) + print("TTL: %s" % data.ttl) + await asyncio.sleep(2) + data_catched = await client.fetch_user(843715177) + print("TTL: %s" % data_catched.ttl) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/characters.py b/example/characters.py index 907c8aa..bf4fd57 100644 --- a/example/characters.py +++ b/example/characters.py @@ -5,22 +5,23 @@ client = EnkaNetworkAPI(lang="th") async def main(): - data = await client.fetch_user(843715177) - print("=== Characters ===") - for character in data.characters: - print(f"ID: {character.id}") - print(f"Name: {character.name}") - print(f"Level: {character.level} / {character.max_level}") - print(f"Rarity: {character.rarity}") - print(f"Element: {character.element}") - print(f"Friendship Level: {character.friendship_level}") - print(f"Ascension: {'⭐'*character.ascension}") - print(f"Constellations unlocked: C{character.constellations_unlocked}") - print(f"XP: {character.xp}") - print(f"Icon: {character.image.icon}") - print(f"Side icon: {character.image.side}") - print(f"Wish banner: {character.image.banner}") - print("="*18) + async with client: + data = await client.fetch_user(843715177) + print("=== Characters ===") + for character in data.characters: + print(f"ID: {character.id}") + print(f"Name: {character.name}") + print(f"Level: {character.level} / {character.max_level}") + print(f"Rarity: {character.rarity}") + print(f"Element: {character.element}") + print(f"Friendship Level: {character.friendship_level}") + print(f"Ascension: {'⭐'*character.ascension}") + print(f"Constellations unlocked: C{character.constellations_unlocked}") + print(f"XP: {character.xp}") + print(f"Icon: {character.image.icon}") + print(f"Side icon: {character.image.side}") + print(f"Wish banner: {character.image.banner}") + print("="*18) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/constellations.py b/example/constellations.py index 8d80cba..8d7b081 100644 --- a/example/constellations.py +++ b/example/constellations.py @@ -5,15 +5,16 @@ client = EnkaNetworkAPI(lang="th") async def main(): - data = await client.fetch_user(843715177) - for character in data.characters: - print(f"=== Constellations of {character.name} ===") - for constellation in character.constellations: - print(f"ID: {constellation.id}") - print(f"Name: {constellation.name}") - print(f"Icon: {constellation.icon}") - print(f"Unlocked: {constellation.unlocked}") - print("="*18) + async with client: + data = await client.fetch_user(843715177) + for character in data.characters: + print(f"=== Constellations of {character.name} ===") + for constellation in character.constellations: + print(f"ID: {constellation.id}") + print(f"Name: {constellation.name}") + print(f"Icon: {constellation.icon}") + print(f"Unlocked: {constellation.unlocked}") + print("="*18) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/custom_cache.py b/example/custom_cache.py index bec31a1..2b32f60 100644 --- a/example/custom_cache.py +++ b/example/custom_cache.py @@ -4,21 +4,17 @@ class CustomCache(Cache): def __init__(self): + super().__init__(1024, 60 * 3) self.cache = {} - def get(self, key): - return self.cache.get(key) - - def set(self, key, value): - self.cache[key] = value - client = EnkaNetworkAPI(lang="th", cache=True) client.set_cache(CustomCache()) async def main(): - await client.fetch_user(843715177) - await asyncio.sleep(2) - await client.fetch_user(843715177) + async with client: + await client.fetch_user(843715177) + await asyncio.sleep(2) + await client.fetch_user(843715177) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/debug.py b/example/debug.py index 75da5cc..27f51eb 100644 --- a/example/debug.py +++ b/example/debug.py @@ -5,8 +5,9 @@ client = EnkaNetworkAPI(lang="th", debug=True) async def main(): - await client.fetch_user(843715177) - # You can see the debug log in console. + async with client: + await client.fetch_user(843715177) + # You can see the debug log in console. loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/languege.py b/example/languege.py index 742bf07..501b45c 100644 --- a/example/languege.py +++ b/example/languege.py @@ -5,10 +5,11 @@ client = EnkaNetworkAPI(lang="th", debug=True) async def main(): - # Change language to "en" - client.lang = "en" - # Or you can use set_language() function (EN -> TW) - await client.set_language("cht") + async with client: + # Change language to "en" + client.lang = "en" + # Or you can use set_language() function (EN -> TW) + await client.set_language("cht") loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/namecards.py b/example/namecards.py index 2ff37b2..98fca31 100644 --- a/example/namecards.py +++ b/example/namecards.py @@ -5,22 +5,23 @@ client = EnkaNetworkAPI(lang="th") async def main(): - data = await client.fetch_user(843715177) - print("=== Namecard main ===") - print(f"ID: {data.player.namecard.id}") - print(f"Name: {data.player.namecard.name}") - print(f"Banner URL: {data.player.namecard.banner}") - print(f"Navbar URL: {data.player.namecard.navbar}") - print(f"Icon URL: {data.player.namecard.icon}") - print("\n") - print("=== List Namecard ===") - for namecard in data.player.namecards: - print(f"ID: {namecard.id}") - print(f"Name: {namecard.name}") - print(f"Banner URL: {namecard.banner}") - print(f"Navbar URL: {namecard.navbar}") - print(f"Icon URL: {namecard.icon}") - print("-"*18) + async with client: + data = await client.fetch_user(843715177) + print("=== Namecard main ===") + print(f"ID: {data.player.namecard.id}") + print(f"Name: {data.player.namecard.name}") + print(f"Banner URL: {data.player.namecard.banner}") + print(f"Navbar URL: {data.player.namecard.navbar}") + print(f"Icon URL: {data.player.namecard.icon}") + print("\n") + print("=== List Namecard ===") + for namecard in data.player.namecards: + print(f"ID: {namecard.id}") + print(f"Name: {namecard.name}") + print(f"Banner URL: {namecard.banner}") + print(f"Navbar URL: {namecard.navbar}") + print(f"Icon URL: {namecard.icon}") + print("-"*18) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/player.py b/example/player.py index d995d05..de7ff88 100644 --- a/example/player.py +++ b/example/player.py @@ -5,22 +5,23 @@ client = EnkaNetworkAPI(lang="th") async def main(): - data = await client.fetch_user(843715177) - print("=== Player Info ===") - print(f"Nickname: {data.player.nickname}") - print(f"Level: {data.player.level}") - print(f"Icon: {data.player.icon.url}") - print(f"Signature: {data.player.signature}") - print(f"Achievement: {data.player.achievement}") - print(f"Abyss floor: {data.player.abyss_floor} - {data.player.abyss_room}") - print(f"Cache timeout: {data.ttl}") - print("=== Characters Preview ===") - for charactersPreview in data.player.characters_preview: - print("ID:", charactersPreview.id) - print("Name:", charactersPreview.name) - print("Icon:", charactersPreview.icon) - print("Level:", charactersPreview.level) - print("="*18) + async with client: + data = await client.fetch_user(843715177) + print("=== Player Info ===") + print(f"Nickname: {data.player.nickname}") + print(f"Level: {data.player.level}") + print(f"Icon: {data.player.icon.url}") + print(f"Signature: {data.player.signature}") + print(f"Achievement: {data.player.achievement}") + print(f"Abyss floor: {data.player.abyss_floor} - {data.player.abyss_room}") + print(f"Cache timeout: {data.ttl}") + print("=== Characters Preview ===") + for charactersPreview in data.player.characters_preview: + print("ID:", charactersPreview.id) + print("Name:", charactersPreview.name) + print("Icon:", charactersPreview.icon) + print("Level:", charactersPreview.level) + print("="*18) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/skills.py b/example/skills.py index da5de9c..f6393c8 100644 --- a/example/skills.py +++ b/example/skills.py @@ -5,15 +5,16 @@ client = EnkaNetworkAPI(lang="th") async def main(): - data = await client.fetch_user(843715177) - for character in data.characters: - print(f"=== Skill of {character.name} ===") - for skill in character.skills: - print(f"ID: {skill.id}") - print(f"Name: {skill.name}") - print(f"Icon: {skill.icon}") - print(f"Level: {skill.level}") - print("="*18) + async with client: + data = await client.fetch_user(843715177) + for character in data.characters: + print(f"=== Skill of {character.name} ===") + for skill in character.skills: + print(f"ID: {skill.id}") + print(f"Name: {skill.name}") + print(f"Icon: {skill.icon}") + print(f"Level: {skill.level}") + print("="*18) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/stats.py b/example/stats.py index 61b9ab5..14b3799 100644 --- a/example/stats.py +++ b/example/stats.py @@ -6,13 +6,14 @@ client = EnkaNetworkAPI(lang="th") async def main(): - data = await client.fetch_user(843715177) - - for character in data.characters: - print(f"=== Stats of {character.name} ===") - for stat in character.stats: - print(f"- {stat[0]}: {stat[1].to_rounded() if isinstance(stat[1], Stats) else stat[1].to_percentage_symbol()}") - print("="*18) + async with client: + data = await client.fetch_user(843715177) + + for character in data.characters: + print(f"=== Stats of {character.name} ===") + for stat in character.stats: + print(f"- {stat[0]}: {stat[1].to_rounded() if isinstance(stat[1], Stats) else stat[1].to_percentage_symbol()}") + print("="*18) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/update_assets.py b/example/update_assets.py index ead971a..b95f86a 100644 --- a/example/update_assets.py +++ b/example/update_assets.py @@ -5,8 +5,9 @@ client = EnkaNetworkAPI(debug=True) async def main(): - await client.update_assets() - # You can see the progress download new assets in console + async with client: + await client.update_assets() + # You can see the progress download new assets in console loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file diff --git a/example/weapon.py b/example/weapon.py index 703d44d..26dbc0e 100644 --- a/example/weapon.py +++ b/example/weapon.py @@ -6,24 +6,25 @@ client = EnkaNetworkAPI(lang="th") async def main(): - data = await client.fetch_user(843715177) - for character in data.characters: - print(f"=== Weapon of {character.name} ===") - weapon = character.equipments[-1] - print(f"ID: {weapon.id}") - print(f"Name: {weapon.detail.name}") - print(f"Icon: {weapon.detail.icon}") - print(f"Level: {weapon.level}") - print(f"Refinement: R{weapon.refinement}") - print(f"Ascension: {'⭐'*weapon.ascension}") - print("--- Main Stats ---") - print(f"Name: {weapon.detail.mainstats.name}") - print(f"Value: {weapon.detail.mainstats.value}{'%' if weapon.detail.mainstats.type == DigitType.PERCENT else ''}") - print("--- Sub Stats ---") - for substate in weapon.detail.substats: - print(f"Name: {substate.name}") - print(f"Value: {substate.value}{'%' if substate.type == DigitType.PERCENT else ''}") - print("-"*18) + async with client: + data = await client.fetch_user(843715177) + for character in data.characters: + print(f"=== Weapon of {character.name} ===") + weapon = character.equipments[-1] + print(f"ID: {weapon.id}") + print(f"Name: {weapon.detail.name}") + print(f"Icon: {weapon.detail.icon}") + print(f"Level: {weapon.level}") + print(f"Refinement: R{weapon.refinement}") + print(f"Ascension: {'⭐'*weapon.ascension}") + print("--- Main Stats ---") + print(f"Name: {weapon.detail.mainstats.name}") + print(f"Value: {weapon.detail.mainstats.value}{'%' if weapon.detail.mainstats.type == DigitType.PERCENT else ''}") + print("--- Sub Stats ---") + for substate in weapon.detail.substats: + print(f"Name: {substate.name}") + print(f"Value: {substate.value}{'%' if substate.type == DigitType.PERCENT else ''}") + print("-"*18) loop = asyncio.get_event_loop() loop.run_until_complete(main()) \ No newline at end of file