mirror of
https://github.com/PaiGramTeam/SIMNet.git
synced 2024-11-22 06:17:57 +00:00
🐛 Fix issues and improve functionality
- Fix issue with Daily Reward Client not running - Update Wish Client to support multiple banner retrieval - Fix issue with setting salt for dynamic secret - Improve APIModel to support multiple aliases - Add asyncio.sleep() to Wish paginator to prevent excessive requests Co-authored-by: xtaodada <xtao@xtaolink.cn>
This commit is contained in:
parent
5d2d3609bb
commit
4250481727
@ -8,7 +8,7 @@ from httpx import AsyncClient, TimeoutException, Response, HTTPError, Timeout
|
|||||||
from simnet.client.cookies import Cookies
|
from simnet.client.cookies import Cookies
|
||||||
from simnet.client.headers import Headers
|
from simnet.client.headers import Headers
|
||||||
from simnet.errors import TimedOut, NetworkError, BadRequest, raise_for_ret_code
|
from simnet.errors import TimedOut, NetworkError, BadRequest, raise_for_ret_code
|
||||||
from simnet.utils.ds import generate_dynamic_secret
|
from simnet.utils.ds import generate_dynamic_secret, DSType
|
||||||
from simnet.utils.enum_ import Region, Game
|
from simnet.utils.enum_ import Region, Game
|
||||||
from simnet.utils.types import (
|
from simnet.utils.types import (
|
||||||
RT,
|
RT,
|
||||||
@ -187,7 +187,7 @@ class BaseClient(AsyncContextManager["BaseClient"]):
|
|||||||
header (HeaderTypes): The header to use.
|
header (HeaderTypes): The header to use.
|
||||||
lang (Optional[str], optional): The language to use for overseas regions. Defaults to None.
|
lang (Optional[str], optional): The language to use for overseas regions. Defaults to None.
|
||||||
ds (str, optional): The DS string to use. Defaults to None.
|
ds (str, optional): The DS string to use. Defaults to None.
|
||||||
ds_type (str, optional): The DS type to use. Defaults to None.
|
ds_type (Optional[DSType], optional): The DS type to use. Defaults to None.
|
||||||
new_ds (bool, optional): Whether to generate a new DS. Defaults to False.
|
new_ds (bool, optional): Whether to generate a new DS. Defaults to False.
|
||||||
data (Any, optional): The data to use. Defaults to None.
|
data (Any, optional): The data to use. Defaults to None.
|
||||||
params (Optional[QueryParamTypes], optional): The query parameters to use. Defaults to None.
|
params (Optional[QueryParamTypes], optional): The query parameters to use. Defaults to None.
|
||||||
@ -309,7 +309,7 @@ class BaseClient(AsyncContextManager["BaseClient"]):
|
|||||||
headers: Optional[HeaderTypes] = None,
|
headers: Optional[HeaderTypes] = None,
|
||||||
lang: Optional[str] = None,
|
lang: Optional[str] = None,
|
||||||
new_ds: bool = False,
|
new_ds: bool = False,
|
||||||
ds_type: str = None,
|
ds_type: Optional[DSType] = None,
|
||||||
):
|
):
|
||||||
"""Make a request to the lab API and return the data.
|
"""Make a request to the lab API and return the data.
|
||||||
|
|
||||||
@ -325,7 +325,7 @@ class BaseClient(AsyncContextManager["BaseClient"]):
|
|||||||
headers (Optional[HeaderTypes]): The headers to include in the request.
|
headers (Optional[HeaderTypes]): The headers to include in the request.
|
||||||
lang (Optional[str]): The language of the request (e.g., "en", "zh").
|
lang (Optional[str]): The language of the request (e.g., "en", "zh").
|
||||||
new_ds (bool): Whether to use a new dataset for the request.
|
new_ds (bool): Whether to use a new dataset for the request.
|
||||||
ds_type (str): The type of dataset to use for the request (e.g., "news", "qa").
|
ds_type (Optional[DSType]): The type of dataset to use for the request (e.g., "news", "qa").
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Any: The data returned by the lab API.
|
Any: The data returned by the lab API.
|
||||||
|
@ -218,15 +218,19 @@ class GenshinBattleChronicleClient(BaseChronicleClient):
|
|||||||
Returns:
|
Returns:
|
||||||
Character: The requested genshin user with all their possible data.
|
Character: The requested genshin user with all their possible data.
|
||||||
"""
|
"""
|
||||||
user, abyss1, abyss2, activities = await asyncio.gather(
|
index, character, abyss1, abyss2, activities = await asyncio.gather(
|
||||||
self.get_genshin_user(player_id, lang=lang),
|
self._request_genshin_record("index", player_id, lang=lang),
|
||||||
|
self._request_genshin_record(
|
||||||
|
"character", player_id, lang=lang, method="POST"
|
||||||
|
),
|
||||||
self.get_genshin_spiral_abyss(player_id, lang=lang, previous=False),
|
self.get_genshin_spiral_abyss(player_id, lang=lang, previous=False),
|
||||||
self.get_genshin_spiral_abyss(player_id, lang=lang, previous=True),
|
self.get_genshin_spiral_abyss(player_id, lang=lang, previous=True),
|
||||||
self.get_genshin_activities(player_id, lang=lang),
|
self.get_genshin_activities(player_id, lang=lang),
|
||||||
)
|
)
|
||||||
|
user = {**index, **character}
|
||||||
abyss = SpiralAbyssPair(current=abyss1, previous=abyss2)
|
abyss = SpiralAbyssPair(current=abyss1, previous=abyss2)
|
||||||
|
|
||||||
return FullGenshinUserStats(**user.dict(), abyss=abyss, activities=activities)
|
return FullGenshinUserStats(**user, abyss=abyss, activities=activities)
|
||||||
|
|
||||||
async def get_genshin_activities(
|
async def get_genshin_activities(
|
||||||
self, player_id: Optional[int] = None, *, lang: Optional[str] = None
|
self, player_id: Optional[int] = None, *, lang: Optional[str] = None
|
||||||
|
@ -2,11 +2,14 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from typing import Optional, Dict, Any, List
|
from typing import Optional, Dict, Any, List
|
||||||
|
|
||||||
|
from httpx import QueryParams
|
||||||
|
|
||||||
from simnet.client.base import BaseClient
|
from simnet.client.base import BaseClient
|
||||||
from simnet.utils.ds import hex_digest
|
|
||||||
from simnet.client.routes import REWARD_URL
|
from simnet.client.routes import REWARD_URL
|
||||||
from simnet.models.lab.daily import DailyRewardInfo, DailyReward, ClaimedDailyReward
|
from simnet.models.lab.daily import DailyRewardInfo, DailyReward, ClaimedDailyReward
|
||||||
|
from simnet.utils.ds import hex_digest
|
||||||
from simnet.utils.enum_ import Game, Region
|
from simnet.utils.enum_ import Game, Region
|
||||||
|
from simnet.utils.player import recognize_genshin_server, recognize_starrail_server
|
||||||
|
|
||||||
__all__ = ("DailyRewardClient",)
|
__all__ = ("DailyRewardClient",)
|
||||||
|
|
||||||
@ -40,19 +43,18 @@ class DailyRewardClient(BaseClient):
|
|||||||
Returns:
|
Returns:
|
||||||
A dictionary containing the response data.
|
A dictionary containing the response data.
|
||||||
"""
|
"""
|
||||||
new_ds: bool = False
|
|
||||||
headers: Optional[Dict[str, str]] = None
|
headers: Optional[Dict[str, str]] = None
|
||||||
|
params = QueryParams(params)
|
||||||
|
|
||||||
|
base_url = REWARD_URL.get_url(self.region, self.game or game)
|
||||||
|
params = params.merge(base_url.params)
|
||||||
|
|
||||||
if self.region == Region.CHINESE:
|
if self.region == Region.CHINESE:
|
||||||
headers = {}
|
headers = {}
|
||||||
if challenge is not None and validate is not None:
|
if challenge is not None and validate is not None:
|
||||||
headers["x-rpc-challenge"] = challenge
|
headers["x-rpc-challenge"] = challenge
|
||||||
headers["x-rpc-validate"] = validate
|
headers["x-rpc-validate"] = validate
|
||||||
headers["x-rpc-seccode"] = f"{validate}|jordan"
|
headers["x-rpc-seccode"] = f"{validate}|jordan"
|
||||||
headers["Referer"] = (
|
|
||||||
"https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?"
|
|
||||||
"bbs_auth_required=true&act_id=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon"
|
|
||||||
)
|
|
||||||
|
|
||||||
headers["x-rpc-device_name"] = "Chrome 20 2023"
|
headers["x-rpc-device_name"] = "Chrome 20 2023"
|
||||||
headers["x-rpc-channel"] = "chrome"
|
headers["x-rpc-channel"] = "chrome"
|
||||||
headers["x-rpc-device_model"] = "Chrome 2023"
|
headers["x-rpc-device_model"] = "Chrome 2023"
|
||||||
@ -61,13 +63,31 @@ class DailyRewardClient(BaseClient):
|
|||||||
device_id = self.device_id
|
device_id = self.device_id
|
||||||
hash_value = hex_digest(device_id)
|
hash_value = hex_digest(device_id)
|
||||||
headers["x-rpc-device_fp"] = hash_value[:13]
|
headers["x-rpc-device_fp"] = hash_value[:13]
|
||||||
new_ds = endpoint == "sign"
|
if self.game == Game.GENSHIN:
|
||||||
|
headers["referer"] = (
|
||||||
|
"https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?"
|
||||||
|
"bbs_auth_required=true&act_id=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon"
|
||||||
|
)
|
||||||
|
params = params.set("uid", self.player_id)
|
||||||
|
params = params.set("region", recognize_genshin_server(self.player_id))
|
||||||
|
if self.game == Game.STARRAIL:
|
||||||
|
headers["referer"] = (
|
||||||
|
"https://webstatic.mihoyo.com/bbs/event/signin/hkrpg/index.html?"
|
||||||
|
"bbs_auth_required=true&act_id=e202304121516551&"
|
||||||
|
"bbs_auth_required=true&bbs_presentation_style=fullscreen&"
|
||||||
|
"utm_source=bbs&utm_medium=mys&utm_campaign=icon"
|
||||||
|
)
|
||||||
|
params = params.set("uid", self.player_id)
|
||||||
|
params = params.set("region", recognize_starrail_server(self.player_id))
|
||||||
|
|
||||||
base_url = REWARD_URL.get_url(self.region, self.game or game)
|
url = base_url / endpoint
|
||||||
url = (base_url / endpoint).update_query(**base_url.query)
|
|
||||||
|
|
||||||
return await self.request_lab(
|
return await self.request_lab(
|
||||||
method, url, params=params, headers=headers, lang=lang, new_ds=new_ds
|
url,
|
||||||
|
method,
|
||||||
|
params=params,
|
||||||
|
headers=headers,
|
||||||
|
lang=lang,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_reward_info(
|
async def get_reward_info(
|
||||||
@ -105,7 +125,7 @@ class DailyRewardClient(BaseClient):
|
|||||||
"""
|
"""
|
||||||
data = await self.request_daily_reward(
|
data = await self.request_daily_reward(
|
||||||
"home",
|
"home",
|
||||||
game=game,
|
game=game or self.game,
|
||||||
lang=lang,
|
lang=lang,
|
||||||
)
|
)
|
||||||
return [DailyReward(**i) for i in data["awards"]]
|
return [DailyReward(**i) for i in data["awards"]]
|
||||||
@ -129,7 +149,7 @@ class DailyRewardClient(BaseClient):
|
|||||||
page.
|
page.
|
||||||
"""
|
"""
|
||||||
data = await self.request_daily_reward(
|
data = await self.request_daily_reward(
|
||||||
"award", params=dict(current_page=page), game=game, lang=lang
|
"award", params=dict(current_page=page), game=game or self.game, lang=lang
|
||||||
)
|
)
|
||||||
return [ClaimedDailyReward(**i) for i in data["list"]]
|
return [ClaimedDailyReward(**i) for i in data["list"]]
|
||||||
|
|
||||||
@ -159,7 +179,7 @@ class DailyRewardClient(BaseClient):
|
|||||||
break
|
break
|
||||||
|
|
||||||
fetched_items = await self._get_claimed_rewards_page(
|
fetched_items = await self._get_claimed_rewards_page(
|
||||||
page, game=game, lang=lang
|
page, game=game or self.game, lang=lang
|
||||||
)
|
)
|
||||||
if not fetched_items:
|
if not fetched_items:
|
||||||
break
|
break
|
||||||
@ -206,7 +226,7 @@ class DailyRewardClient(BaseClient):
|
|||||||
await self.request_daily_reward(
|
await self.request_daily_reward(
|
||||||
"sign",
|
"sign",
|
||||||
method="POST",
|
method="POST",
|
||||||
game=game,
|
game=game or self.game,
|
||||||
lang=lang,
|
lang=lang,
|
||||||
challenge=challenge,
|
challenge=challenge,
|
||||||
validate=validate,
|
validate=validate,
|
||||||
@ -216,7 +236,7 @@ class DailyRewardClient(BaseClient):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
info, rewards = await asyncio.gather(
|
info, rewards = await asyncio.gather(
|
||||||
self.get_reward_info(game=game, lang=lang),
|
self.get_reward_info(game=game or self.game, lang=lang),
|
||||||
self.get_monthly_rewards(game=game, lang=lang),
|
self.get_monthly_rewards(game=game or self.game, lang=lang),
|
||||||
)
|
)
|
||||||
return rewards[info.claimed_rewards - 1]
|
return rewards[info.claimed_rewards - 1]
|
||||||
|
@ -102,7 +102,9 @@ class BaseWishClient(BaseClient):
|
|||||||
game=game,
|
game=game,
|
||||||
lang=lang,
|
lang=lang,
|
||||||
authkey=authkey,
|
authkey=authkey,
|
||||||
params=dict(gacha_type=banner_type, size=size, end_id=end_id),
|
params=dict(
|
||||||
|
gacha_type=banner_type, size=size, end_id=end_id, game_biz=Game.value
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_banner_names(
|
async def get_banner_names(
|
||||||
|
@ -6,7 +6,6 @@ from simnet.models.genshin.wish import Wish
|
|||||||
from simnet.utils.enum_ import Game
|
from simnet.utils.enum_ import Game
|
||||||
from simnet.utils.paginator import WishPaginator
|
from simnet.utils.paginator import WishPaginator
|
||||||
|
|
||||||
|
|
||||||
__all__ = ("GenshinWishClient",)
|
__all__ = ("GenshinWishClient",)
|
||||||
|
|
||||||
|
|
||||||
@ -15,17 +14,16 @@ class GenshinWishClient(BaseWishClient):
|
|||||||
|
|
||||||
async def wish_history(
|
async def wish_history(
|
||||||
self,
|
self,
|
||||||
banner_type: int,
|
banner_types: Optional[List[int]] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
lang: Optional[str] = None,
|
lang: Optional[str] = None,
|
||||||
authkey: Optional[str] = None,
|
authkey: Optional[str] = None,
|
||||||
end_id: int = 0,
|
end_id: int = 0,
|
||||||
) -> List[Wish]:
|
) -> List[Wish]:
|
||||||
"""
|
"""Get the wish history for a list of banner types.
|
||||||
Get the wish history for a list of banner types.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
banner_type (int, optional): The banner types to get the wish history for.
|
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.
|
limit (Optional[int] , optional): The maximum number of wishes to retrieve.
|
||||||
If not provided, all available wishes will be returned.
|
If not provided, all available wishes will be returned.
|
||||||
lang (Optional[str], optional): The language code to use for the request.
|
lang (Optional[str], optional): The language code to use for the request.
|
||||||
@ -36,9 +34,12 @@ class GenshinWishClient(BaseWishClient):
|
|||||||
Returns:
|
Returns:
|
||||||
List[Wish]: A list of GenshinWish objects representing the retrieved wishes.
|
List[Wish]: A list of GenshinWish objects representing the retrieved wishes.
|
||||||
"""
|
"""
|
||||||
|
banner_types = banner_types or [100, 200, 301, 302]
|
||||||
banner_names = await self.get_banner_names(
|
banner_names = await self.get_banner_names(
|
||||||
game=Game.GENSHIN, lang=lang, authkey=authkey
|
game=Game.GENSHIN, lang=lang, authkey=authkey
|
||||||
)
|
)
|
||||||
|
wishes = []
|
||||||
|
for banner_type in banner_types:
|
||||||
paginator = WishPaginator(
|
paginator = WishPaginator(
|
||||||
end_id,
|
end_id,
|
||||||
partial(
|
partial(
|
||||||
@ -50,5 +51,5 @@ class GenshinWishClient(BaseWishClient):
|
|||||||
)
|
)
|
||||||
items = await paginator.get(limit)
|
items = await paginator.get(limit)
|
||||||
banner_name = banner_names[banner_type]
|
banner_name = banner_names[banner_type]
|
||||||
wish = [Wish(**i, banner_name=banner_name) for i in items]
|
wishes.extend([Wish(**i, banner_name=banner_name) for i in items])
|
||||||
return wish
|
return sorted(wishes, key=lambda wish: wish.time.timestamp())
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
|
|
||||||
from simnet.client.components.wish.base import BaseWishClient
|
from simnet.client.components.wish.base import BaseWishClient
|
||||||
from simnet.models.starrail.wish import StarRailWish
|
from simnet.models.starrail.wish import StarRailWish
|
||||||
from simnet.utils.enum_ import Game
|
from simnet.utils.enum_ import Game
|
||||||
@ -13,7 +14,7 @@ class StarRailWishClient(BaseWishClient):
|
|||||||
|
|
||||||
async def wish_history(
|
async def wish_history(
|
||||||
self,
|
self,
|
||||||
banner_type: int,
|
banner_types: Optional[List[int]] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
lang: Optional[str] = None,
|
lang: Optional[str] = None,
|
||||||
authkey: Optional[str] = None,
|
authkey: Optional[str] = None,
|
||||||
@ -23,7 +24,7 @@ class StarRailWishClient(BaseWishClient):
|
|||||||
Get the wish history for a list of banner types.
|
Get the wish history for a list of banner types.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
banner_type (int, optional): The banner types to get the wish history for.
|
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.
|
limit (Optional[int] , optional): The maximum number of wishes to retrieve.
|
||||||
If not provided, all available wishes will be returned.
|
If not provided, all available wishes will be returned.
|
||||||
lang (Optional[str], optional): The language code to use for the request.
|
lang (Optional[str], optional): The language code to use for the request.
|
||||||
@ -34,6 +35,9 @@ class StarRailWishClient(BaseWishClient):
|
|||||||
Returns:
|
Returns:
|
||||||
List[StarRailWish]: A list of StarRailWish objects representing the retrieved wishes.
|
List[StarRailWish]: A list of StarRailWish objects representing the retrieved wishes.
|
||||||
"""
|
"""
|
||||||
|
banner_types = banner_types or [1, 2, 11, 12]
|
||||||
|
wishes = []
|
||||||
|
for banner_type in banner_types:
|
||||||
paginator = WishPaginator(
|
paginator = WishPaginator(
|
||||||
end_id,
|
end_id,
|
||||||
partial(
|
partial(
|
||||||
@ -44,5 +48,5 @@ class StarRailWishClient(BaseWishClient):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
items = await paginator.get(limit)
|
items = await paginator.get(limit)
|
||||||
wish = [StarRailWish(**i) for i in items]
|
wishes.extend([StarRailWish(**i) for i in items])
|
||||||
return wish
|
return sorted(wishes, key=lambda wish: wish.time.timestamp())
|
||||||
|
@ -2,13 +2,16 @@ from typing import Optional
|
|||||||
|
|
||||||
from simnet.client.components.auth import AuthClient
|
from simnet.client.components.auth import AuthClient
|
||||||
from simnet.client.components.chronicle.genshin import GenshinBattleChronicleClient
|
from simnet.client.components.chronicle.genshin import GenshinBattleChronicleClient
|
||||||
|
from simnet.client.components.daily import DailyRewardClient
|
||||||
from simnet.client.components.wish.genshin import GenshinWishClient
|
from simnet.client.components.wish.genshin import GenshinWishClient
|
||||||
from simnet.utils.enum_ import Game
|
from simnet.utils.enum_ import Game
|
||||||
|
|
||||||
__all__ = ("GenshinClient",)
|
__all__ = ("GenshinClient",)
|
||||||
|
|
||||||
|
|
||||||
class GenshinClient(GenshinBattleChronicleClient, GenshinWishClient, AuthClient):
|
class GenshinClient(
|
||||||
|
GenshinBattleChronicleClient, GenshinWishClient, AuthClient, DailyRewardClient
|
||||||
|
):
|
||||||
"""A simple http client for StarRail endpoints."""
|
"""A simple http client for StarRail endpoints."""
|
||||||
|
|
||||||
game: Optional[Game] = Game.GENSHIN
|
game: Optional[Game] = Game.GENSHIN
|
||||||
|
@ -2,11 +2,13 @@ from typing import Optional
|
|||||||
|
|
||||||
from simnet.client.components.auth import AuthClient
|
from simnet.client.components.auth import AuthClient
|
||||||
from simnet.client.components.chronicle.genshin import GenshinBattleChronicleClient
|
from simnet.client.components.chronicle.genshin import GenshinBattleChronicleClient
|
||||||
|
from simnet.client.components.daily import DailyRewardClient
|
||||||
from simnet.client.components.wish.genshin import GenshinWishClient
|
from simnet.client.components.wish.genshin import GenshinWishClient
|
||||||
from simnet.utils.enum_ import Region
|
from simnet.utils.enum_ import Region
|
||||||
from simnet.utils.types import CookieTypes, HeaderTypes, TimeoutTypes
|
from simnet.utils.types import CookieTypes, HeaderTypes, TimeoutTypes
|
||||||
|
|
||||||
class GenshinClient(GenshinBattleChronicleClient, GenshinWishClient, AuthClient):
|
|
||||||
|
class GenshinClient(GenshinBattleChronicleClient, GenshinWishClient, AuthClient, DailyRewardClient):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
cookies: Optional[CookieTypes] = None,
|
cookies: Optional[CookieTypes] = None,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -9,6 +11,13 @@ except ImportError:
|
|||||||
class APIModel(BaseModel):
|
class APIModel(BaseModel):
|
||||||
"""A Pydantic BaseModel class used for modeling JSON data returned by an API."""
|
"""A Pydantic BaseModel class used for modeling JSON data returned by an API."""
|
||||||
|
|
||||||
|
def __init__(self, **data: Any) -> None:
|
||||||
|
for field_name, field in self.__fields__.items():
|
||||||
|
aliases = field.field_info.extra.get("aliases")
|
||||||
|
if aliases and aliases in data:
|
||||||
|
data[field_name] = data.pop(aliases)
|
||||||
|
super().__init__(**data)
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
"""A nested class defining configuration options for the APIModel."""
|
"""A nested class defining configuration options for the APIModel."""
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
from simnet.models.base import APIModel
|
from simnet.models.base import APIModel
|
||||||
|
|
||||||
__all__ = ("BaseCharacter",)
|
__all__ = ("BaseCharacter",)
|
||||||
@ -20,10 +22,10 @@ class BaseCharacter(APIModel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
id: int
|
id: int
|
||||||
name: str
|
name: Optional[str] = None
|
||||||
element: str
|
element: Optional[str] = None
|
||||||
rarity: int
|
rarity: Optional[str] = None
|
||||||
icon: str
|
icon: Optional[str] = None
|
||||||
|
|
||||||
collab: bool = False
|
collab: bool = False
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ class PartialCharacter(BaseCharacter):
|
|||||||
constellation (int): The number of constellations that are currently active for the character.
|
constellation (int): The number of constellations that are currently active for the character.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
icon: str = Field(alias="image")
|
||||||
level: int
|
level: int
|
||||||
friendship: int = Field(alias="fetter")
|
friendship: int = Field(alias="fetter")
|
||||||
constellation: int = Field(alias="actived_constellation_num")
|
constellation: int = Field(alias="actived_constellation_num")
|
||||||
|
@ -30,21 +30,21 @@ class Stats(APIModel):
|
|||||||
unlocked_domains (int): Number of domains unlocked by the user.
|
unlocked_domains (int): Number of domains unlocked by the user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
achievements: int = Field(alias="achievement_number")
|
achievements: int = Field(aliases="achievement_number")
|
||||||
days_active: int = Field(alias="active_day_number")
|
days_active: int = Field(aliases="active_day_number")
|
||||||
characters: int = Field(alias="avatar_number")
|
characters: int = Field(aliases="avatar_number")
|
||||||
spiral_abyss: str = Field(alias="spiral_abyss")
|
spiral_abyss: str = Field(aliases="spiral_abyss")
|
||||||
anemoculi: int = Field(alias="anemoculus_number")
|
anemoculi: int = Field(aliases="anemoculus_number")
|
||||||
geoculi: int = Field(alias="geoculus_number")
|
geoculi: int = Field(aliases="geoculus_number")
|
||||||
dendroculi: int = Field(alias="dendroculus_number")
|
dendroculi: int = Field(aliases="dendroculus_number")
|
||||||
electroculi: int = Field(alias="electroculus_number")
|
electroculi: int = Field(aliases="electroculus_number")
|
||||||
common_chests: int = Field(alias="common_chest_number")
|
common_chests: int = Field(aliases="common_chest_number")
|
||||||
exquisite_chests: int = Field(alias="exquisite_chest_number")
|
exquisite_chests: int = Field(aliases="exquisite_chest_number")
|
||||||
precious_chests: int = Field(alias="precious_chest_number")
|
precious_chests: int = Field(aliases="precious_chest_number")
|
||||||
luxurious_chests: int = Field(alias="luxurious_chest_number")
|
luxurious_chests: int = Field(aliases="luxurious_chest_number")
|
||||||
remarkable_chests: int = Field(alias="magic_chest_number")
|
remarkable_chests: int = Field(aliases="magic_chest_number")
|
||||||
unlocked_waypoints: int = Field(alias="way_point_number")
|
unlocked_waypoints: int = Field(aliases="way_point_number")
|
||||||
unlocked_domains: int = Field(alias="domain_number")
|
unlocked_domains: int = Field(aliases="domain_number")
|
||||||
|
|
||||||
|
|
||||||
class Offering(APIModel):
|
class Offering(APIModel):
|
||||||
@ -183,9 +183,9 @@ class PartialGenshinUserStats(APIModel):
|
|||||||
|
|
||||||
info: UserInfo = Field("role")
|
info: UserInfo = Field("role")
|
||||||
stats: Stats
|
stats: Stats
|
||||||
characters: List[PartialCharacter] = Field(alias="avatars")
|
characters: List[PartialCharacter] = Field(aliases="avatars")
|
||||||
explorations: List[Exploration] = Field(alias="world_explorations")
|
explorations: List[Exploration] = Field(aliases="world_explorations")
|
||||||
teapot: Optional[Teapot] = Field(alias="homes")
|
teapot: Optional[Teapot] = Field(aliases="homes")
|
||||||
|
|
||||||
@validator("teapot", pre=True)
|
@validator("teapot", pre=True)
|
||||||
def format_teapot(cls, v: Any) -> Optional[Dict[str, Any]]:
|
def format_teapot(cls, v: Any) -> Optional[Dict[str, Any]]:
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import calendar
|
|
||||||
from datetime import timezone, timedelta, datetime
|
from datetime import timezone, timedelta, datetime
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
@ -25,8 +24,7 @@ class DailyRewardInfo(NamedTuple):
|
|||||||
"""The number of rewards that the user has missed since the start of the month."""
|
"""The number of rewards that the user has missed since the start of the month."""
|
||||||
cn_timezone = timezone(timedelta(hours=8))
|
cn_timezone = timezone(timedelta(hours=8))
|
||||||
now = datetime.now(cn_timezone)
|
now = datetime.now(cn_timezone)
|
||||||
month_days = calendar.monthrange(now.year, now.month)[1]
|
return now.day - self.claimed_rewards
|
||||||
return month_days - self.claimed_rewards
|
|
||||||
|
|
||||||
|
|
||||||
class DailyReward(APIModel):
|
class DailyReward(APIModel):
|
||||||
|
@ -15,14 +15,12 @@ class DSType(Enum):
|
|||||||
Enumeration of dynamic secret types.
|
Enumeration of dynamic secret types.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
|
WEB (str): Android dynamic secret type.
|
||||||
ANDROID (str): Android dynamic secret type.
|
ANDROID (str): Android dynamic secret type.
|
||||||
ANDROID_NEW (str): New Android dynamic secret type.
|
|
||||||
SIGN (str): Sign dynamic secret type.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
WEB = "web"
|
||||||
ANDROID = "android"
|
ANDROID = "android"
|
||||||
ANDROID_NEW = "android_new"
|
|
||||||
SIGN = "sign"
|
|
||||||
|
|
||||||
|
|
||||||
def hex_digest(text):
|
def hex_digest(text):
|
||||||
@ -87,14 +85,20 @@ def generate_dynamic_secret(
|
|||||||
salt = "6s25p5ox5y14umn1p61aqyyvbvvl3lrt"
|
salt = "6s25p5ox5y14umn1p61aqyyvbvvl3lrt"
|
||||||
app_version = "1.5.0"
|
app_version = "1.5.0"
|
||||||
elif region == Region.CHINESE:
|
elif region == Region.CHINESE:
|
||||||
|
if new_ds:
|
||||||
if ds_type is None:
|
if ds_type is None:
|
||||||
salt = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs"
|
salt = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs"
|
||||||
elif ds_type == DSType.ANDROID:
|
elif ds_type == DSType.ANDROID:
|
||||||
|
client_type = "2"
|
||||||
salt = "KZazpG4cO2QECFDBUCxdhS8cYCsQHfzn"
|
salt = "KZazpG4cO2QECFDBUCxdhS8cYCsQHfzn"
|
||||||
client_type = "2"
|
else:
|
||||||
elif ds_type == DSType.ANDROID_NEW:
|
raise ValueError(f"Unknown ds_type: {ds_type}")
|
||||||
client_type = "2"
|
else:
|
||||||
|
if ds_type is None:
|
||||||
|
salt = "X7UOLLnTuNS3kgTJ1BUHOvKpiqp3kmym"
|
||||||
|
elif ds_type == DSType.ANDROID:
|
||||||
salt = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v"
|
salt = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v"
|
||||||
|
client_type = "2"
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown ds_type: {ds_type}")
|
raise ValueError(f"Unknown ds_type: {ds_type}")
|
||||||
else:
|
else:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
from typing import List, Dict, Callable, Any, Awaitable
|
from typing import List, Dict, Callable, Any, Awaitable
|
||||||
|
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ class WishPaginator:
|
|||||||
break
|
break
|
||||||
|
|
||||||
all_items.extend(filtered_items)
|
all_items.extend(filtered_items)
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
# Return up to the specified limit.
|
# Return up to the specified limit.
|
||||||
return all_items[: min(len(all_items), limit)]
|
return all_items[: min(len(all_items), limit)] if limit else all_items
|
||||||
|
Loading…
Reference in New Issue
Block a user