SIMNet/simnet/client/components/daily.py
洛水居室 4250481727
🐛 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>
2023-05-08 08:56:24 +08:00

243 lines
8.6 KiB
Python

"""Daily reward component."""
import asyncio
from typing import Optional, Dict, Any, List
from httpx import QueryParams
from simnet.client.base import BaseClient
from simnet.client.routes import REWARD_URL
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.player import recognize_genshin_server, recognize_starrail_server
__all__ = ("DailyRewardClient",)
class DailyRewardClient(BaseClient):
"""A client for interacting with the daily reward system."""
async def request_daily_reward(
self,
endpoint: str,
*,
method: str = "GET",
challenge: Optional[str] = None,
validate: Optional[str] = None,
game: Optional[Game] = None,
lang: Optional[str] = None,
params: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""
Makes a request to the daily reward endpoint.
Args:
endpoint (str): The endpoint to request.
method (str): The HTTP method to use. Defaults to "GET".
challenge (str): A challenge string for validating the request. Defaults to None.
validate (str): A validation string for validating the request. Defaults to None.
game (Game): The game to request data for. Defaults to None.
lang (str): The language to use. Defaults to None.
params (Dict[str, Any]): Any parameters to include in the request.
Returns:
A dictionary containing the response data.
"""
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:
headers = {}
if challenge is not None and validate is not None:
headers["x-rpc-challenge"] = challenge
headers["x-rpc-validate"] = validate
headers["x-rpc-seccode"] = f"{validate}|jordan"
headers["x-rpc-device_name"] = "Chrome 20 2023"
headers["x-rpc-channel"] = "chrome"
headers["x-rpc-device_model"] = "Chrome 2023"
headers["x-rpc-sys_version"] = "13"
headers["x-rpc-platform"] = "android"
device_id = self.device_id
hash_value = hex_digest(device_id)
headers["x-rpc-device_fp"] = hash_value[:13]
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))
url = base_url / endpoint
return await self.request_lab(
url,
method,
params=params,
headers=headers,
lang=lang,
)
async def get_reward_info(
self,
*,
game: Optional[Game] = None,
lang: Optional[str] = None,
) -> DailyRewardInfo:
"""Gets the daily reward info for the current user.
Args:
game (Game): The game to request data for. Defaults to None.
lang (str): The language to use. Defaults to None.
Returns:
A DailyRewardInfo object containing information about the user's daily reward status.
"""
data = await self.request_daily_reward("info", game=game, lang=lang)
return DailyRewardInfo(data["is_sign"], data["total_sign_day"])
async def get_monthly_rewards(
self,
*,
game: Optional[Game] = None,
lang: Optional[str] = None,
) -> List[DailyReward]:
"""Gets a list of all available rewards for the current month.
Args:
game (Game): The game to request data for. Defaults to None.
lang (str): The language to use. Defaults to None.
Returns:
A list of DailyReward objects representing the available rewards for the current month.
"""
data = await self.request_daily_reward(
"home",
game=game or self.game,
lang=lang,
)
return [DailyReward(**i) for i in data["awards"]]
async def _get_claimed_rewards_page(
self,
page: int,
*,
game: Optional[Game] = None,
lang: Optional[str] = None,
) -> List[ClaimedDailyReward]:
"""Gets a single page of claimed rewards for the current user.
Args:
page (int): The page number to retrieve.
game (Game): The game to request data for. Defaults to None.
lang (str): The language to use. Defaults to None.
Returns:
A list of ClaimedDailyReward objects representing the claimed rewards for the current user on the specified
page.
"""
data = await self.request_daily_reward(
"award", params=dict(current_page=page), game=game or self.game, lang=lang
)
return [ClaimedDailyReward(**i) for i in data["list"]]
async def claimed_rewards(
self,
*,
limit: Optional[int] = None,
game: Optional[Game] = None,
lang: Optional[str] = None,
) -> List[ClaimedDailyReward]:
"""Gets all claimed rewards for the current user.
Args:
limit (int): The maximum number of rewards to return. Defaults to None.
game (Game): The game to request data for. Defaults to None.
lang (str): The language to use. Defaults to None.
Returns:
A list of ClaimedDailyReward objects representing the claimed rewards for the current user.
"""
result = []
index = 0
page = 1
while True:
if page >= 10:
break
fetched_items = await self._get_claimed_rewards_page(
page, game=game or self.game, lang=lang
)
if not fetched_items:
break
# Calculate how many items should be added
items_to_add = (
limit - index
if limit is not None and limit - index < len(fetched_items)
else len(fetched_items)
)
result.extend(fetched_items[:items_to_add])
index += items_to_add
if limit is not None and index >= limit:
break
page += 1
return result
async def claim_daily_reward(
self,
*,
challenge: Optional[str] = None,
validate: Optional[str] = None,
game: Optional[Game] = None,
lang: Optional[str] = None,
reward: bool = True,
) -> Optional[DailyReward]:
"""
Signs into lab and claims the daily reward.
Args:
challenge (str): A challenge string for validating the request. Defaults to None.
validate (str): A validation string for validating the request. Defaults to None.
game (Game): The game to claim the reward for. Defaults to None.
lang (str): The language to use. Defaults to None.
reward (bool): Whether to return the claimed reward. Defaults to True.
Returns:
If `reward` is True, a DailyReward object representing the claimed reward. Otherwise, None.
"""
await self.request_daily_reward(
"sign",
method="POST",
game=game or self.game,
lang=lang,
challenge=challenge,
validate=validate,
)
if not reward:
return None
info, rewards = await asyncio.gather(
self.get_reward_info(game=game or self.game, lang=lang),
self.get_monthly_rewards(game=game or self.game, lang=lang),
)
return rewards[info.claimed_rewards - 1]