mirror of
https://github.com/PaiGramTeam/EnkaNetwork.py.git
synced 2024-11-16 03:45:28 +00:00
Pretty code and add document
This commit is contained in:
parent
618e45e745
commit
d08c255da7
@ -17,7 +17,7 @@ from .enum import Language
|
||||
from .cache import Cache
|
||||
from .config import Config
|
||||
|
||||
from typing import Union, Optional, Type, TYPE_CHECKING, List, Any
|
||||
from typing import Union, Optional, Type, TYPE_CHECKING, List, Any, Callable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Self
|
||||
@ -27,9 +27,52 @@ __all__ = ("EnkaNetworkAPI",)
|
||||
|
||||
|
||||
class EnkaNetworkAPI:
|
||||
""" A library for API wrapper player by UID / Username from https://enka.network
|
||||
|
||||
Parameters
|
||||
------------
|
||||
lang: :class:`str`
|
||||
Init default language
|
||||
debug: :class:`bool`
|
||||
If set to `True`. In request data or get assets. It's will be shown log processing
|
||||
key: :class:`str`
|
||||
Depercated
|
||||
cache: :class:`bool`
|
||||
If set to `True`. In response data will be cache data
|
||||
user_agent: :class:`str`
|
||||
User-Agent for speical to request Enka.Network
|
||||
timeout: :class:`int`
|
||||
Request timeout to Enka.Network
|
||||
|
||||
Attributes
|
||||
------------
|
||||
assets: :class:`Assets`
|
||||
Assets character / artifact / namecards / language / etc. data
|
||||
http: :class:`HTTPClient`
|
||||
HTTP for request and handle data
|
||||
lang: :class:`Language`
|
||||
A default language
|
||||
|
||||
Example
|
||||
------------
|
||||
```py
|
||||
import asyncio
|
||||
|
||||
from enkanetwork import EnkaNetworkAPI
|
||||
|
||||
client = EnkaNetworkAPI(lang="th",user_agent="SpeicalAgent/1.0")
|
||||
|
||||
async def main():
|
||||
async with client:
|
||||
data = await client.fetch_user(843715177)
|
||||
print(data.player.nickname)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
"""
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
def __init__(self, lang: str = "en", *, debug: bool = False, key: str = "", cache: bool = True, user_agent: str = "", timeout: int = 10) -> None: # noqa: E501
|
||||
def __init__(self, *, lang: str = "en", debug: bool = False, key: str = "", cache: bool = True, user_agent: str = "", timeout: int = 10) -> None: # noqa: E501
|
||||
# Logging
|
||||
logging.basicConfig()
|
||||
logging.getLogger("enkanetwork").setLevel(logging.DEBUG if debug else logging.ERROR) # noqa: E501
|
||||
@ -40,7 +83,7 @@ class EnkaNetworkAPI:
|
||||
# Cache
|
||||
self._enable_cache = cache
|
||||
if self._enable_cache:
|
||||
Config.init_cache(Cache(1024, 60 * 3))
|
||||
Config.init_cache(Cache(1024, 60 * 1))
|
||||
|
||||
# http client
|
||||
self.__http = HTTPClient(key=key, agent=user_agent, timeout=timeout)
|
||||
@ -90,32 +133,45 @@ class EnkaNetworkAPI:
|
||||
*,
|
||||
info: bool = False
|
||||
) -> EnkaNetworkResponse:
|
||||
if self._enable_cache:
|
||||
self.LOGGER.warning("Getting data from cache...")
|
||||
data = await Config.CACHE.get(uid)
|
||||
""" Fetch user profile by UID
|
||||
|
||||
if data is not None:
|
||||
# Return data
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
return EnkaNetworkResponse.parse_obj(data)
|
||||
Parameters
|
||||
------------
|
||||
uid: Union[:class:`str`,:class:`int`]
|
||||
UID player in-game
|
||||
info: :class:`bool`
|
||||
If set to `True`. It's will response player info only
|
||||
|
||||
self.LOGGER.debug(f"Fetching user with UID {uid}...")
|
||||
user = await self.__http.fetch_user_by_uid(uid, info=info)
|
||||
|
||||
data = user["content"]
|
||||
data = json.loads(data)
|
||||
|
||||
if self._enable_cache:
|
||||
self.LOGGER.debug("Caching data...")
|
||||
await Config.CACHE.set(uid, data)
|
||||
Raises
|
||||
------------
|
||||
VaildateUIDError
|
||||
Player UID empty/format has incorrect.
|
||||
EnkaPlayerNotFound
|
||||
Player UID doesn't not exists in-game
|
||||
EnkaServerRateLimit
|
||||
Enka.Network has been rate limit
|
||||
EnkaServerMaintanance
|
||||
Enka.Network has maintenance server
|
||||
EnkaServerError
|
||||
Enka.Network has server error (The reason normal is `general`)
|
||||
EnkaServerUnknown
|
||||
Enka.Network has error another
|
||||
|
||||
Returns
|
||||
------------
|
||||
:class:`EnkaNetworkResponse`
|
||||
The response player data
|
||||
"""
|
||||
func = self.__http.fetch_user_by_uid(uid, info=info)
|
||||
data = await self.request_enka(func, uid)
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
# Return data
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
if "owner" in data:
|
||||
data["owner"] = {
|
||||
**data["owner"],
|
||||
"builds": await self.fetch_builds(
|
||||
profile_id=data["owner"]["username"],
|
||||
profile_id=data["owner"]["username"],
|
||||
metaname=data["owner"]["hash"]
|
||||
)
|
||||
}
|
||||
@ -124,29 +180,37 @@ class EnkaNetworkAPI:
|
||||
|
||||
async def fetch_user_by_username(
|
||||
self,
|
||||
profile_id: Union[str, int]
|
||||
profile_id: Optional[str]
|
||||
) -> EnkaNetworkProfileResponse:
|
||||
if self._enable_cache:
|
||||
self.LOGGER.warning("Getting data from cache...")
|
||||
data = await Config.CACHE.get(profile_id)
|
||||
""" Fetch user profile by Username / patreon ID
|
||||
|
||||
if data is not None:
|
||||
# Return data
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
return EnkaNetworkProfileResponse.parse_obj(data)
|
||||
Parameters
|
||||
------------
|
||||
profile_id: Optional[:class:`str`]
|
||||
Username / patreon ID has subscriptions in Enka.Network
|
||||
|
||||
self.LOGGER.debug(f"Fetching user with profile {profile_id}...")
|
||||
Raises
|
||||
------------
|
||||
EnkaPlayerNotFound
|
||||
Player UID doesn't not exists in-game
|
||||
EnkaServerRateLimit
|
||||
Enka.Network has been rate limit
|
||||
EnkaServerMaintanance
|
||||
Enka.Network has maintenance server
|
||||
EnkaServerError
|
||||
Enka.Network has server error (The reason normal is `general`)
|
||||
EnkaServerUnknown
|
||||
Enka.Network has error another
|
||||
|
||||
user = await self.__http.fetch_user_by_username(profile_id)
|
||||
data = user["content"]
|
||||
data = json.loads(data)
|
||||
|
||||
if self._enable_cache:
|
||||
self.LOGGER.debug("Caching data...")
|
||||
await Config.CACHE.set(profile_id, data)
|
||||
|
||||
# Return data
|
||||
Returns
|
||||
------------
|
||||
:class:`EnkaNetworkProfileResponse`
|
||||
The response profile / hoyos and builds data
|
||||
"""
|
||||
func = self.__http.fetch_user_by_username(profile_id)
|
||||
data = await self.request_enka(func, profile_id)
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
|
||||
return EnkaNetworkProfileResponse.parse_obj({
|
||||
**data,
|
||||
"hoyos": await self.fetch_hoyos_by_username(profile_id)
|
||||
@ -154,60 +218,104 @@ class EnkaNetworkAPI:
|
||||
|
||||
async def fetch_hoyos_by_username(
|
||||
self,
|
||||
profile_id: Union[str, int]
|
||||
profile_id: Optional[str]
|
||||
) -> List[PlayerHoyos]:
|
||||
""" Fetch hoyos user data by Username / patreon ID
|
||||
|
||||
Parameters
|
||||
------------
|
||||
profile_id: Optional[:class:`str`]
|
||||
Username / patreon ID has subscriptions in Enka.Network
|
||||
|
||||
Raises
|
||||
------------
|
||||
EnkaPlayerNotFound
|
||||
Player UID doesn't not exists in-game
|
||||
EnkaServerRateLimit
|
||||
Enka.Network has been rate limit
|
||||
EnkaServerMaintanance
|
||||
Enka.Network has maintenance server
|
||||
EnkaServerError
|
||||
Enka.Network has server error (The reason normal is `general`)
|
||||
EnkaServerUnknown
|
||||
Enka.Network has error another
|
||||
|
||||
Returns
|
||||
------------
|
||||
List[:class:`PlayerHoyos`]
|
||||
A response hoyos player data
|
||||
"""
|
||||
key = profile_id + ":hoyos"
|
||||
# Check config
|
||||
if Config.CACHE_ENABLED:
|
||||
self.LOGGER.warning("Getting data from cache...")
|
||||
data = await Config.CACHE.get(key)
|
||||
|
||||
if data is not None:
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
return await self.__format_hoyos(profile_id, data)
|
||||
|
||||
self.LOGGER.debug(f"Fetching user hoyos with profile {profile_id}...")
|
||||
user = await self.__http.fetch_hoyos_by_username(profile_id)
|
||||
data = user["content"]
|
||||
data = json.loads(data)
|
||||
|
||||
if Config.CACHE_ENABLED:
|
||||
self.LOGGER.debug("Caching data...")
|
||||
await Config.CACHE.set(key, data)
|
||||
|
||||
func = self.__http.fetch_hoyos_by_username(key)
|
||||
data = await self.request_enka(func, profile_id)
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
|
||||
return await self.__format_hoyos(profile_id, data)
|
||||
|
||||
async def fetch_builds(
|
||||
self,
|
||||
*,
|
||||
profile_id: Union[str, int],
|
||||
metaname: Union[str, int]
|
||||
) -> PlayerHoyos:
|
||||
profile_id: Optional[str],
|
||||
metaname: Optional[str]
|
||||
) -> Builds:
|
||||
""" Fetch hoyos build(s) data
|
||||
|
||||
Parameters
|
||||
------------
|
||||
profile_id: Optional[:class:`str`]
|
||||
Username / patreon ID has subscriptions in Enka.Network
|
||||
metaname: Optional[:class:`str`]
|
||||
Metaname from hoyos data or owner tag in hash field
|
||||
|
||||
Raises
|
||||
------------
|
||||
EnkaPlayerNotFound
|
||||
Player UID doesn't not exists in-game
|
||||
EnkaServerRateLimit
|
||||
Enka.Network has been rate limit
|
||||
EnkaServerMaintanance
|
||||
Enka.Network has maintenance server
|
||||
EnkaServerError
|
||||
Enka.Network has server error (The reason normal is `general`)
|
||||
EnkaServerUnknown
|
||||
Enka.Network has error another
|
||||
|
||||
Returns
|
||||
------------
|
||||
:class:`Builds`
|
||||
A response builds data
|
||||
"""
|
||||
key = profile_id + ":hoyos:" + metaname + ":builds"
|
||||
func = self.__http.fetch_hoyos_by_username(profile_id, metaname, True)
|
||||
data = await self.request_enka(func, key)
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
|
||||
return Builds.parse_obj(data)
|
||||
|
||||
async def request_enka(
|
||||
self,
|
||||
func: Callable,
|
||||
cache_key: str,
|
||||
):
|
||||
key = cache_key
|
||||
# Check config
|
||||
if Config.CACHE_ENABLED:
|
||||
self.LOGGER.warning("Getting data from cache...")
|
||||
self.LOGGER.warning(f"Getting data {key} from cache...")
|
||||
data = await Config.CACHE.get(key)
|
||||
|
||||
if data is not None:
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
return await self.__format_hoyos(profile_id, data)
|
||||
return data
|
||||
|
||||
# Request data first
|
||||
user = await self.__http.fetch_hoyos_by_username(profile_id,metaname,True)
|
||||
user = await func
|
||||
data = user["content"]
|
||||
data = json.loads(data)
|
||||
|
||||
if Config.CACHE_ENABLED:
|
||||
self.LOGGER.debug("Caching data...")
|
||||
self.LOGGER.debug(f"Caching data {key}...")
|
||||
await Config.CACHE.set(key, data)
|
||||
|
||||
self.LOGGER.debug("Parsing data...")
|
||||
return Builds.parse_obj(data)
|
||||
|
||||
async def request_enka():
|
||||
pass
|
||||
return data
|
||||
|
||||
async def update_assets(self) -> None:
|
||||
print("Updating assets...")
|
||||
@ -229,7 +337,7 @@ class EnkaNetworkAPI:
|
||||
|
||||
# Reload config
|
||||
self.assets.reload_assets()
|
||||
|
||||
|
||||
async def __format_hoyos(self, username: str, data: List[Any]) -> List[PlayerHoyos]:
|
||||
return [PlayerHoyos.parse_obj({
|
||||
"builds": await self.fetch_builds(profile_id=username, metaname=data[key]["hash"]),
|
||||
|
@ -7,7 +7,7 @@ from .cache import Cache
|
||||
class Config:
|
||||
# HTTP Config
|
||||
ENKA_PROTOCOL: ClassVar[str] = "https"
|
||||
ENKA_URL: ClassVar[str] = "dev.enka.network"
|
||||
ENKA_URL: ClassVar[str] = "enka.network"
|
||||
# Assets
|
||||
ASSETS_PROTOCOL: ClassVar[str] = "https"
|
||||
ASSETS_URL: ClassVar[str] = "raw.githubusercontent.com"
|
||||
|
@ -13,7 +13,7 @@ class HTTPException(Exception):
|
||||
class EnkaValidateFailed(HTTPException):
|
||||
""" Exception that's raised for when status code 400 occurs."""
|
||||
|
||||
class EnkaUIDNotFound(Exception):
|
||||
class EnkaPlayerNotFound(Exception):
|
||||
""" Raised when the UID is not found. """
|
||||
|
||||
class EnkaServerError(HTTPException):
|
||||
@ -30,7 +30,7 @@ class EnkaServerUnknown(HTTPException):
|
||||
|
||||
ERROR_ENKA = {
|
||||
400: [VaildateUIDError, "Validate UID {uid} failed."],
|
||||
404: [EnkaUIDNotFound, "UID {uid} not found. Please check your UID"],
|
||||
404: [EnkaPlayerNotFound, "Player ID {uid} not found. Please check your UID / Username"],
|
||||
429: [EnkaServerRateLimit, "Enka.network has been rate limit this path"],
|
||||
424: [EnkaServerMaintanance, "Enka.Network doing maintenance server. Please wait took 5-8 hours or 1 day"],
|
||||
500: [EnkaServerError, "Enka.network server has down or Genshin server broken."],
|
||||
|
@ -124,28 +124,13 @@ class HTTPClient:
|
||||
_host = response.host
|
||||
if 300 > response.status >= 200:
|
||||
data = await utils.to_data(response)
|
||||
|
||||
# if not data['content'] or response.status != 200:
|
||||
# raise UIDNotFounded(f"UID {username} not found.")
|
||||
|
||||
|
||||
self.LOGGER.debug('%s %s has received %s', method, url, data)
|
||||
return data
|
||||
|
||||
if _host == Config.ENKA_URL:
|
||||
err = ERROR_ENKA[response.status]
|
||||
raise err[0](err[1].format(uid=username))
|
||||
# if response.status == 500: # UID not found or Genshin broken
|
||||
# raise UIDNotFounded(f"UID {username} not found or Genshin server broken.")
|
||||
|
||||
# if response.status == 404: # Profile UID not found
|
||||
# raise ProfileNotFounded(f"Profile {username} not found. Please check username has subscription?")
|
||||
|
||||
# if response.status == 424:
|
||||
# raise EnkaServerMaintanance("Enka.Network doing maintenance server. Please wait took 5-8 hours or 1 day")
|
||||
|
||||
# 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}")
|
||||
@ -221,7 +206,7 @@ class HTTPClient:
|
||||
def fetch_asset(self, folder: str, filename: str) -> Response[DefaultPayload]:
|
||||
r = Route(
|
||||
'GET',
|
||||
f'/master/exports/{folder}/{filename}',
|
||||
f'/mrwan200/enkanetwork.py-data/master/exports/{folder}/{filename}',
|
||||
endpoint='assets'
|
||||
)
|
||||
return self.request(r)
|
||||
|
@ -13,7 +13,24 @@ __all__ = (
|
||||
'CharacterAsset'
|
||||
)
|
||||
|
||||
|
||||
class NamecardAsset(BaseModel):
|
||||
""" Namecards (Assets)
|
||||
|
||||
Attributes
|
||||
------------
|
||||
id: :class:`int`
|
||||
Namecard ID
|
||||
hash_id: :class:`str`
|
||||
Namecard hash id
|
||||
icon: :class:`IconAsset`
|
||||
A icon assets. Please refers in `IconAsset` class
|
||||
banner: :class:`IconAsset`
|
||||
A banner assets. Please refers in `IconAsset` class
|
||||
navbar: :class:`IconAsset`
|
||||
A navbar assets. Please refers in `IconAsset` class
|
||||
"""
|
||||
|
||||
id: int = 0
|
||||
hash_id: str = Field("", alias="nameTextMapHash")
|
||||
icon: IconAsset
|
||||
@ -22,13 +39,39 @@ class NamecardAsset(BaseModel):
|
||||
|
||||
|
||||
class CharacterIconAsset(BaseModel):
|
||||
""" Character Icon (Assets)
|
||||
|
||||
Attributes
|
||||
------------
|
||||
icon: :class:`IconAsset`
|
||||
A icon assets. Please refers in `IconAsset` class
|
||||
side: :class:`IconAsset`
|
||||
A navbar assets. Please refers in `IconAsset` class
|
||||
banner: :class:`IconAsset`
|
||||
A banner assets. Please refers in `IconAsset` class
|
||||
card: :class:`IconAsset`
|
||||
A navbar assets. Please refers in `IconAsset` class
|
||||
"""
|
||||
icon: IconAsset
|
||||
side: IconAsset
|
||||
side: IconAsset
|
||||
banner: IconAsset
|
||||
card: IconAsset
|
||||
|
||||
|
||||
class CharacterSkillAsset(BaseModel):
|
||||
""" Character Skill(s) (Assets)
|
||||
|
||||
Attributes
|
||||
------------
|
||||
id: :class:`int`
|
||||
Character skill(s) ID
|
||||
pround_map: :class:`int`
|
||||
pround map for a booest skill by constellation
|
||||
hash_id: :class:`str`
|
||||
Skill(s) hash id
|
||||
icon: :class:`IconAsset`
|
||||
A icon assets. Please refers in `IconAsset` class
|
||||
"""
|
||||
id: int = 0,
|
||||
pround_map: int = 0,
|
||||
hash_id: str = Field("", alias="nameTextMapHash")
|
||||
@ -36,16 +79,60 @@ class CharacterSkillAsset(BaseModel):
|
||||
|
||||
|
||||
class CharacterConstellationsAsset(BaseModel):
|
||||
""" Character Constellations (Assets)
|
||||
|
||||
Attributes
|
||||
------------
|
||||
id: :class:`int`
|
||||
Character constellations ID
|
||||
hash_id: :class:`str`
|
||||
Constellations hash id
|
||||
icon: :class:`IconAsset`
|
||||
A icon assets. Please refers in `IconAsset` class
|
||||
"""
|
||||
id: int = 0
|
||||
hash_id: str = Field("", alias="nameTextMapHash")
|
||||
icon: IconAsset = None
|
||||
|
||||
|
||||
class CharacterCostume(BaseModel):
|
||||
""" Character Costume (Assets)
|
||||
|
||||
Attributes
|
||||
------------
|
||||
id: :class:`int`
|
||||
Costume ID
|
||||
hash_id: :class:`str`
|
||||
Costume hash id
|
||||
icon: :class:`IconAsset`
|
||||
A icon assets. Please refers in `IconAsset` class
|
||||
"""
|
||||
id: int = 0
|
||||
images: CharacterIconAsset = None
|
||||
|
||||
|
||||
class CharacterAsset(BaseModel):
|
||||
""" Character (Assets)
|
||||
|
||||
Attributes
|
||||
------------
|
||||
id: :class:`int`
|
||||
Avatar ID
|
||||
rarity: :class:`int`
|
||||
Character rarity (5 stars or 4stars)
|
||||
hash_id: :class:`str`
|
||||
Character hash id
|
||||
element: :class:`ElementType`
|
||||
Character element type
|
||||
images: :class:`CharacterIconAsset`
|
||||
Character image data. Please refers in `CharacterIconAsset`
|
||||
skill_id: :class:`int`
|
||||
Character skill ID
|
||||
skills: List[:class:`int`]
|
||||
Character skill data list
|
||||
constellations: List[:class:`int`]
|
||||
Character constellations data list
|
||||
"""
|
||||
id: int = 0
|
||||
rarity: int = 0
|
||||
hash_id: str = Field("", alias="nameTextMapHash")
|
||||
@ -61,5 +148,6 @@ class CharacterAsset(BaseModel):
|
||||
def __init__(self, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
|
||||
self.element = ElementType(data["costElemType"]) if data["costElemType"] != "" else ElementType.Unknown
|
||||
self.element = ElementType(
|
||||
data["costElemType"]) if data["costElemType"] != "" else ElementType.Unknown
|
||||
self.rarity = 5 if data["qualityType"].endswith("_ORANGE") else 4
|
||||
|
Loading…
Reference in New Issue
Block a user