From 7eeb800660e05d27f296b9d219055d5bbcbde9e5 Mon Sep 17 00:00:00 2001 From: seria Date: Wed, 30 Oct 2024 08:48:15 +0900 Subject: [PATCH] Add HSR event calendar support --- .../client/components/chronicle/starrail.py | 10 + genshin/models/starrail/chronicle/__init__.py | 1 + genshin/models/starrail/chronicle/events.py | 249 ++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 genshin/models/starrail/chronicle/events.py diff --git a/genshin/client/components/chronicle/starrail.py b/genshin/client/components/chronicle/starrail.py index e4568206..f3aabaf9 100644 --- a/genshin/client/components/chronicle/starrail.py +++ b/genshin/client/components/chronicle/starrail.py @@ -151,3 +151,13 @@ async def get_starrail_apc_shadow( payload = dict(schedule_type=2 if previous else 1, need_all="true") data = await self._request_starrail_record("challenge_boss", uid, lang=lang, payload=payload) return models.StarRailAPCShadow(**data) + + async def get_starrail_event_calendar( + self, + uid: typing.Optional[int] = None, + *, + lang: typing.Optional[str] = None, + ) -> models.HSREventCalendar: + """Get HSR event calendar.""" + data = await self._request_starrail_record("get_act_calender", uid, lang=lang, cache=True) + return models.HSREventCalendar(**data) diff --git a/genshin/models/starrail/chronicle/__init__.py b/genshin/models/starrail/chronicle/__init__.py index 30c85a08..1c4fd616 100644 --- a/genshin/models/starrail/chronicle/__init__.py +++ b/genshin/models/starrail/chronicle/__init__.py @@ -2,6 +2,7 @@ from .challenge import * from .characters import * +from .events import * from .notes import * from .rogue import * from .stats import * diff --git a/genshin/models/starrail/chronicle/events.py b/genshin/models/starrail/chronicle/events.py new file mode 100644 index 00000000..827c1f8c --- /dev/null +++ b/genshin/models/starrail/chronicle/events.py @@ -0,0 +1,249 @@ +import typing +from enum import Enum + +import pydantic + +from genshin.models.model import APIModel, DateTimeField + +__all__ = ( + "ChallengeStatus", + "ChallengeType", + "EventWarp", + "EventWarpCharacter", + "EventWarpLightCone", + "HSRBaseEventItem", + "HSRChallenge", + "HSREvent", + "HSREventCalendar", + "HSREventReward", + "HSREventStatus", + "HSREventTimeType", + "HSREventType", + "TimeInfo", +) + + +class HSREventType(Enum): + """Event type enum.""" + + SIGN_IN = "ActivityTypeSign" + """Daily sign-in event.""" + OTHER = "ActivityTypeOther" + DOUBLE_REWARDS = "ActivityTypeDouble" + """Plannar Fissure, Garden of Plenty, etc.""" + + +class HSREventStatus(Enum): + """Event status enum.""" + + OTHER_LOCKED = "OtherActStatusUnopened" + OTHER_IN_PROGRESS = "OtherActStatusUnFinish" + DOUBLE_REWARDS_LOCKED = "DoubleActStatusUnopened" + SIGN_IN_UNCLAIMED = "SignActStatusUnclaimed" + + +class HSREventTimeType(Enum): + """Event time type enum.""" + + LONG = "ActTimeTypeLong" + """Simulated universe.""" + DEFAULT = "ActTimeTypeDefault" + + +class ChallengeType(Enum): + """Challenge type enum.""" + + APC_SHADOW = "ChallengeTypeBoss" + """Apocalyptic shadow.""" + MOC = "ChallengeTypeChasm" + """Memory of Chaos.""" + PURE_FICTION = "ChallengeTypeStory" + + +class ChallengeStatus(Enum): + """Challenge status enum.""" + + IN_PROGRESS = "challengeStatusInProgress" + LOCKED = "challengeStatusUnopened" + + +class TimeInfo(APIModel): + """Time info model.""" + + start: DateTimeField = pydantic.Field(alias="start_ts") + end: DateTimeField = pydantic.Field(alias="end_ts") + now: DateTimeField + + +class HSRBaseEvent(APIModel): + """HSR base event model.""" + + time_info: typing.Optional[TimeInfo] = None + + @pydantic.field_validator("time_info", mode="before") + def __validate_time_info(cls, v: dict[str, typing.Any]) -> typing.Optional[dict[str, typing.Any]]: + if not v["start_time"]: + return None + return v + + +class HSRBaseEventItem(APIModel): + """Base event item model.""" + + id: int = pydantic.Field(alias="item_id") + name: str = pydantic.Field(alias="item_name") + icon: str = pydantic.Field(alias="icon_url") + path: int = pydantic.Field(alias="avatar_base_type") + + rarity: int + wiki_url: str + + is_forward: bool # No clue what this is + + +class EventWarpCharacter(HSRBaseEventItem): + """Event warp character model.""" + + icon: str = pydantic.Field(alias="icon_url") + large_icon: str = pydantic.Field(alias="item_avatar_icon_path") + element: int = pydantic.Field(alias="damage_type") + + +class EventWarpLightCone(HSRBaseEventItem): + """Event warp light cone model.""" + + icon: str = pydantic.Field(alias="item_url") + + +class EventWarp(HSRBaseEvent): + """Event warp model.""" + + name: str + type: str # Seems to always be 'CardPoolRole' + characters: typing.Sequence[EventWarpCharacter] = pydantic.Field(alias="avatar_list") + light_cones: typing.Sequence[EventWarpLightCone] = pydantic.Field(alias="equip_list") + + is_after_version: bool + """Whether the event happens after last version's update.""" + version: str + id: int + + +class HSREventReward(APIModel): + """HSR event reward model.""" + + id: int = pydantic.Field(alias="item_id") + name: str + icon: str + wiki_url: str + num: int + rarity: int + + +class HSREvent(HSRBaseEvent): + """HSR event model.""" + + id: int + version: str + name: str + description: str = pydantic.Field(alias="panel_desc") + + type: typing.Union[HSREventType, str] = pydantic.Field(alias="act_type") + time_type: typing.Union[HSREventTimeType, str] = pydantic.Field(alias="act_time_type") + status: typing.Union[HSREventStatus, str] = pydantic.Field(alias="act_status") + + rewards: typing.Sequence[HSREventReward] = pydantic.Field(alias="reward_list") + total_progress: int + current_progress: int + special_reward: typing.Optional[HSREventReward] + + is_after_version: bool + """Whether the event happens after last version's update.""" + all_finished: bool + show_text: str + + # No clue what these are + strategy: str + multiple_drop_type: int + multiple_drop_type_list: list[int] + count_refresh_type: int + count_value: int + drop_multiple: int + panel_id: int + sort_weight: int + + @pydantic.field_validator("special_reward", mode="after") + def __validate_special_reward(cls, v: HSREventReward) -> typing.Optional[HSREventReward]: + if v.id == 0: + return None + return v + + @pydantic.field_validator("type", mode="before") + def __validate_type(cls, v: str) -> typing.Union[HSREventType, str]: + try: + return HSREventType(v) + except ValueError: + return v + + @pydantic.field_validator("time_type", mode="before") + def __validate_time_type(cls, v: str) -> typing.Union[HSREventTimeType, str]: + try: + return HSREventTimeType(v) + except ValueError: + return v + + @pydantic.field_validator("status", mode="before") + def __validate_status(cls, v: str) -> typing.Union[HSREventStatus, str]: + try: + return HSREventStatus(v) + except ValueError: + return v + + +class HSRChallenge(HSRBaseEvent): + """HSR challenge model.""" + + id: int = pydantic.Field(alias="group_id") + name: str = pydantic.Field(alias="name_mi18n") + + type: typing.Union[ChallengeType, str] = pydantic.Field(alias="challenge_type") + status: typing.Union[ChallengeStatus, str] + + rewards: typing.Sequence[HSREventReward] = pydantic.Field(alias="reward_list") + special_reward: typing.Optional[HSREventReward] + total_progress: int + current_progress: int + show_text: str + + @pydantic.field_validator("special_reward", mode="after") + def __validate_special_reward(cls, v: HSREventReward) -> typing.Optional[HSREventReward]: + if v.id == 0: + return None + return v + + @pydantic.field_validator("type", mode="before") + def __validate_type(cls, v: str) -> typing.Union[ChallengeType, str]: + try: + return ChallengeType(v) + except ValueError: + return v + + @pydantic.field_validator("status", mode="before") + def __validate_status(cls, v: str) -> typing.Union[ChallengeStatus, str]: + try: + return ChallengeStatus(v) + except ValueError: + return v + + +class HSREventCalendar(APIModel): + """HSR event calendar model.""" + + character_warps: typing.Sequence[EventWarp] = pydantic.Field(alias="avatar_card_pool_list") + light_cone_warps: typing.Sequence[EventWarp] = pydantic.Field(alias="equip_card_pool_list") + events: typing.Sequence[HSREvent] = pydantic.Field(alias="act_list") + challenges: typing.Sequence[HSRChallenge] = pydantic.Field(alias="challenge_list") + + cur_game_version: str + """Current game version.""" + now: DateTimeField