3
0
Telegram_PaimonBot/genshinstats/utils.py
2022-01-28 17:58:47 +08:00

121 lines
3.3 KiB
Python

"""Various utility functions for genshinstats."""
import inspect
import os.path
import re
import warnings
from functools import wraps
from typing import Callable, Iterable, Optional, Type, TypeVar, Union
from .errors import AccountNotFound
__all__ = [
"USER_AGENT",
"recognize_server",
"recognize_id",
"is_game_uid",
"is_chinese",
"get_logfile",
]
T = TypeVar("T")
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
def recognize_server(uid: int) -> str:
"""Recognizes which server a UID is from."""
server = {
"1": "cn_gf01",
"2": "cn_gf01",
"5": "cn_qd01",
"6": "os_usa",
"7": "os_euro",
"8": "os_asia",
"9": "os_cht",
}.get(str(uid)[0])
if server:
return server
else:
raise AccountNotFound(f"UID {uid} isn't associated with any server")
def recognize_id(id: int) -> Optional[str]:
"""Attempts to recognize what item type an id is"""
if 10000000 < id < 20000000:
return "character"
elif 1000000 < id < 10000000:
return "artifact_set"
elif 100000 < id < 1000000:
return "outfit"
elif 50000 < id < 100000:
return "artifact"
elif 10000 < id < 50000:
return "weapon"
elif 100 < id < 1000:
return "constellation"
elif 10 ** 17 < id < 10 ** 19:
return "transaction"
# not sure about these ones:
elif 1 <= id <= 4:
return "exploration"
else:
return None
def is_game_uid(uid: int) -> bool:
"""Recognizes whether the uid is a game uid."""
return bool(re.fullmatch(r"[6789]\d{8}", str(uid)))
def is_chinese(x: Union[int, str]) -> bool:
"""Recognizes whether the server/uid is chinese."""
return str(x).startswith(("cn", "1", "5"))
def get_logfile() -> Optional[str]:
"""Find and return the Genshin Impact logfile. None if not found."""
mihoyo_dir = os.path.expanduser("~/AppData/LocalLow/miHoYo/")
for name in ["Genshin Impact", "原神", "YuanShen"]:
output_log = os.path.join(mihoyo_dir, name, "output_log.txt")
if os.path.isfile(output_log):
return output_log
return None # no genshin installation
def retry(
tries: int = 3,
exceptions: Union[Type[BaseException], Iterable[Type[BaseException]]] = Exception,
) -> Callable[[T], T]:
"""A classic retry() decorator"""
def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
for _ in range(tries):
try:
return func(*args, **kwargs)
except exceptions as e:
exc = e
else:
raise Exception(f"Maximum tries ({tries}) exceeded: {exc}") from exc # type: ignore
return inner
return wrapper # type: ignore
def deprecated(
message: str = "{} is deprecated and will be removed in future versions",
) -> Callable[[T], T]:
"""Shows a warning when a function is attempted to be used"""
def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
warnings.warn(message.format(func.__name__), PendingDeprecationWarning)
return func(*args, **kwargs)
return inner
return wrapper # type: ignore