mirror of
https://github.com/PaiGramTeam/SIMNet.git
synced 2024-11-21 21:58:05 +00:00
✨ Support ZZZClient
This commit is contained in:
parent
05fcb568d6
commit
074939d881
@ -8,7 +8,7 @@ readme = "README.md"
|
|||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.8"
|
python = "^3.8"
|
||||||
httpx = "^0.25.0"
|
httpx = ">=0.25.0"
|
||||||
pydantic = "<2.0.0,>=1.10.7"
|
pydantic = "<2.0.0,>=1.10.7"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from simnet.client.genshin import GenshinClient
|
from simnet.client.genshin import GenshinClient
|
||||||
from simnet.client.starrail import StarRailClient
|
from simnet.client.starrail import StarRailClient
|
||||||
|
from simnet.client.zzz import ZZZClient
|
||||||
from simnet.utils.enums import Game, Region
|
from simnet.utils.enums import Game, Region
|
||||||
|
|
||||||
__all__ = ("StarRailClient", "GenshinClient", "Game", "Region")
|
__all__ = ("StarRailClient", "GenshinClient", "ZZZClient", "Game", "Region")
|
||||||
|
@ -217,6 +217,8 @@ class BaseClient(AsyncContextManager["BaseClient"]):
|
|||||||
headers["x-rpc-device_id"] = self.get_device_id()
|
headers["x-rpc-device_id"] = self.get_device_id()
|
||||||
headers["x-rpc-device_fp"] = self.get_device_fp()
|
headers["x-rpc-device_fp"] = self.get_device_fp()
|
||||||
if self.region == Region.OVERSEAS:
|
if self.region == Region.OVERSEAS:
|
||||||
|
if self.game == Game.ZZZ:
|
||||||
|
headers["x-rpc-lang"] = self.lang or lang
|
||||||
headers["x-rpc-language"] = self.lang or lang
|
headers["x-rpc-language"] = self.lang or lang
|
||||||
if ds is None:
|
if ds is None:
|
||||||
app_version, client_type, ds = generate_dynamic_secret(self.region, ds_type, new_ds, data, params)
|
app_version, client_type, ds = generate_dynamic_secret(self.region, ds_type, new_ds, data, params)
|
||||||
|
@ -50,10 +50,13 @@ class BaseChronicleClient(BaseClient):
|
|||||||
TimedOut: If the request times out.
|
TimedOut: If the request times out.
|
||||||
BadRequest: If the response contains an error.
|
BadRequest: If the response contains an error.
|
||||||
"""
|
"""
|
||||||
base_url = RECORD_URL.get_url(region or self.region)
|
base_url = RECORD_URL.get_url(region or self.region, game or Game.GENSHIN)
|
||||||
|
|
||||||
if game:
|
if game:
|
||||||
base_url = base_url / game.value / endpoint_type
|
if game == Game.ZZZ:
|
||||||
|
base_url = base_url / endpoint_type / "zzz"
|
||||||
|
else:
|
||||||
|
base_url = base_url / game.value / endpoint_type
|
||||||
|
|
||||||
url = base_url / endpoint
|
url = base_url / endpoint
|
||||||
new_ds = self.region == Region.CHINESE
|
new_ds = self.region == Region.CHINESE
|
||||||
|
@ -312,7 +312,7 @@ class GenshinBattleChronicleClient(BaseChronicleClient):
|
|||||||
if self.account_id is not None and stuid is None:
|
if self.account_id is not None and stuid is None:
|
||||||
self.cookies.set("stuid", str(self.account_id))
|
self.cookies.set("stuid", str(self.account_id))
|
||||||
if self.region == Region.OVERSEAS:
|
if self.region == Region.OVERSEAS:
|
||||||
route = RECORD_URL.get_url(self.region) / "../community/apihub/api/widget/data"
|
route = RECORD_URL.get_url(self.region, self.game) / "../community/apihub/api/widget/data"
|
||||||
params = {"game_id": "2"}
|
params = {"game_id": "2"}
|
||||||
data = await self.request_lab(route, params=params, lang=lang)
|
data = await self.request_lab(route, params=params, lang=lang)
|
||||||
model = NotesOverseaWidget
|
model = NotesOverseaWidget
|
||||||
|
@ -412,7 +412,7 @@ class StarRailBattleChronicleClient(BaseChronicleClient):
|
|||||||
if self.account_id is not None and stuid is None:
|
if self.account_id is not None and stuid is None:
|
||||||
self.cookies.set("stuid", str(self.account_id))
|
self.cookies.set("stuid", str(self.account_id))
|
||||||
if self.region == Region.OVERSEAS:
|
if self.region == Region.OVERSEAS:
|
||||||
route = RECORD_URL.get_url(self.region) / "../community/apihub/api/hsr_widget"
|
route = RECORD_URL.get_url(self.region, self.game) / "../community/apihub/api/hsr_widget"
|
||||||
data = await self.request_lab(route, lang=lang)
|
data = await self.request_lab(route, lang=lang)
|
||||||
model = StarRailNoteOverseaWidget
|
model = StarRailNoteOverseaWidget
|
||||||
else:
|
else:
|
||||||
|
218
simnet/client/components/chronicle/zzz.py
Normal file
218
simnet/client/components/chronicle/zzz.py
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
from typing import Optional, Mapping, Dict, Any, List
|
||||||
|
|
||||||
|
from simnet.client.components.chronicle.base import BaseChronicleClient
|
||||||
|
from simnet.errors import BadRequest, DataNotPublic
|
||||||
|
from simnet.models.lab.record import RecordCard
|
||||||
|
from simnet.models.zzz.calculator import ZZZCalculatorCharacterDetails
|
||||||
|
from simnet.models.zzz.chronicle.notes import ZZZNote
|
||||||
|
from simnet.models.zzz.chronicle.stats import ZZZUserStats, ZZZAvatarBasic, ZZZBuddyBasic
|
||||||
|
from simnet.utils.enums import Game
|
||||||
|
from simnet.utils.player import recognize_region, recognize_zzz_server
|
||||||
|
|
||||||
|
__all__ = ("ZZZBattleChronicleClient",)
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZBattleChronicleClient(BaseChronicleClient):
|
||||||
|
"""A client for retrieving data from ZZZ's battle chronicle component.
|
||||||
|
|
||||||
|
This class is used to retrieve various data objects from StarRail's battle chronicle component,
|
||||||
|
including real-time notes, user statistics, and character information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def _request_zzz_record(
|
||||||
|
self,
|
||||||
|
endpoint: str,
|
||||||
|
player_id: Optional[int] = None,
|
||||||
|
endpoint_type: str = "api",
|
||||||
|
method: str = "GET",
|
||||||
|
lang: Optional[str] = None,
|
||||||
|
payload: Optional[Dict[str, Any]] = None,
|
||||||
|
) -> Mapping[str, Any]:
|
||||||
|
"""Get an arbitrary object from ZZZ's battle chronicle.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
endpoint (str): The endpoint of the object to retrieve.
|
||||||
|
player_id (Optional[int], optional): The player ID. Defaults to None.
|
||||||
|
method (str, optional): The HTTP method to use. Defaults to "GET".
|
||||||
|
lang (Optional[str], optional): The language of the data. Defaults to None.
|
||||||
|
payload (Optional[Dict[str, Any]], optional): The request payload. Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Mapping[str, Any]: The requested object.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BadRequest: If the request is invalid.
|
||||||
|
DataNotPublic: If the requested data is not public.
|
||||||
|
"""
|
||||||
|
payload = dict(payload or {})
|
||||||
|
|
||||||
|
player_id = player_id or self.player_id
|
||||||
|
payload = dict(role_id=player_id, server=recognize_zzz_server(player_id), **payload)
|
||||||
|
|
||||||
|
data, params = None, None
|
||||||
|
if method == "POST":
|
||||||
|
data = payload
|
||||||
|
else:
|
||||||
|
params = payload
|
||||||
|
|
||||||
|
return await self.request_game_record(
|
||||||
|
endpoint,
|
||||||
|
endpoint_type=endpoint_type,
|
||||||
|
lang=lang,
|
||||||
|
game=Game.ZZZ,
|
||||||
|
region=recognize_region(player_id, game=Game.ZZZ),
|
||||||
|
params=params,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def get_zzz_notes(
|
||||||
|
self,
|
||||||
|
player_id: Optional[int] = None,
|
||||||
|
lang: Optional[str] = None,
|
||||||
|
autoauth: bool = True,
|
||||||
|
) -> ZZZNote:
|
||||||
|
"""Get ZZZ's real-time notes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
player_id (Optional[int], optional): The player ID. Defaults to None.
|
||||||
|
lang (Optional[str], optional): The language of the data. Defaults to None.
|
||||||
|
autoauth (bool, optional): Whether to automatically authenticate the user. Defaults to True.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ZZZNote: The requested real-time notes.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BadRequest: If the request is invalid.
|
||||||
|
DataNotPublic: If the requested data is not public.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = await self._request_zzz_record("note", player_id, lang=lang)
|
||||||
|
except DataNotPublic as e:
|
||||||
|
# error raised only when real-time notes are not enabled
|
||||||
|
if player_id and self.player_id != player_id:
|
||||||
|
raise BadRequest(e.response, "Cannot view real-time notes of other users.") from e
|
||||||
|
if not autoauth:
|
||||||
|
raise BadRequest(e.response, "Real-time notes are not enabled.") from e
|
||||||
|
await self.update_settings(3, True, game=Game.ZZZ)
|
||||||
|
data = await self._request_zzz_record("note", player_id, lang=lang)
|
||||||
|
|
||||||
|
return ZZZNote(**data)
|
||||||
|
|
||||||
|
async def get_zzz_user(
|
||||||
|
self,
|
||||||
|
player_id: Optional[int] = None,
|
||||||
|
*,
|
||||||
|
lang: Optional[str] = None,
|
||||||
|
) -> "ZZZUserStats":
|
||||||
|
"""Get ZZZ user statistics.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
player_id (Optional[int], optional): The player ID. Defaults to None.
|
||||||
|
lang (Optional[str], optional): The language of the data. Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ZZZUserStats: The requested user statistics.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BadRequest: If the request is invalid.
|
||||||
|
DataNotPublic: If the requested data is not public.
|
||||||
|
"""
|
||||||
|
data = await self._request_zzz_record("index", player_id, lang=lang)
|
||||||
|
return ZZZUserStats(**data)
|
||||||
|
|
||||||
|
async def get_zzz_characters(
|
||||||
|
self,
|
||||||
|
player_id: Optional[int] = None,
|
||||||
|
lang: Optional[str] = None,
|
||||||
|
) -> "ZZZAvatarBasic":
|
||||||
|
"""Get ZZZ character basic information.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
player_id (Optional[int], optional): The player ID. Defaults to None.
|
||||||
|
lang (Optional[str], optional): The language of the data. Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ZZZAvatarBasic: The requested character information.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BadRequest: If the request is invalid.
|
||||||
|
DataNotPublic: If the requested data is not public.
|
||||||
|
"""
|
||||||
|
data = await self._request_zzz_record("avatar/basic", player_id, lang=lang)
|
||||||
|
return ZZZAvatarBasic(**data)
|
||||||
|
|
||||||
|
async def get_zzz_character_info(
|
||||||
|
self,
|
||||||
|
characters: List[int],
|
||||||
|
player_id: Optional[int] = None,
|
||||||
|
lang: Optional[str] = None,
|
||||||
|
) -> "ZZZCalculatorCharacterDetails":
|
||||||
|
"""Get ZZZ character detail information.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
characters (List[int]): A list of character IDs.
|
||||||
|
player_id (Optional[int], optional): The player ID. Defaults to None.
|
||||||
|
lang (Optional[str], optional): The language of the data. Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ZZZCalculatorCharacterDetails: The requested character information.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BadRequest: If the request is invalid.
|
||||||
|
DataNotPublic: If the requested data is not public.
|
||||||
|
"""
|
||||||
|
ch = characters
|
||||||
|
if isinstance(characters, int):
|
||||||
|
ch = [characters]
|
||||||
|
payload = {"need_wiki": "true", "id_list[]": ch}
|
||||||
|
data = await self._request_zzz_record("avatar/info", player_id, lang=lang, payload=payload)
|
||||||
|
return ZZZCalculatorCharacterDetails(**data)
|
||||||
|
|
||||||
|
async def get_zzz_buddy_list(
|
||||||
|
self,
|
||||||
|
player_id: Optional[int] = None,
|
||||||
|
lang: Optional[str] = None,
|
||||||
|
) -> "ZZZBuddyBasic":
|
||||||
|
"""Get ZZZ buddy basic information.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
player_id (Optional[int], optional): The player ID. Defaults to None.
|
||||||
|
lang (Optional[str], optional): The language of the data. Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ZZZBuddyBasic: The requested buddy information.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BadRequest: If the request is invalid.
|
||||||
|
DataNotPublic: If the requested data is not public.
|
||||||
|
"""
|
||||||
|
data = await self._request_zzz_record("buddy/info", player_id, lang=lang)
|
||||||
|
return ZZZBuddyBasic(**data)
|
||||||
|
|
||||||
|
async def get_record_card(
|
||||||
|
self,
|
||||||
|
account_id: Optional[int] = None,
|
||||||
|
*,
|
||||||
|
lang: Optional[str] = None,
|
||||||
|
) -> Optional[RecordCard]:
|
||||||
|
"""Get a zzz player record cards.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
account_id: Optional[int], the user's account ID, defaults to None
|
||||||
|
lang: Optional[str], the language version of the request, defaults to None
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Starrail user record cards.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[RecordCard]: RecordCard objects.
|
||||||
|
"""
|
||||||
|
account_id = account_id or self.account_id
|
||||||
|
|
||||||
|
record_cards = await self.get_record_cards(account_id, lang=lang)
|
||||||
|
|
||||||
|
for record_card in record_cards:
|
||||||
|
if record_card.game == Game.ZZZ:
|
||||||
|
return record_card
|
||||||
|
|
||||||
|
return None
|
@ -10,7 +10,7 @@ from simnet.client.routes import REWARD_URL
|
|||||||
from simnet.errors import GeetestTriggered
|
from simnet.errors import GeetestTriggered
|
||||||
from simnet.models.lab.daily import DailyRewardInfo, DailyReward, ClaimedDailyReward
|
from simnet.models.lab.daily import DailyRewardInfo, DailyReward, ClaimedDailyReward
|
||||||
from simnet.utils.enums import Game, Region
|
from simnet.utils.enums import Game, Region
|
||||||
from simnet.utils.player import recognize_genshin_server, recognize_starrail_server
|
from simnet.utils.player import recognize_genshin_server, recognize_starrail_server, recognize_zzz_server
|
||||||
|
|
||||||
__all__ = ("DailyRewardClient",)
|
__all__ = ("DailyRewardClient",)
|
||||||
|
|
||||||
@ -82,6 +82,14 @@ class DailyRewardClient(BaseClient):
|
|||||||
)
|
)
|
||||||
params = params.set("uid", self.player_id)
|
params = params.set("uid", self.player_id)
|
||||||
params = params.set("region", recognize_starrail_server(self.player_id))
|
params = params.set("region", recognize_starrail_server(self.player_id))
|
||||||
|
if self.game == Game.ZZZ:
|
||||||
|
headers["referer"] = (
|
||||||
|
"https://act.mihoyo.com/bbs/event/signin/zzz/e202406242138391.html?"
|
||||||
|
"act_id=e202406242138391&mhy_auth_required=true&mhy_presentation_style=fullscreen&"
|
||||||
|
"utm_source=bbs&utm_medium=zzz&utm_campaign=icon"
|
||||||
|
)
|
||||||
|
params = params.set("uid", self.player_id)
|
||||||
|
params = params.set("region", recognize_zzz_server(self.player_id))
|
||||||
|
|
||||||
url = base_url / endpoint
|
url = base_url / endpoint
|
||||||
|
|
||||||
|
@ -261,3 +261,12 @@ class LabClient(BaseClient):
|
|||||||
"""
|
"""
|
||||||
accounts = await self.get_game_accounts(lang=lang)
|
accounts = await self.get_game_accounts(lang=lang)
|
||||||
return [account for account in accounts if account.game == Game.STARRAIL]
|
return [account for account in accounts if account.game == Game.STARRAIL]
|
||||||
|
|
||||||
|
async def get_zzz_accounts(self, *, lang: Optional[str] = None) -> List[Account]:
|
||||||
|
"""Get the zzz accounts of the currently logged-in user.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Account]: A list of account info objects of zzz accounts.
|
||||||
|
"""
|
||||||
|
accounts = await self.get_game_accounts(lang=lang)
|
||||||
|
return [account for account in accounts if account.game == Game.ZZZ]
|
||||||
|
@ -47,6 +47,9 @@ class BaseWishClient(BaseClient):
|
|||||||
params["authkey"] = unquote(authkey)
|
params["authkey"] = unquote(authkey)
|
||||||
params["lang"] = create_short_lang_code(lang or self.lang)
|
params["lang"] = create_short_lang_code(lang or self.lang)
|
||||||
|
|
||||||
|
if game == Game.ZZZ:
|
||||||
|
params["real_gacha_type"] = params.get("gacha_type", "")
|
||||||
|
|
||||||
return await self.request_api("GET", url, params=params)
|
return await self.request_api("GET", url, params=params)
|
||||||
|
|
||||||
async def wish_history(
|
async def wish_history(
|
||||||
|
54
simnet/client/components/wish/zzz.py
Normal file
54
simnet/client/components/wish/zzz.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from functools import partial
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from simnet.client.components.wish.base import BaseWishClient
|
||||||
|
from simnet.models.zzz.wish import ZZZWish
|
||||||
|
from simnet.utils.enums import Game
|
||||||
|
from simnet.utils.paginator import WishPaginator
|
||||||
|
|
||||||
|
__all__ = ("ZZZWishClient",)
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZWishClient(BaseWishClient):
|
||||||
|
"""The ZZZWishClient class for making requests towards the Wish API."""
|
||||||
|
|
||||||
|
async def wish_history(
|
||||||
|
self,
|
||||||
|
banner_types: Optional[List[int]] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
lang: Optional[str] = None,
|
||||||
|
authkey: Optional[str] = None,
|
||||||
|
end_id: int = 0,
|
||||||
|
) -> List[ZZZWish]:
|
||||||
|
"""
|
||||||
|
Get the wish history for a list of banner types.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
banner_types (Optional[List[int]], optional): The banner types to get the wish history for.
|
||||||
|
limit (Optional[int] , optional): The maximum number of wishes to retrieve.
|
||||||
|
If not provided, all available wishes will be returned.
|
||||||
|
lang (Optional[str], optional): The language code to use for the request.
|
||||||
|
If not provided, the class default will be used.
|
||||||
|
authkey (Optional[str], optional): The authorization key for making the request.
|
||||||
|
end_id (int, optional): The ending ID of the last wish to retrieve.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[ZZZWish]: A list of ZZZWish objects representing the retrieved wishes.
|
||||||
|
"""
|
||||||
|
banner_types = banner_types or [1, 2, 3, 5]
|
||||||
|
if isinstance(banner_types, int):
|
||||||
|
banner_types = [banner_types]
|
||||||
|
wishes = []
|
||||||
|
for banner_type in banner_types:
|
||||||
|
paginator = WishPaginator(
|
||||||
|
end_id,
|
||||||
|
partial(
|
||||||
|
self.get_wish_page,
|
||||||
|
banner_type=banner_type,
|
||||||
|
game=Game.ZZZ,
|
||||||
|
authkey=authkey,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
items = await paginator.get(limit)
|
||||||
|
wishes.extend([ZZZWish(**i) for i in items])
|
||||||
|
return sorted(wishes, key=lambda wish: wish.time.timestamp())
|
@ -220,9 +220,17 @@ class GameRoute(BaseRoute):
|
|||||||
return self.urls[region][game]
|
return self.urls[region][game]
|
||||||
|
|
||||||
|
|
||||||
RECORD_URL = InternationalRoute(
|
RECORD_URL = GameRoute(
|
||||||
overseas="https://bbs-api-os.hoyolab.com/game_record",
|
overseas=dict(
|
||||||
chinese="https://api-takumi-record.mihoyo.com/game_record/app",
|
genshin="https://bbs-api-os.hoyolab.com/game_record",
|
||||||
|
hkrpg="https://bbs-api-os.hoyolab.com/game_record",
|
||||||
|
nap="https://sg-act-nap-api.hoyolab.com/event/game_record_zzz",
|
||||||
|
),
|
||||||
|
chinese=dict(
|
||||||
|
genshin="https://api-takumi-record.mihoyo.com/game_record/app",
|
||||||
|
hkrpg="https://api-takumi-record.mihoyo.com/game_record/app",
|
||||||
|
nap="https://api-takumi-record.mihoyo.com/event/game_record_zzz",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
GACHA_INFO_URL = GameRoute(
|
GACHA_INFO_URL = GameRoute(
|
||||||
@ -233,6 +241,7 @@ GACHA_INFO_URL = GameRoute(
|
|||||||
chinese=dict(
|
chinese=dict(
|
||||||
genshin="https://public-operation-hk4e.mihoyo.com/gacha_info/api",
|
genshin="https://public-operation-hk4e.mihoyo.com/gacha_info/api",
|
||||||
hkrpg="https://api-takumi.mihoyo.com/common/gacha_record/api",
|
hkrpg="https://api-takumi.mihoyo.com/common/gacha_record/api",
|
||||||
|
nap="https://public-operation-nap.mihoyo.com/common/gacha_record/api",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -264,11 +273,13 @@ REWARD_URL = GameRoute(
|
|||||||
genshin="https://sg-hk4e-api.hoyolab.com/event/sol/?act_id=e202102251931481",
|
genshin="https://sg-hk4e-api.hoyolab.com/event/sol/?act_id=e202102251931481",
|
||||||
honkai3rd="https://sg-public-api.hoyolab.com/event/mani/?act_id=e202110291205111",
|
honkai3rd="https://sg-public-api.hoyolab.com/event/mani/?act_id=e202110291205111",
|
||||||
hkrpg="https://sg-public-api.hoyolab.com/event/luna/os/?act_id=e202303301540311",
|
hkrpg="https://sg-public-api.hoyolab.com/event/luna/os/?act_id=e202303301540311",
|
||||||
|
nap="https://sg-act-nap-api.hoyolab.com/event/luna/zzz/os/?act_id=e202406031448091",
|
||||||
),
|
),
|
||||||
chinese=dict(
|
chinese=dict(
|
||||||
genshin="https://api-takumi.mihoyo.com/event/luna/?act_id=e202311201442471",
|
genshin="https://api-takumi.mihoyo.com/event/luna/?act_id=e202311201442471",
|
||||||
honkai3rd="https://api-takumi.mihoyo.com/event/luna/?act_id=e202207181446311",
|
honkai3rd="https://api-takumi.mihoyo.com/event/luna/?act_id=e202207181446311",
|
||||||
hkrpg="https://api-takumi.mihoyo.com/event/luna/?act_id=e202304121516551",
|
hkrpg="https://api-takumi.mihoyo.com/event/luna/?act_id=e202304121516551",
|
||||||
|
nap="https://act-nap-api.mihoyo.com/event/luna/zzz/?act_id=e202406242138391",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
TAKUMI_URL = InternationalRoute(
|
TAKUMI_URL = InternationalRoute(
|
||||||
@ -321,6 +332,7 @@ CODE_HOYOLAB_URL = GameRoute(
|
|||||||
overseas=dict(
|
overseas=dict(
|
||||||
genshin="https://sg-hk4e-api.hoyolab.com/common/apicdkey/api/webExchangeCdkeyHyl",
|
genshin="https://sg-hk4e-api.hoyolab.com/common/apicdkey/api/webExchangeCdkeyHyl",
|
||||||
hkrpg="https://sg-hkrpg-api.hoyolab.com/common/apicdkey/api/webExchangeCdkeyHyl",
|
hkrpg="https://sg-hkrpg-api.hoyolab.com/common/apicdkey/api/webExchangeCdkeyHyl",
|
||||||
|
nap="https://public-operation-nap.hoyoverse.com/common/apicdkey/api/webExchangeCdkey",
|
||||||
),
|
),
|
||||||
chinese={},
|
chinese={},
|
||||||
)
|
)
|
||||||
|
24
simnet/client/zzz.py
Normal file
24
simnet/client/zzz.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from simnet.client.components.auth import AuthClient
|
||||||
|
from simnet.client.components.chronicle.zzz import ZZZBattleChronicleClient
|
||||||
|
from simnet.client.components.daily import DailyRewardClient
|
||||||
|
from simnet.client.components.lab import LabClient
|
||||||
|
from simnet.client.components.verify import VerifyClient
|
||||||
|
from simnet.client.components.wish.zzz import ZZZWishClient
|
||||||
|
from simnet.utils.enums import Game
|
||||||
|
|
||||||
|
__all__ = ("ZZZClient",)
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZClient(
|
||||||
|
ZZZBattleChronicleClient,
|
||||||
|
ZZZWishClient,
|
||||||
|
DailyRewardClient,
|
||||||
|
AuthClient,
|
||||||
|
LabClient,
|
||||||
|
VerifyClient,
|
||||||
|
):
|
||||||
|
"""A simple http client for StarRail endpoints."""
|
||||||
|
|
||||||
|
game: Optional[Game] = Game.ZZZ
|
@ -58,6 +58,8 @@ class Account(APIModel):
|
|||||||
return Game.HONKAI
|
return Game.HONKAI
|
||||||
if "hkrpg" in self.game_biz:
|
if "hkrpg" in self.game_biz:
|
||||||
return Game.STARRAIL
|
return Game.STARRAIL
|
||||||
|
if "nap" in self.game_biz:
|
||||||
|
return Game.ZZZ
|
||||||
try:
|
try:
|
||||||
return Game(self.game_biz)
|
return Game(self.game_biz)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
@ -420,6 +422,57 @@ class StarRailRecodeCard(RecordCard):
|
|||||||
return int(self.data[3].value)
|
return int(self.data[3].value)
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZRecodeCard(RecordCard):
|
||||||
|
"""ZZZ record card."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def game(self) -> Game:
|
||||||
|
"""Returns the game associated with the record card.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Game: The game associated with the record card.
|
||||||
|
"""
|
||||||
|
return Game.ZZZ
|
||||||
|
|
||||||
|
@property
|
||||||
|
def days_active(self) -> int:
|
||||||
|
"""Returns the number of days the user has been active.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The number of days the user has been active.
|
||||||
|
"""
|
||||||
|
return int(self.data[0].value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def world_level_name(self) -> str:
|
||||||
|
"""
|
||||||
|
Returns the user's world level name.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The user's world level name.
|
||||||
|
"""
|
||||||
|
return self.data[1].value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def characters(self) -> int:
|
||||||
|
"""Returns the number of characters the user has.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The number of characters the user has.
|
||||||
|
"""
|
||||||
|
return int(self.data[2].value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def buddies(self) -> int:
|
||||||
|
"""Returns the number of buddies the user has found.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The number of buddies the user has found.
|
||||||
|
"""
|
||||||
|
return int(self.data[3].value)
|
||||||
|
|
||||||
|
|
||||||
RECORD_CARD_MAP.setdefault(1, HonkaiRecordCard)
|
RECORD_CARD_MAP.setdefault(1, HonkaiRecordCard)
|
||||||
RECORD_CARD_MAP.setdefault(2, GenshinRecordCard)
|
RECORD_CARD_MAP.setdefault(2, GenshinRecordCard)
|
||||||
RECORD_CARD_MAP.setdefault(6, StarRailRecodeCard)
|
RECORD_CARD_MAP.setdefault(6, StarRailRecodeCard)
|
||||||
|
RECORD_CARD_MAP.setdefault(8, ZZZRecodeCard)
|
||||||
|
0
simnet/models/zzz/__init__.py
Normal file
0
simnet/models/zzz/__init__.py
Normal file
69
simnet/models/zzz/calculator.py
Normal file
69
simnet/models/zzz/calculator.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from simnet.models.base import APIModel
|
||||||
|
from simnet.models.zzz.character import ZZZPartialCharacter
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZCalculatorWeaponProperty(APIModel):
|
||||||
|
|
||||||
|
property_name: str
|
||||||
|
property_id: int
|
||||||
|
base: str
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZCalculatorWeapon(APIModel):
|
||||||
|
|
||||||
|
id: int
|
||||||
|
level: int
|
||||||
|
name: str
|
||||||
|
star: int
|
||||||
|
icon: str
|
||||||
|
rarity: str
|
||||||
|
properties: List[ZZZCalculatorWeaponProperty]
|
||||||
|
main_properties: List[ZZZCalculatorWeaponProperty]
|
||||||
|
talent_title: str
|
||||||
|
talent_content: str
|
||||||
|
profession: int
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZCalculatorAvatarProperty(ZZZCalculatorWeaponProperty):
|
||||||
|
|
||||||
|
add: str
|
||||||
|
final: str
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZCalculatorAvatarSkillItem(APIModel):
|
||||||
|
title: str
|
||||||
|
text: str
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZCalculatorAvatarSkill(APIModel):
|
||||||
|
|
||||||
|
level: int
|
||||||
|
skill_type: int
|
||||||
|
items: List[ZZZCalculatorAvatarSkillItem]
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZCalculatorAvatarRank(APIModel):
|
||||||
|
|
||||||
|
id: int
|
||||||
|
name: str
|
||||||
|
desc: str
|
||||||
|
pos: int
|
||||||
|
is_unlocked: bool
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZCalculatorCharacter(ZZZPartialCharacter):
|
||||||
|
|
||||||
|
equip: List
|
||||||
|
weapon: ZZZCalculatorWeapon
|
||||||
|
properties: List[ZZZCalculatorAvatarProperty]
|
||||||
|
skills: List[ZZZCalculatorAvatarSkill]
|
||||||
|
ranks: List[ZZZCalculatorAvatarRank]
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZCalculatorCharacterDetails(APIModel):
|
||||||
|
|
||||||
|
characters: List[ZZZCalculatorCharacter] = Field(alias="avatar_list")
|
36
simnet/models/zzz/character.py
Normal file
36
simnet/models/zzz/character.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
"""Starrail base character model."""
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from simnet.models.base import APIModel
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZBaseCharacter(APIModel):
|
||||||
|
"""Base character model."""
|
||||||
|
|
||||||
|
id: int
|
||||||
|
element_type: int
|
||||||
|
rarity: str
|
||||||
|
group_icon_path: str
|
||||||
|
hollow_icon_path: str
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZPartialCharacter(ZZZBaseCharacter):
|
||||||
|
"""Character without any equipment."""
|
||||||
|
|
||||||
|
name: str = Field(alias="name_mi18n")
|
||||||
|
full_name: str = Field(alias="full_name_mi18n")
|
||||||
|
camp_name: str = Field(alias="camp_name_mi18n")
|
||||||
|
avatar_profession: int
|
||||||
|
level: int
|
||||||
|
rank: int
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZBaseBuddy(APIModel):
|
||||||
|
"""Base Buddy model."""
|
||||||
|
|
||||||
|
id: int
|
||||||
|
name: str
|
||||||
|
rarity: str
|
||||||
|
level: int
|
||||||
|
star: int
|
0
simnet/models/zzz/chronicle/__init__.py
Normal file
0
simnet/models/zzz/chronicle/__init__.py
Normal file
110
simnet/models/zzz/chronicle/notes.py
Normal file
110
simnet/models/zzz/chronicle/notes.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import datetime
|
||||||
|
import enum
|
||||||
|
|
||||||
|
from simnet.models.base import APIModel
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZNoteProgress(APIModel):
|
||||||
|
"""
|
||||||
|
Represents the progress of the user.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
max (int): The maximum progress of the user.
|
||||||
|
current (int): The current progress of the user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
max: int
|
||||||
|
current: int
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZNoteEnergy(APIModel):
|
||||||
|
"""
|
||||||
|
Represents the energy of the user.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
progress (ZZZNoteProgress): The progress of the progress
|
||||||
|
restore (int): The restore of the progress
|
||||||
|
"""
|
||||||
|
|
||||||
|
progress: ZZZNoteProgress
|
||||||
|
restore: datetime.timedelta
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZNoteVitality(APIModel):
|
||||||
|
"""
|
||||||
|
Represents the vitality of the user.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
max (int): The maximum vitality of the user.
|
||||||
|
current (int): The current vitality of the user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
max: int
|
||||||
|
current: int
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZNoteVhsSaleState(str, enum.Enum):
|
||||||
|
"""
|
||||||
|
Represents the state of the vhs sale of the user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
FREE = "SaleStateNo"
|
||||||
|
DOING = "SaleStateDoing"
|
||||||
|
DONE = "SaleStateDone"
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZNoteVhsSale(APIModel):
|
||||||
|
"""
|
||||||
|
Represents the vhs sale of the user.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
sale_state (ZZZNoteVhsSaleState): The state of the vhs sale of the user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
sale_state: ZZZNoteVhsSaleState
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZNoteCardSignState(str, enum.Enum):
|
||||||
|
"""
|
||||||
|
Represents the state of the card sign of the user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
FREE = "CardSignNo"
|
||||||
|
DONE = "CardSignDone"
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZNote(APIModel):
|
||||||
|
"""Represents a ZZZ Note.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
energy (ZZZNoteEnergy): The energy of the user.
|
||||||
|
vitality (ZZZNoteVitality): The vitality of the user.
|
||||||
|
vhs_sale (ZZZNoteVhsSale): The vhs sale of the user.
|
||||||
|
card_sign (ZZZNoteCardSignState): The card sign of the user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
energy: ZZZNoteEnergy
|
||||||
|
vitality: ZZZNoteVitality
|
||||||
|
vhs_sale: ZZZNoteVhsSale
|
||||||
|
card_sign: ZZZNoteCardSignState
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_stamina(self) -> int:
|
||||||
|
return self.energy.progress.current
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_stamina(self) -> int:
|
||||||
|
return self.energy.progress.max
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stamina_recover_time(self) -> datetime:
|
||||||
|
"""A property that returns the time when resin will be fully recovered."""
|
||||||
|
return datetime.datetime.now().astimezone() + self.energy.restore
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_train_score(self) -> int:
|
||||||
|
return self.vitality.current
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_train_score(self) -> int:
|
||||||
|
return self.vitality.max
|
38
simnet/models/zzz/chronicle/stats.py
Normal file
38
simnet/models/zzz/chronicle/stats.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
"""Starrail chronicle stats."""
|
||||||
|
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from simnet.models.base import APIModel
|
||||||
|
from simnet.models.zzz import character
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZStats(APIModel):
|
||||||
|
"""Overall user stats."""
|
||||||
|
|
||||||
|
active_days: int
|
||||||
|
avatar_num: int
|
||||||
|
world_level_name: str
|
||||||
|
cur_period_zone_layer_count: int
|
||||||
|
buddy_num: int
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZAvatarBasic(APIModel):
|
||||||
|
"""Basic avatar"""
|
||||||
|
|
||||||
|
characters: typing.Sequence[character.ZZZPartialCharacter] = Field(alias="avatar_list")
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZBuddyBasic(APIModel):
|
||||||
|
"""Basic buddy"""
|
||||||
|
|
||||||
|
buddy_list: typing.Sequence[character.ZZZBaseBuddy] = Field(alias="list")
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZUserStats(ZZZAvatarBasic):
|
||||||
|
"""User stats with characters without equipment."""
|
||||||
|
|
||||||
|
stats: ZZZStats
|
||||||
|
cur_head_icon_url: str
|
||||||
|
buddy_list: typing.Sequence[character.ZZZBaseBuddy]
|
64
simnet/models/zzz/wish.py
Normal file
64
simnet/models/zzz/wish.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from enum import IntEnum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pydantic import Field, validator
|
||||||
|
|
||||||
|
from simnet.models.base import APIModel
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZBannerType(IntEnum):
|
||||||
|
"""Banner types in wish histories."""
|
||||||
|
|
||||||
|
STANDARD = PERMANENT = NOVICE = 1
|
||||||
|
"""Permanent standard banner."""
|
||||||
|
|
||||||
|
CHARACTER = 2
|
||||||
|
"""Rotating character banner."""
|
||||||
|
|
||||||
|
WEAPON = 3
|
||||||
|
"""Rotating weapon banner."""
|
||||||
|
|
||||||
|
BANGBOO = 5
|
||||||
|
"""BangBoo banner."""
|
||||||
|
|
||||||
|
|
||||||
|
class ZZZWish(APIModel):
|
||||||
|
"""Wish made on any banner."""
|
||||||
|
|
||||||
|
uid: int
|
||||||
|
"""User ID of the wish maker."""
|
||||||
|
|
||||||
|
id: int
|
||||||
|
"""ID of the wished item."""
|
||||||
|
|
||||||
|
type: str = Field(alias="item_type")
|
||||||
|
"""Type of the wished item."""
|
||||||
|
|
||||||
|
item_id: int = Field(alias="item_id")
|
||||||
|
"""ID of the wished item."""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""Name of the wished item."""
|
||||||
|
|
||||||
|
rarity: int = Field(alias="rank_type")
|
||||||
|
"""Rarity of the wished item."""
|
||||||
|
|
||||||
|
time: datetime
|
||||||
|
"""Time when the wish was made."""
|
||||||
|
|
||||||
|
banner_id: int = Field(alias="gacha_id")
|
||||||
|
"""ID of the banner the wish was made on."""
|
||||||
|
|
||||||
|
banner_type: ZZZBannerType = Field(alias="gacha_type")
|
||||||
|
"""Type of the banner the wish was made on."""
|
||||||
|
|
||||||
|
@validator("banner_type", pre=True)
|
||||||
|
def cast_banner_type(cls, v: Any) -> int:
|
||||||
|
"""Converts the banner type from any type to int."""
|
||||||
|
return int(v)
|
||||||
|
|
||||||
|
@validator("rarity")
|
||||||
|
def add_rarity(cls, v: int) -> int:
|
||||||
|
"""Add rarity 1."""
|
||||||
|
return v + 1
|
@ -24,8 +24,10 @@ class Game(str, _enum.Enum):
|
|||||||
GENSHIN (Game): Represents the game "Genshin Impact".
|
GENSHIN (Game): Represents the game "Genshin Impact".
|
||||||
HONKAI (Game): Represents the game "Honkai Impact 3rd".
|
HONKAI (Game): Represents the game "Honkai Impact 3rd".
|
||||||
STARRAIL (Game): Represents the game "Honkai Impact 3rd RPG".
|
STARRAIL (Game): Represents the game "Honkai Impact 3rd RPG".
|
||||||
|
ZZZ (Game): Represents the game "Zenless Zone Zero".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
GENSHIN = "genshin"
|
GENSHIN = "genshin"
|
||||||
HONKAI = "honkai3rd"
|
HONKAI = "honkai3rd"
|
||||||
STARRAIL = "hkrpg"
|
STARRAIL = "hkrpg"
|
||||||
|
ZZZ = "nap"
|
||||||
|
@ -50,7 +50,7 @@ class WishPaginator:
|
|||||||
if limit and len(all_items) >= limit:
|
if limit and len(all_items) >= limit:
|
||||||
break
|
break
|
||||||
|
|
||||||
await asyncio.sleep(0.5)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
# Return up to the specified limit.
|
# Return up to the specified limit.
|
||||||
return all_items[: min(len(all_items), limit)] if limit else all_items
|
return all_items[: min(len(all_items), limit)] if limit else all_items
|
||||||
|
@ -8,6 +8,7 @@ UID_LENGTH: Mapping[Game, int] = {
|
|||||||
Game.GENSHIN: 9,
|
Game.GENSHIN: 9,
|
||||||
Game.STARRAIL: 9,
|
Game.STARRAIL: 9,
|
||||||
Game.HONKAI: 8,
|
Game.HONKAI: 8,
|
||||||
|
Game.ZZZ: 9,
|
||||||
}
|
}
|
||||||
UID_RANGE: Mapping[Game, Mapping[Region, Sequence[int]]] = {
|
UID_RANGE: Mapping[Game, Mapping[Region, Sequence[int]]] = {
|
||||||
Game.GENSHIN: {
|
Game.GENSHIN: {
|
||||||
@ -22,6 +23,9 @@ UID_RANGE: Mapping[Game, Mapping[Region, Sequence[int]]] = {
|
|||||||
Region.OVERSEAS: (1, 2),
|
Region.OVERSEAS: (1, 2),
|
||||||
Region.CHINESE: (3, 4),
|
Region.CHINESE: (3, 4),
|
||||||
},
|
},
|
||||||
|
Game.ZZZ: {
|
||||||
|
Region.OVERSEAS: (13,),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -104,6 +108,33 @@ def recognize_starrail_server(player_id: int) -> str:
|
|||||||
raise ValueError(f"player id {player_id} isn't associated with any server")
|
raise ValueError(f"player id {player_id} isn't associated with any server")
|
||||||
|
|
||||||
|
|
||||||
|
def recognize_zzz_server(player_id: int) -> str:
|
||||||
|
"""Recognize which server a ZZZ UID is from.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
player_id (int): The player ID to recognize the server for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The name of the server associated with the given player ID.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the player ID is not associated with any server.
|
||||||
|
"""
|
||||||
|
if len(str(player_id)) == 8:
|
||||||
|
return "prod_gf_cn"
|
||||||
|
server = {
|
||||||
|
10: "prod_gf_us",
|
||||||
|
15: "prod_gf_eu",
|
||||||
|
13: "prod_gf_jp",
|
||||||
|
17: "prod_gf_sg",
|
||||||
|
}.get(recognize_game_uid_first_digit(player_id, Game.ZZZ))
|
||||||
|
|
||||||
|
if server:
|
||||||
|
return server
|
||||||
|
|
||||||
|
raise ValueError(f"player id {player_id} isn't associated with any server")
|
||||||
|
|
||||||
|
|
||||||
def recognize_region(player_id: int, game: Game) -> Optional[Region]:
|
def recognize_region(player_id: int, game: Game) -> Optional[Region]:
|
||||||
"""
|
"""
|
||||||
Recognizes the region of a player ID for a given game.
|
Recognizes the region of a player ID for a given game.
|
||||||
@ -115,6 +146,9 @@ def recognize_region(player_id: int, game: Game) -> Optional[Region]:
|
|||||||
Returns:
|
Returns:
|
||||||
Optional[Region]: The region the player ID belongs to if it can be recognized, None otherwise.
|
Optional[Region]: The region the player ID belongs to if it can be recognized, None otherwise.
|
||||||
"""
|
"""
|
||||||
|
if game == Game.ZZZ and len(str(player_id)) == 8:
|
||||||
|
return Region.CHINESE
|
||||||
|
|
||||||
for region, digits in UID_RANGE[game].items():
|
for region, digits in UID_RANGE[game].items():
|
||||||
first = recognize_game_uid_first_digit(player_id, game)
|
first = recognize_game_uid_first_digit(player_id, game)
|
||||||
if first in digits:
|
if first in digits:
|
||||||
@ -141,6 +175,8 @@ def recognize_server(player_id: int, game: Game) -> str:
|
|||||||
return recognize_genshin_server(player_id)
|
return recognize_genshin_server(player_id)
|
||||||
if game == Game.STARRAIL:
|
if game == Game.STARRAIL:
|
||||||
return recognize_starrail_server(player_id)
|
return recognize_starrail_server(player_id)
|
||||||
|
if game == Game.ZZZ:
|
||||||
|
return recognize_zzz_server(player_id)
|
||||||
raise ValueError(f"{game} is not a valid game")
|
raise ValueError(f"{game} is not a valid game")
|
||||||
|
|
||||||
|
|
||||||
@ -162,6 +198,15 @@ def recognize_starrail_game_biz(game_uid: int) -> str:
|
|||||||
return "hkrpg_cn" if game_uid < 600000000 else "hkrpg_global"
|
return "hkrpg_cn" if game_uid < 600000000 else "hkrpg_global"
|
||||||
|
|
||||||
|
|
||||||
|
def recognize_zzz_game_biz(game_uid: int) -> str:
|
||||||
|
"""Recognizes the game biz of a player ID for a game biz.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The game biz the player ID belongs to.
|
||||||
|
"""
|
||||||
|
return "nap_cn" if len(str(game_uid)) == 8 else "nap_global"
|
||||||
|
|
||||||
|
|
||||||
def recognize_game_biz(player_id: int, game: Game) -> str:
|
def recognize_game_biz(player_id: int, game: Game) -> str:
|
||||||
"""
|
"""
|
||||||
Recognizes the game biz of a player ID for a given game.
|
Recognizes the game biz of a player ID for a given game.
|
||||||
@ -180,4 +225,6 @@ def recognize_game_biz(player_id: int, game: Game) -> str:
|
|||||||
return recognize_genshin_game_biz(player_id)
|
return recognize_genshin_game_biz(player_id)
|
||||||
if game == Game.STARRAIL:
|
if game == Game.STARRAIL:
|
||||||
return recognize_starrail_game_biz(player_id)
|
return recognize_starrail_game_biz(player_id)
|
||||||
|
if game == Game.ZZZ:
|
||||||
|
return recognize_zzz_game_biz(player_id)
|
||||||
raise ValueError(f"{game} is not a valid game")
|
raise ValueError(f"{game} is not a valid game")
|
||||||
|
Loading…
Reference in New Issue
Block a user