Support hoyolab auth methods

This commit is contained in:
omg-xtao 2023-07-18 14:31:06 +08:00 committed by GitHub
parent 408e5ba014
commit e66746da23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 44 deletions

View File

@ -318,11 +318,10 @@ class BaseClient(AsyncContextManager["BaseClient"]):
params=params,
headers=headers,
)
# if "application/json" in response.headers.get("Content-Type", ""):
if not response.is_error:
data = response.json()
ret_code = data.get("retcode")
if response.is_error or ret_code != 0:
ret_code = data.get("retcode", 0)
if ret_code != 0:
raise_for_ret_code(data)
return data["data"]
if response.status_code == 404:

View File

@ -5,10 +5,9 @@ from simnet.client.routes import (
AUTH_URL,
AUTH_KEY_URL,
HK4E_LOGIN_URL,
GET_COOKIES_TOKEN_BY_STOKEN_URL,
GET_LTOKEN_BY_STOKEN_URL,
PASSPORT_URL,
WEB_ACCOUNT_URL,
)
from simnet.errors import RegionNotSupported
from simnet.utils.enum_ import Region
__all__ = ("AuthClient",)
@ -37,12 +36,9 @@ class AuthClient(BaseClient):
Optional[str]: The retrieved super ticket (`stoken`).
Raises:
RegionNotSupported: This method is only available for the Chinese region.
ValueError: If the `login_ticket` argument is `None`, or if the `account_id` argument is `None`.
"""
if self.region != Region.CHINESE:
raise RegionNotSupported("This method is only available for the Chinese region.")
url = AUTH_URL.get_url(Region.CHINESE) / "getMultiTokenByLoginTicket"
url = AUTH_URL.get_url(self.region) / "getMultiTokenByLoginTicket"
login_ticket = login_ticket or self.cookies.get("login_ticket")
account_id = account_id or self.account_id
if login_ticket is None:
@ -64,9 +60,41 @@ class AuthClient(BaseClient):
stoken = self.cookies.get("stoken")
stuid = self.cookies.get("stuid")
if stoken and stuid:
self.cookies["stuid"] = self.account_id
self.cookies["stuid"] = str(self.account_id)
return stoken
async def get_cookie_token_by_login_ticket(self, login_ticket: Optional[str] = None) -> Optional[str]:
"""
Retrieves a cookie token (`cookie_token`) using a login ticket (`login_ticket`).
Args:
login_ticket (Optional[str]): The login ticket to use to retrieve the cookie token. If not provided, the
`login_ticket` cookie value will be used.
Returns:
Optional[str]: The retrieved cookie token (`cookie_token`).
Raises:
ValueError: If the `login_ticket` argument is `None`.
"""
url = WEB_ACCOUNT_URL.get_url(self.region) / "cookie_accountinfo_by_loginticket"
login_ticket = login_ticket or self.cookies.get("login_ticket")
if login_ticket is None:
raise ValueError("The 'login_ticket' argument cannot be None.")
params = {"login_ticket": login_ticket}
data = await self.request_lab(url, params=params)
cookie_info = data.get("cookie_info")
if not cookie_info:
raise ValueError("The 'login_ticket' is expired.")
account_id = cookie_info.get("account_id")
cookie_token = cookie_info.get("cookie_token")
if account_id:
self.account_id = account_id
self.cookies["account_id"] = str(account_id)
if cookie_token:
self.cookies["cookie_token"] = cookie_token
return cookie_token
async def get_cookie_token_by_stoken(
self, stoken: Optional[str] = None, account_id: Optional[int] = None
) -> Optional[str]:
@ -83,27 +111,25 @@ class AuthClient(BaseClient):
Optional[str]: The retrieved cookie token (`cookie_token`).
Raises:
RegionNotSupported: This method is only available for the Chinese region.
ValueError: If the `login_ticket` argument is `None`, or if the `account_id` argument is `None`.
"""
if self.region != Region.CHINESE:
raise RegionNotSupported("This method is only available for the Chinese region.")
stoken = stoken or self.cookies.get("stoken")
account_id = account_id or self.account_id
if stoken is None:
raise ValueError("The 'stoken' argument cannot be None.")
if account_id is None:
raise ValueError("The 'account_id' argument cannot be None.")
url = GET_COOKIES_TOKEN_BY_STOKEN_URL.get_url(Region.CHINESE)
url = PASSPORT_URL.get_url(self.region) / "getCookieAccountInfoBySToken"
method = "GET" if self.region == Region.CHINESE else "POST"
params = {
"stoken": stoken,
"uid": account_id,
}
data = await self.request_lab(url, params=params)
data = await self.request_lab(url, method=method, params=params)
cookie_token = data.get("cookie_token")
if cookie_token:
self.cookies["cookie_token"] = cookie_token
self.cookies["account_id"] = self.account_id
self.cookies["account_id"] = str(self.account_id)
return cookie_token
async def get_ltoken_by_stoken(
@ -122,27 +148,25 @@ class AuthClient(BaseClient):
Optional[str]: The retrieved cookie token (`cookie_token`).
Raises:
RegionNotSupported: This method is only available for the Chinese region.
ValueError: If the `login_ticket` argument is `None`, or if the `account_id` argument is `None`.
"""
if self.region != Region.CHINESE:
raise RegionNotSupported("This method is only available for the Chinese region.")
stoken = stoken or self.cookies.get("stoken")
account_id = account_id or self.account_id
if stoken is None:
raise ValueError("The 'stoken' argument cannot be None.")
if account_id is None:
raise ValueError("The 'account_id' argument cannot be None.")
url = GET_LTOKEN_BY_STOKEN_URL.get_url(Region.CHINESE)
url = PASSPORT_URL.get_url(self.region) / "getLTokenBySToken"
method = "GET" if self.region == Region.CHINESE else "POST"
params = {
"stoken": stoken,
"uid": account_id,
}
data = await self.request_lab(url, params=params)
data = await self.request_lab(url, method=method, params=params)
ltoken = data.get("ltoken", "")
if ltoken:
self.cookies["ltoken"] = ltoken
self.cookies["ltuid"] = self.account_id
self.cookies["ltuid"] = str(self.account_id)
return ltoken
async def get_authkey_by_stoken(self, game_biz: str, region: str, auth_appid: str) -> Optional[str]:
@ -159,11 +183,8 @@ class AuthClient(BaseClient):
Optional[str]: The authentication key, or None if not found.
Raises:
RegionNotSupported: This method is only available for the Chinese region.
ValueError: If `stoken` is not found in the cookies or `player_id` not found.
"""
if self.region != Region.CHINESE:
raise RegionNotSupported("This method is only available for the Chinese region.")
stoken = self.cookies.get("stoken")
if stoken is None:
raise ValueError("stoken not found in cookies.")

View File

@ -12,9 +12,11 @@ __all__ = ("Cookies",)
class Cookies(_Cookies):
"""A wrapper around `httpx.Cookies` that provides additional functionality."""
jar: CookieJar
def __init__(self, cookies: Optional[CookieTypes] = None): # skipcq: PYL-W0231
self.jar = CookieJar()
if cookies is None or isinstance(cookies, dict):
self.jar = CookieJar()
if isinstance(cookies, dict):
for key, value in cookies.items():
if isinstance(value, str):
@ -22,20 +24,17 @@ class Cookies(_Cookies):
else:
self.set(key, str(value))
elif isinstance(cookies, list):
self.jar = CookieJar()
for key, value in cookies:
self.set(key, value)
elif isinstance(cookies, Cookies):
self.jar = CookieJar()
for cookie in cookies.jar:
self.jar.set_cookie(cookie)
elif isinstance(cookies, str):
self.jar = CookieJar()
cookie = SimpleCookie(cookies)
for key, value in cookie.items():
self.set(key, value.value)
else:
self.jar = cookies
self.jar = cookies # type: ignore
COOKIE_USER_ID_NAMES = ("ltuid", "account_id", "stuid", "ltuid_v2", "account_id_v2")
@ -54,3 +53,29 @@ class Cookies(_Cookies):
if value is not None:
return int(value)
return None
def get(
self,
name: str,
default: Optional[str] = None,
domain: Optional[str] = None,
path: Optional[str] = None,
) -> Optional[str]:
"""
Get a cookie by name. May optionally include domain and path
in order to specify exactly which cookie to retrieve.
"""
value = None
for cookie in self.jar:
if (
cookie.name == name
and domain is None
or cookie.domain == domain
and path is None
or cookie.path == path
and cookie.value
):
value = cookie.value
if value is None:
return default
return value

View File

@ -17,8 +17,8 @@ __all__ = (
"RECORD_URL",
"GACHA_INFO_URL",
"AUTH_URL",
"GET_COOKIES_TOKEN_BY_STOKEN_URL",
"GET_LTOKEN_BY_STOKEN_URL",
"PASSPORT_URL",
"WEB_ACCOUNT_URL",
"AUTH_KEY_URL",
"HK4E_LOGIN_URL",
"REWARD_URL",
@ -188,8 +188,6 @@ class GameRoute(BaseRoute):
return self.urls[region][game]
PASSPORT_HOST = "passport-api.mihoyo.com"
RECORD_URL = InternationalRoute(
overseas="https://bbs-api-os.hoyolab.com/game_record",
chinese="https://api-takumi-record.mihoyo.com/game_record/app",
@ -207,18 +205,16 @@ GACHA_INFO_URL = GameRoute(
)
AUTH_URL = InternationalRoute(
overseas="",
overseas="https://api-os-takumi.mihoyo.com/auth/api",
chinese="https://api-takumi.mihoyo.com/auth/api",
)
GET_COOKIES_TOKEN_BY_STOKEN_URL = InternationalRoute(
overseas="",
chinese=f"https://{PASSPORT_HOST}/account/auth/api/getCookieAccountInfoBySToken",
PASSPORT_URL = InternationalRoute(
overseas="https://api-account-os.hoyoverse.com/account/auth/api/",
chinese="https://passport-api.mihoyo.com/account/auth/api/",
)
GET_LTOKEN_BY_STOKEN_URL = InternationalRoute(
overseas="",
chinese=f"https://{PASSPORT_HOST}/account/auth/api/getLTokenBySToken",
WEB_ACCOUNT_URL = InternationalRoute(
overseas="https://webapi-os.account.hoyoverse.com/Api/",
chinese="https://webapi.account.mihoyo.com/Api/",
)
AUTH_KEY_URL = InternationalRoute(overseas="", chinese="https://api-takumi.mihoyo.com/binding/api/genAuthKey")