2023-05-01 11:24:44 +00:00
|
|
|
from typing import Dict, Union
|
|
|
|
from urllib.parse import urljoin
|
|
|
|
|
|
|
|
from httpx import URL as _URL
|
|
|
|
|
2023-06-11 07:15:00 +00:00
|
|
|
from simnet.errors import RegionNotSupported, NotSupported
|
2023-05-01 11:24:44 +00:00
|
|
|
from simnet.utils.enum_ import Region, Game
|
|
|
|
|
|
|
|
URLTypes = Union["URL", str]
|
|
|
|
|
2023-05-05 12:04:41 +00:00
|
|
|
__all__ = (
|
|
|
|
"URL",
|
|
|
|
"BaseRoute",
|
|
|
|
"Route",
|
|
|
|
"InternationalRoute",
|
|
|
|
"GameRoute",
|
|
|
|
"RECORD_URL",
|
|
|
|
"GACHA_INFO_URL",
|
|
|
|
"AUTH_URL",
|
2023-07-18 06:31:06 +00:00
|
|
|
"PASSPORT_URL",
|
|
|
|
"WEB_ACCOUNT_URL",
|
2023-05-05 12:04:41 +00:00
|
|
|
"AUTH_KEY_URL",
|
|
|
|
"HK4E_LOGIN_URL",
|
2023-05-05 14:15:40 +00:00
|
|
|
"REWARD_URL",
|
2023-05-08 02:00:01 +00:00
|
|
|
"TAKUMI_URL",
|
|
|
|
"CALCULATOR_URL",
|
2023-06-09 03:48:56 +00:00
|
|
|
"DETAIL_LEDGER_URL",
|
|
|
|
"INFO_LEDGER_URL",
|
2023-05-08 02:00:01 +00:00
|
|
|
"HK4E_URL",
|
|
|
|
"CODE_URL",
|
2023-06-09 11:50:48 +00:00
|
|
|
"YSULOG_URL",
|
2023-08-09 05:05:48 +00:00
|
|
|
"QRCODE_URL",
|
2023-05-05 12:04:41 +00:00
|
|
|
)
|
|
|
|
|
2023-05-01 11:24:44 +00:00
|
|
|
|
|
|
|
class URL(_URL):
|
|
|
|
"""A subclass of httpx's URL class, with additional convenience methods for URL manipulation."""
|
|
|
|
|
|
|
|
def join(self, url: URLTypes) -> "URL":
|
|
|
|
"""
|
|
|
|
Join the current URL with the given URL.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
url (Union[URL, str]): The URL to join with.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
URL: A new URL instance representing the joined URL.
|
|
|
|
|
|
|
|
"""
|
|
|
|
return URL(urljoin(str(self), str(URL(url))))
|
|
|
|
|
|
|
|
def __truediv__(self, url: URLTypes) -> "URL":
|
|
|
|
"""
|
|
|
|
Append the given URL to the current URL using the '/' operator.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
url (Union[URL, str]): The URL to append.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
URL: A new URL instance representing the joined URL.
|
|
|
|
|
|
|
|
"""
|
|
|
|
return URL(urljoin(str(self) + "/", str(URL(url))))
|
|
|
|
|
2023-06-11 14:19:20 +00:00
|
|
|
def __bool__(self):
|
|
|
|
"""Return True if the URL is not empty.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: True if the URL is not empty.
|
|
|
|
|
|
|
|
"""
|
|
|
|
return str(self) != ""
|
|
|
|
|
2023-08-09 05:05:48 +00:00
|
|
|
def replace(self, old: str, new: str) -> "URL":
|
|
|
|
"""
|
|
|
|
Replace a substring in the URL.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
old (str): The substring to replace.
|
|
|
|
new (str): The new substring to replace with.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
URL: A new URL instance with the substring replaced.
|
|
|
|
|
|
|
|
"""
|
|
|
|
return URL(str(self).replace(old, new))
|
|
|
|
|
2023-05-01 11:24:44 +00:00
|
|
|
|
|
|
|
class BaseRoute:
|
|
|
|
"""A base class for defining routes with useful metadata."""
|
|
|
|
|
|
|
|
|
|
|
|
class Route(BaseRoute):
|
|
|
|
"""A standard route with a single URL."""
|
|
|
|
|
|
|
|
url: URL
|
|
|
|
|
|
|
|
def __init__(self, url: str) -> None:
|
|
|
|
"""
|
|
|
|
Initialize a Route instance.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
url (str): The URL for this route.
|
|
|
|
|
|
|
|
"""
|
|
|
|
self.url = URL(url)
|
|
|
|
|
|
|
|
def get_url(self) -> URL:
|
|
|
|
"""
|
|
|
|
Get the URL for this route.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
URL: The URL for this route.
|
|
|
|
|
|
|
|
"""
|
|
|
|
return self.url
|
|
|
|
|
2023-08-09 05:05:48 +00:00
|
|
|
def __truediv__(self, other: str) -> URL:
|
|
|
|
"""
|
|
|
|
Append the given URL to this route using the '/' operator.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
other (Union[URL, str]): The URL to append.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
URL: A new URL instance representing the joined URL.
|
|
|
|
|
|
|
|
"""
|
|
|
|
return self.url / other
|
|
|
|
|
2023-05-01 11:24:44 +00:00
|
|
|
|
|
|
|
class InternationalRoute(BaseRoute):
|
|
|
|
"""A route with URLs for both the overseas and Chinese regions."""
|
|
|
|
|
|
|
|
urls: Dict[Region, URL]
|
|
|
|
|
|
|
|
def __init__(self, overseas: str, chinese: str) -> None:
|
|
|
|
"""
|
|
|
|
Initialize an InternationalRoute instance.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
overseas (str): The URL for the overseas region.
|
|
|
|
chinese (str): The URL for the Chinese region.
|
|
|
|
|
|
|
|
"""
|
|
|
|
self.urls = {
|
|
|
|
Region.OVERSEAS: URL(overseas),
|
|
|
|
Region.CHINESE: URL(chinese),
|
|
|
|
}
|
|
|
|
|
|
|
|
def get_url(self, region: Region) -> URL:
|
|
|
|
"""
|
|
|
|
Get the URL for the given region.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
region (Region): The region to get the URL for.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
URL: The URL for the given region.
|
|
|
|
|
|
|
|
Raises:
|
2023-06-11 07:15:00 +00:00
|
|
|
RegionNotSupported: If the given region is not supported.
|
2023-05-01 11:24:44 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
if not self.urls[region]:
|
2023-06-11 07:15:00 +00:00
|
|
|
raise RegionNotSupported(f"URL does not support {region.name} region.")
|
2023-05-01 11:24:44 +00:00
|
|
|
|
|
|
|
return self.urls[region]
|
|
|
|
|
|
|
|
|
|
|
|
class GameRoute(BaseRoute):
|
|
|
|
"""A route with URLs for different games and regions."""
|
|
|
|
|
|
|
|
urls: Dict[Region, Dict[Game, URL]]
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
overseas: Dict[str, str],
|
|
|
|
chinese: Dict[str, str],
|
|
|
|
) -> None:
|
|
|
|
"""
|
|
|
|
Initialize a GameRoute instance.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
overseas (Dict[str, str]): A dictionary mapping game names to URLs for the overseas region.
|
|
|
|
chinese (Dict[str, str]): A dictionary mapping game names to URLs for the Chinese region.
|
|
|
|
|
|
|
|
"""
|
|
|
|
self.urls = {
|
|
|
|
Region.OVERSEAS: {Game(game): URL(url) for game, url in overseas.items()},
|
|
|
|
Region.CHINESE: {Game(game): URL(url) for game, url in chinese.items()},
|
|
|
|
}
|
|
|
|
|
|
|
|
def get_url(self, region: Region, game: Game) -> URL:
|
|
|
|
"""
|
|
|
|
Get the URL for the given region and game.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
region (Region): The region to get the URL for.
|
|
|
|
game (Game): The game to get the URL for.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
URL: The URL for the given region and game.
|
|
|
|
|
|
|
|
Raises:
|
2023-06-11 07:15:00 +00:00
|
|
|
RegionNotSupported: If the given region is not supported.
|
|
|
|
GameNotSupported: If the given game is not supported.
|
2023-05-01 11:24:44 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
if not self.urls[region]:
|
2023-06-11 07:15:00 +00:00
|
|
|
raise RegionNotSupported(f"URL does not support {region.name} region.")
|
2023-05-01 11:24:44 +00:00
|
|
|
|
|
|
|
if not self.urls[region][game]:
|
2023-06-11 07:15:00 +00:00
|
|
|
raise NotSupported(f"URL does not support {game.name} game for {region.name} region.")
|
2023-05-01 11:24:44 +00:00
|
|
|
|
|
|
|
return self.urls[region][game]
|
2023-05-01 12:50:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
RECORD_URL = InternationalRoute(
|
|
|
|
overseas="https://bbs-api-os.hoyolab.com/game_record",
|
|
|
|
chinese="https://api-takumi-record.mihoyo.com/game_record/app",
|
|
|
|
)
|
|
|
|
|
|
|
|
GACHA_INFO_URL = GameRoute(
|
|
|
|
overseas=dict(
|
|
|
|
genshin="https://hk4e-api-os.hoyoverse.com/event/gacha_info/api",
|
2023-07-17 07:34:43 +00:00
|
|
|
hkrpg="https://api-os-takumi.mihoyo.com/common/gacha_record/api/",
|
2023-05-01 12:50:48 +00:00
|
|
|
),
|
|
|
|
chinese=dict(
|
|
|
|
genshin="https://hk4e-api.mihoyo.com/event/gacha_info/api",
|
|
|
|
hkrpg="https://api-takumi.mihoyo.com/common/gacha_record/api",
|
|
|
|
),
|
|
|
|
)
|
2023-05-04 15:51:24 +00:00
|
|
|
|
|
|
|
AUTH_URL = InternationalRoute(
|
2023-07-18 06:31:06 +00:00
|
|
|
overseas="https://api-os-takumi.mihoyo.com/auth/api",
|
2023-05-04 15:51:24 +00:00
|
|
|
chinese="https://api-takumi.mihoyo.com/auth/api",
|
|
|
|
)
|
2023-07-18 06:31:06 +00:00
|
|
|
PASSPORT_URL = InternationalRoute(
|
|
|
|
overseas="https://api-account-os.hoyoverse.com/account/auth/api/",
|
|
|
|
chinese="https://passport-api.mihoyo.com/account/auth/api/",
|
2023-05-04 15:51:24 +00:00
|
|
|
)
|
2023-07-18 06:31:06 +00:00
|
|
|
WEB_ACCOUNT_URL = InternationalRoute(
|
|
|
|
overseas="https://webapi-os.account.hoyoverse.com/Api/",
|
|
|
|
chinese="https://webapi.account.mihoyo.com/Api/",
|
2023-05-04 15:51:24 +00:00
|
|
|
)
|
|
|
|
|
2023-09-11 11:32:44 +00:00
|
|
|
AUTH_KEY_URL = InternationalRoute(
|
|
|
|
overseas="https://sg-public-api.hoyoverse.com/binding/api/genAuthKey",
|
|
|
|
chinese="https://api-takumi.mihoyo.com/binding/api/genAuthKey",
|
|
|
|
)
|
2023-05-04 15:51:24 +00:00
|
|
|
|
|
|
|
HK4E_LOGIN_URL = InternationalRoute(
|
|
|
|
overseas="https://sg-public-api.hoyoverse.com/common/badge/v1/login/account",
|
|
|
|
chinese="https://api-takumi.mihoyo.com/common/badge/v1/login/account",
|
|
|
|
)
|
2023-05-05 14:15:40 +00:00
|
|
|
|
|
|
|
REWARD_URL = GameRoute(
|
|
|
|
overseas=dict(
|
2023-07-18 13:27:23 +00:00
|
|
|
genshin="https://sg-hk4e-api.hoyolab.com/event/sol/?act_id=e202102251931481",
|
|
|
|
honkai3rd="https://sg-public-api.hoyolab.com/event/mani/?act_id=e202110291205111",
|
2023-05-05 14:15:40 +00:00
|
|
|
hkrpg="https://sg-public-api.hoyolab.com/event/luna/os/?act_id=e202303301540311",
|
|
|
|
),
|
|
|
|
chinese=dict(
|
|
|
|
genshin="https://api-takumi.mihoyo.com/event/bbs_sign_reward/?act_id=e202009291139501",
|
|
|
|
honkai3rd="https://api-takumi.mihoyo.com/event/luna/?act_id=e202207181446311",
|
|
|
|
hkrpg="https://api-takumi.mihoyo.com/event/luna/?act_id=e202304121516551",
|
|
|
|
),
|
|
|
|
)
|
2023-05-08 02:00:01 +00:00
|
|
|
TAKUMI_URL = InternationalRoute(
|
|
|
|
overseas="https://api-os-takumi.mihoyo.com/",
|
|
|
|
chinese="https://api-takumi.mihoyo.com/",
|
|
|
|
)
|
|
|
|
|
|
|
|
CALCULATOR_URL = InternationalRoute(
|
|
|
|
overseas="https://sg-public-api.hoyoverse.com/event/calculateos/",
|
|
|
|
chinese="https://api-takumi.mihoyo.com/event/e20200928calculate/v1/",
|
|
|
|
)
|
|
|
|
|
2023-06-11 14:19:20 +00:00
|
|
|
DETAIL_LEDGER_URL = GameRoute(
|
|
|
|
overseas=dict(
|
|
|
|
genshin="https://sg-hk4e-api.hoyolab.com/event/ysledgeros/month_detail",
|
2023-06-23 14:54:42 +00:00
|
|
|
hkrpg="https://sg-public-api.hoyolab.com/event/srledger/month_detail",
|
2023-06-11 14:19:20 +00:00
|
|
|
),
|
|
|
|
chinese=dict(
|
|
|
|
genshin="https://hk4e-api.mihoyo.com/event/ys_ledger/monthDetail",
|
|
|
|
hkrpg="https://api-takumi.mihoyo.com/event/srledger/month_detail",
|
|
|
|
),
|
2023-06-09 03:48:56 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
INFO_LEDGER_URL = GameRoute(
|
|
|
|
overseas=dict(
|
|
|
|
genshin="https://sg-hk4e-api.hoyolab.com/event/ysledgeros/month_info",
|
2023-06-23 14:54:42 +00:00
|
|
|
hkrpg="https://sg-public-api.hoyolab.com/event/srledger/month_info",
|
2023-06-09 03:48:56 +00:00
|
|
|
),
|
|
|
|
chinese=dict(
|
|
|
|
genshin="https://hk4e-api.mihoyo.com/event/ys_ledger/monthInfo",
|
|
|
|
hkrpg="https://api-takumi.mihoyo.com/event/srledger/month_info",
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
2023-06-09 11:50:48 +00:00
|
|
|
YSULOG_URL = InternationalRoute(
|
2023-07-17 05:14:40 +00:00
|
|
|
overseas="https://hk4e-api-os.hoyoverse.com/common/hk4e_self_help_query/User/",
|
|
|
|
chinese="https://hk4e-api.mihoyo.com/common/hk4e_self_help_query/User/",
|
2023-06-09 11:50:48 +00:00
|
|
|
)
|
2023-05-08 02:00:01 +00:00
|
|
|
|
|
|
|
HK4E_URL = Route("https://sg-hk4e-api.hoyoverse.com/common/hk4e_global/")
|
|
|
|
|
2023-05-12 03:24:06 +00:00
|
|
|
CODE_URL = Route("https://sg-hk4e-api.hoyoverse.com/common/apicdkey/api/webExchangeCdkey")
|
2023-08-09 05:05:48 +00:00
|
|
|
|
|
|
|
QRCODE_URL = Route("https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode")
|