feat: Update to version 1.1

- Add UserNotFound and InvalidParams exception.
- The API has changed its data format.
- Updated the model to accommodate the new format.
- Old format models have been moved to "models.v1".
- Use the "fetch_user_v1" function to retrieve data in the old format.
This commit is contained in:
KT 2023-06-08 22:03:47 +08:00
parent 8dcd3b62be
commit 4a892d213b
15 changed files with 636 additions and 162 deletions

View File

@ -3,8 +3,9 @@ from enum import Enum
import aiohttp
from .errors import HttpRequestError
from .errors import HttpRequestError, InvalidParams, UserNotFound
from .models import StarrailInfoParsed
from .models.v1 import StarrailInfoParsedV1
from .tools import remove_empty_dict, replace_trailblazer_name
@ -48,6 +49,8 @@ class MihomoAPI:
self,
uid: int | str,
language: Language,
*,
params: dict[str, str] = {},
) -> typing.Any:
"""
Makes an HTTP request to the API.
@ -61,18 +64,32 @@ class MihomoAPI:
Raises:
HttpRequestError: If the HTTP request fails.
InvalidParams: If the API request contains invalid parameters.
UserNotFound: If the requested user is not found.
"""
url = self.BASE_URL + "/" + str(uid)
params = {}
if language != Language.CHS:
params.update({"lang": language.value})
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as response:
if response.status == 200:
return await response.json(encoding="utf-8")
else:
raise HttpRequestError(response.status, str(response.reason))
match response.status:
case 200:
return await response.json(encoding="utf-8")
case 400:
try:
data = await response.json(encoding="utf-8")
except:
raise InvalidParams()
else:
if isinstance(data, dict) and (detail := data.get("detail")):
raise InvalidParams(detail)
raise InvalidParams()
case 404:
raise UserNotFound()
case _:
raise HttpRequestError(response.status, str(response.reason))
async def fetch_user(self, uid: int) -> StarrailInfoParsed:
"""
@ -86,8 +103,23 @@ class MihomoAPI:
"""
data = await self.request(uid, self.lang)
data = remove_empty_dict(data)
data = StarrailInfoParsed.parse_obj(data)
return data
async def fetch_user_v1(self, uid: int) -> StarrailInfoParsedV1:
"""
Fetches user data from the API using version 1.
Args:
uid (int): The user ID.
Returns:
StarrailInfoParsedV1: The parsed user data from the Mihomo API (version 1).
"""
data = await self.request(uid, self.lang, params={"version": "v1"})
data = remove_empty_dict(data)
data = StarrailInfoParsedV1.parse_obj(data)
data = replace_trailblazer_name(data)
return data

View File

@ -1,9 +1,19 @@
class HttpRequestError(Exception):
"""Http request failed"""
class BaseException(Exception):
"""Base exception class."""
message: str = ""
def __init__(self, message: str | None = None, *args: object) -> None:
if message is not None:
self.message = message
super().__init__(self.message, *args)
class HttpRequestError(BaseException):
"""Exception raised when an HTTP request fails."""
status: int = 0
reason: str = ""
message: str = ""
def __init__(
self,
@ -18,3 +28,15 @@ class HttpRequestError(Exception):
self.reason = reason
self.message = message
super().__init__(message, *args)
class UserNotFound(BaseException):
"""Exception raised when a user is not found."""
message = "User not found."
class InvalidParams(BaseException):
"""Exception raised when invalid parameters are provided."""
message: str = "Invalid parameters"

View File

@ -1,4 +1,5 @@
from .base import *
from .character import *
from .combat import *
from .equipment import *
from .player import *

View File

@ -1,7 +1,7 @@
from pydantic import BaseModel, Field
from .character import Character
from .player import Player, PlayerSpaceInfo
from .player import Player
class StarrailInfoParsed(BaseModel):
@ -9,14 +9,11 @@ class StarrailInfoParsed(BaseModel):
Mihomo parsed data
Attributes:
- player (`Player`): The player's basic info.
- player_details (`PlayerSpaceInfo`): The player's details.
- player (`Player`): The player's info.
- characters (list[`Character`]): The list of characters.
"""
player: Player
"""Player's basic info"""
player_details: PlayerSpaceInfo = Field(..., alias="PlayerSpaceInfo")
"""Player's details"""
characters: list[Character]
"""The list of characters"""

View File

@ -2,66 +2,55 @@ from typing import Any
from pydantic import BaseModel, Field, root_validator
from .combat import Attribute, Element, Path, Property
from .equipment import LightCone, Relic, RelicSet
class EidolonIcon(BaseModel):
"""
Represents an Eidolon icon.
Attributes:
- icon (`str`): The eidolon icon.
- unlock (`bool`): Indicates if the eidolon is unlocked.
"""
icon: str
"""The eidolon icon"""
unlock: bool
"""Indicates if the eidolon is unlocked"""
class Trace(BaseModel):
"""
Represents a character's skill trace.
Attributes:
- id (`int`): The ID of the trace.
- name (`str`): The name of the trace.
- level (`int`): The level of the trace.
- level (`int`): The current level of the trace.
- max_level (`int`): The maximum level of the trace.
- element (`Element` | None): The element of the trace, or None if not applicable.
- type (`str`): The type of the trace.
- type_text (`str`): The type text of the trace.
- effect (`str`): The effect of the trace.
- effect_text (`str`): The effect text of the trace.
- simple_desc (`str`): The simple description of the trace.
- desc (`str`): The detailed description of the trace.
- icon (`str`): The trace icon.
"""
id: int
"""The ID of the trace"""
name: str
"""The name of the trace"""
level: int
"""The level of the trace"""
"""The current level of the trace"""
max_level: int
"""The maximum level of the trace"""
element: Element | None = None
"""The element of the trace"""
type: str
"""The type of the trace"""
type_text: str
"""The type text of the trace"""
effect: str
"""The effect of the trace"""
effect_text: str
"""The effect text of the trace"""
simple_desc: str
"""The simple description of the trace"""
desc: str
"""The detailed description of the trace"""
icon: str
"""The trace icon"""
class Stat(BaseModel):
"""
Represents a character's stat.
Attributes:
- name (`str`): The name of the stat.
- base (`str`): The base value of the stat.
- addition (`str` | `None`): The additional value of the stat, or None if not applicable.
- icon (`str`): The stat icon.
"""
name: str
"""The name of the stat"""
base: str
"""The base value of the stat"""
addition: str | None = None
"""The additional value of the stat"""
icon: str
"""The stat icon"""
class Character(BaseModel):
"""
Represents a character.
@ -72,26 +61,25 @@ class Character(BaseModel):
- name (`str`): The character's name.
- rarity (`int`): The character's rarity.
- level (`int`): The character's level.
- Eidolon
- ascension (`int`): Ascension level.
- eidolon (`int`): The character's eidolon rank.
- eidolon_text (`str`): The text representation of the eidolon.
- eidolon_icons (list[`EidolonIcon`]): The list of eidolon icons.
- Image
- icon (`str`): The character avatar image
- preview (`str`): The character's preview image.
- portrait (`str`): The character's portrait image.
- Combat type
- path (`str`): The character's path.
- path_icon (`str`): The character's path icon.
- element (`str`): The character's element.
- element_icon (`str`): The character's element icon.
- color (`str`): The character's element color.
- Combat
- path (`Path`): The character's path.
- element (`Element`): The character's element.
- Equipment
- traces (list[`Trace`]): The list of character's skill traces.
- light_cone (`LightCone` | `None`): The character's light cone (weapon), or None if not applicable.
- relics (list[`Relic`] | `None`): The list of character's relics, or None if not applicable.
- relic_set (list[`RelicSet`] | `None`): The list of character's relic sets, or None if not applicable.
- stats (list[`Stat`]): The list of character's stats.
- Stats
- attributes (list[`Attribute`]): The list of character's attributes.
- additions (list[`Attribute`]): The list of character's additional attributes.
- properties (list[`Property`]): The list of character's properties.
"""
id: str
@ -102,52 +90,35 @@ class Character(BaseModel):
"""Character's rarity"""
level: int
"""Character's level"""
ascension: int = Field(..., alias="promotion")
"""Ascension Level"""
eidolon: int = Field(..., alias="rank")
"""Character's eidolon rank"""
eidolon_text: str = Field(..., alias="rank_text")
"""The text representation of the eidolon"""
eidolon_icons: list[EidolonIcon] = Field(..., alias="rank_icons")
"""The list of eidolon icons"""
icon: str
"""Character avatar image"""
preview: str
"""Character preview image"""
portrait: str
"""Character portrait image"""
path: str
path: Path
"""Character's path"""
path_icon: str
"""Character's path icon"""
element: str
element: Element
"""Character's element"""
element_icon: str
"""Character's element icon"""
color: str
"""Character's element color"""
traces: list[Trace] = Field(..., alias="skill")
traces: list[Trace] = Field(..., alias="skills")
"""The list of character's skill traces"""
light_cone: LightCone | None = None
"""Character's light cone (weapon)"""
relics: list[Relic] | None = Field(None, alias="relic")
relics: list[Relic] = []
"""The list of character's relics"""
relic_set: list[RelicSet] | None = None
relic_sets: list[RelicSet] = []
"""The list of character's relic sets"""
stats: list[Stat] = Field(..., alias="property")
"""The list of character's stats"""
@root_validator(pre=True)
def dict_to_list(cls, data: dict[str, Any]):
# The keys of the original dict is not necessary, so remove them here.
if isinstance(data, dict) and data.get("relic") is not None:
if isinstance(data["relic"], dict):
data["relic"] = list(data["relic"].values())
return data
@property
def icon(self) -> str:
"""Character avatar image"""
return f"icon/character/{self.id}.png"
attributes: list[Attribute]
"""The list of character's attributes"""
additions: list[Attribute]
"""The list of character's additional attributes"""
properties: list[Property]
"""The list of character's properties"""

97
mihomo/models/combat.py Normal file
View File

@ -0,0 +1,97 @@
from pydantic import BaseModel, Field
class Element(BaseModel):
"""
Represents an element.
Attributes:
- id (`str`): The ID of the element.
- name (`str`): The name of the element.
- color (`str`): The color of the element.
- icon (`str`): The element icon.
"""
id: str
"""The ID of the element"""
name: str
"""The name of the element"""
color: str
"""The color of the element"""
icon: str
"""The element icon"""
class Path(BaseModel):
"""
Paths are congregations of Imaginary energy, with which the ideals harmonize.
Attributes:
- id (`str`): The ID of the path.
- name (`str`): The name of the path.
- icon (`str`): The path icon.
"""
id: str
"""The ID of the path"""
name: str
"""The name of the path"""
icon: str
"""The path icon"""
class Attribute(BaseModel):
"""
Represents an attribute.
Attributes:
- field (`str`): The field of the attribute.
- name (`str`): The name of the attribute.
- icon (`str`): The attribute icon image.
- value (`float`): The value of the attribute.
- displayed_value (`str`): The displayed value of the attribute.
- is_percent (`bool`): Indicates if the value is in percentage.
"""
field: str
"""The field of the attribute"""
name: str
"""The name of the attribute"""
icon: str
"""The attribute icon image"""
value: float
"""The value of the attribute"""
displayed_value: str = Field(..., alias="display")
"""The displayed value of the attribute"""
is_percent: bool = Field(..., alias="percent")
"""Indicates if the value is in percentage"""
class Property(BaseModel):
"""
Represents a property.
Attributes:
- type (`str`): The type of the property.
- field (`str`): The field of the property.
- name (`str`): The name of the property.
- icon (`str`): The property icon image.
- value (`float`): The value of the property.
- displayed_value (`str`): The displayed value of the property.
- is_percent (`bool`): Indicates if the value is in percentage.
"""
type: str
"""The type of the property"""
field: str
"""The field of the property"""
name: str
"""The name of the property"""
icon: str
"""The property icon image"""
value: float
"""The value of the property"""
displayed_value: str = Field(..., alias="display")
"""The displayed value of the property"""
is_percent: bool = Field(..., alias="percent")
"""Indicates if the value is in percentage"""

View File

@ -1,38 +1,51 @@
from pydantic import BaseModel, Field
from .combat import Attribute, Path, Property
class LightCone(BaseModel):
"""
Represents a light cone (weapon).
Attributes:
- id (`int`): The ID of the light cone.
- name (`str`): The name of the light cone.
- rarity (`int`): The rarity of the light cone.
- superimpose (`int`): The superimpose rank of the light cone.
- level (`int`): The level of the light cone.
- icon (`str`): The light cone icon.
- ascension (`int`): The ascension level of the light cone.
- icon (`str`): The light cone icon image.
- preview (`str`): The light cone preview image.
- portrait (`str`): The light cone portrait image.
- path (`Path`): The path of the light cone.
- attributes (list[`Attribute`]): The list of attributes of the light cone.
- properties (list[`Property`]): The list of properties of the light cone.
"""
id: int
"""The ID of the light cone"""
name: str
"""The name of the light cone"""
rarity: int
"""The rarity of the light cone"""
superimpose: int = Field(..., alias="rank")
"""The superimpose rank of the light cone"""
level: int
"""The level of the light cone"""
ascension: int = Field(..., alias="promotion")
"""The ascension level of the light cone"""
icon: str
class RelicProperty(BaseModel):
"""
Represents a property of a relic.
Attributes:
- name (`str`): The name of the relic property.
- value (`str`): The value of the relic property.
- icon (`str`): The property icon.
"""
name: str
value: str
icon: str
"""The light cone icon image"""
preview: str
"""The light cone preview image"""
portrait: str
"""The light cone portrait image"""
path: Path
"""The path of the light cone"""
attributes: list[Attribute]
"""The list of attributes of the light cone"""
properties: list[Property]
"""The list of properties of the light cone"""
class Relic(BaseModel):
@ -40,7 +53,10 @@ class Relic(BaseModel):
Represents a relic.
Attributes:
- id (`int`): The ID of the relic.
- name (`str`): The name of the relic.
- set_id (`int`): The ID of the relic set.
- set_name (`str`): The name of the relic set.
- rarity (`int`): The rarity of the relic.
- level (`int`): The level of the relic.
- main_property (`RelicProperty`): The main property of the relic.
@ -48,12 +64,24 @@ class Relic(BaseModel):
- icon (`str`): The relic icon.
"""
id: int
"""The ID of the relic"""
name: str
"""The name of the relic"""
set_id: int
"""The ID of the relic set"""
set_name: str
"""The name of the relic set"""
rarity: int
"""The rarity of the relic"""
level: int
main_property: RelicProperty
sub_property: list[RelicProperty]
"""The level of the relic"""
main_property: Property = Field(..., alias="main_affix")
"""The main property of the relic"""
sub_properties: list[Property] = Field(..., alias="sub_affix")
"""The list of sub properties of the relic"""
icon: str
"""The relic icon"""
class RelicSet(BaseModel):
@ -61,11 +89,17 @@ class RelicSet(BaseModel):
Represents a set of relics.
Attributes:
- id (`int`): The ID of the relic set.
- name (`str`): The name of the relic set.
- icon (`str`): The relic set icon.
- desc (`int`): The description of the relic set.
- desc (`str`): The description of the relic set.
- properties (list[`Property`]): The list of properties of the relic set.
"""
id: int
"""The ID of the relic set"""
name: str
icon: str
desc: int
"""The name of the relic set"""
desc: str
"""The description of the relic set"""
properties: list[Property]
"""The list of properties of the relic set"""

View File

@ -1,28 +1,12 @@
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, root_validator
class Player(BaseModel):
"""
Player basic info
Attributes:
- uid (`str`): The player's uid.
- name (`str`): The player's nickname.
- level (`int`): The player's Trailblaze level.
- icon (`str`): The player's profile picture.
- signature (`str`): The player's bio.
"""
uid: str
"""Player's uid"""
name: str
"""Player's nickname"""
level: int
"""Trailblaze level"""
icon: str
class Avatar(BaseModel):
"""Profile picture"""
signature: str
"""Bio"""
id: int
name: str
icon: str
class ForgottenHall(BaseModel):
@ -30,21 +14,31 @@ class ForgottenHall(BaseModel):
Attributes:
- memory (`int`): The progress of the memory.
- memory_of_chaos_id (`int` | `None`): The ID of the memory of chaos, or None if not applicable.
- memory_of_chaos (`int` | `None`): The progress of the memory of chaos, or None if not applicable.
- memory_of_chaos_id (`int`): The ID of the memory of chaos, or None if not applicable.
- memory_of_chaos (`int`): The progress of the memory of chaos, or None if not applicable.
"""
memory: int = Field(..., alias="PreMazeGroupIndex")
memory: int = Field(..., alias="pre_maze_group_index")
"""The progress of the memory"""
memory_of_chaos_id: int | None = Field(None, alias="MazeGroupID")
memory_of_chaos: int | None = Field(None, alias="MazeGroupIndex")
memory_of_chaos_id: int = Field(..., alias="maze_group_id")
"""The ID of the memory of chaos"""
memory_of_chaos: int = Field(..., alias="maze_group_index")
"""The progress of the memory of chaos"""
class PlayerSpaceInfo(BaseModel):
"""Player details
class Player(BaseModel):
"""
Player basic info
Attributes:
- uid (`int`): The player's uid.
- name (`str`): The player's nickname.
- level (`int`): The player's Trailblaze level.
- world_level (`int`): The player's Equilibrium level.
- avatar (`Avatar`): The player's profile picture.
- signature (`str`): The player's bio.
- is_display (`bool`): Is the player's profile display enabled.
- forgotten_hall (`ForgottenHall` | None): The progress of the Forgotten Hall, or None if not applicable.
- simulated_universes (`int`): The number of simulated universes passed.
- light_cones (`int`): The number of light cones owned.
@ -52,13 +46,28 @@ class PlayerSpaceInfo(BaseModel):
- achievements (`int`): The number of achievements unlocked.
"""
forgotten_hall: ForgottenHall | None = Field(None, alias="ChallengeData")
uid: int
"""Player's uid"""
name: str = Field(..., alias="nickname")
"""Player's nickname"""
level: int
"""Trailblaze level"""
world_level: int
"""Equilibrium level"""
avatar: Avatar
"""Profile picture"""
signature: str
"""Bio"""
is_display: bool
"""Is the player's profile display enabled."""
forgotten_hall: ForgottenHall | None = Field(None, alias="challenge_data")
"""The progress of the Forgotten Hall"""
simulated_universes: int = Field(0, alias="PassAreaProgress")
simulated_universes: int = Field(0, alias="pass_area_progress")
"""Number of simulated universes passed"""
light_cones: int = Field(0, alias="LightConeCount")
light_cones: int = Field(0, alias="light_cone_count")
"""Number of light cones owned"""
characters: int = Field(0, alias="AvatarCount")
characters: int = Field(0, alias="avatar_count")
"""Number of characters owned"""
achievements: int = Field(0, alias="AchievementCount")
achievements: int = Field(0, alias="achievement_count")
"""Number of achievements unlocked"""

View File

@ -0,0 +1,4 @@
from .base import *
from .character import *
from .equipment import *
from .player import *

22
mihomo/models/v1/base.py Normal file
View File

@ -0,0 +1,22 @@
from pydantic import BaseModel, Field
from .character import Character
from .player import Player, PlayerSpaceInfo
class StarrailInfoParsedV1(BaseModel):
"""
Mihomo parsed data V1
Attributes:
- player (`Player`): The player's basic info.
- player_details (`PlayerSpaceInfo`): The player's details.
- characters (list[`Character`]): The list of characters.
"""
player: Player
"""Player's basic info"""
player_details: PlayerSpaceInfo = Field(..., alias="PlayerSpaceInfo")
"""Player's details"""
characters: list[Character]
"""The list of characters"""

View File

@ -0,0 +1,150 @@
from typing import Any
from pydantic import BaseModel, Field, root_validator
from .equipment import LightCone, Relic, RelicSet
class EidolonIcon(BaseModel):
"""
Represents an Eidolon icon.
Attributes:
- icon (`str`): The eidolon icon.
- unlock (`bool`): Indicates if the eidolon is unlocked.
"""
icon: str
"""The eidolon icon"""
unlock: bool
"""Indicates if the eidolon is unlocked"""
class Trace(BaseModel):
"""
Represents a character's skill trace.
Attributes:
- name (`str`): The name of the trace.
- level (`int`): The level of the trace.
- type (`str`): The type of the trace.
- icon (`str`): The trace icon.
"""
name: str
"""The name of the trace"""
level: int
"""The level of the trace"""
type: str
"""The type of the trace"""
icon: str
"""The trace icon"""
class Stat(BaseModel):
"""
Represents a character's stat.
Attributes:
- name (`str`): The name of the stat.
- base (`str`): The base value of the stat.
- addition (`str` | `None`): The additional value of the stat, or None if not applicable.
- icon (`str`): The stat icon.
"""
name: str
"""The name of the stat"""
base: str
"""The base value of the stat"""
addition: str | None = None
"""The additional value of the stat"""
icon: str
"""The stat icon"""
class Character(BaseModel):
"""
Represents a character.
Attributes:
- Basic info:
- id (`str`): The character's ID.
- name (`str`): The character's name.
- rarity (`int`): The character's rarity.
- level (`int`): The character's level.
- Eidolon
- eidolon (`int`): The character's eidolon rank.
- eidolon_icons (list[`EidolonIcon`]): The list of eidolon icons.
- Image
- icon (`str`): The character avatar image
- preview (`str`): The character's preview image.
- portrait (`str`): The character's portrait image.
- Combat type
- path (`str`): The character's path.
- path_icon (`str`): The character's path icon.
- element (`str`): The character's element.
- element_icon (`str`): The character's element icon.
- color (`str`): The character's element color.
- Equipment
- traces (list[`Trace`]): The list of character's skill traces.
- light_cone (`LightCone` | `None`): The character's light cone (weapon), or None if not applicable.
- relics (list[`Relic`] | `None`): The list of character's relics, or None if not applicable.
- relic_set (list[`RelicSet`] | `None`): The list of character's relic sets, or None if not applicable.
- stats (list[`Stat`]): The list of character's stats.
"""
id: str
"""Character's ID"""
name: str
"""Character's name"""
rarity: int
"""Character's rarity"""
level: int
"""Character's level"""
eidolon: int = Field(..., alias="rank")
"""Character's eidolon rank"""
eidolon_icons: list[EidolonIcon] = Field(..., alias="rank_icons")
"""The list of eidolon icons"""
preview: str
"""Character preview image"""
portrait: str
"""Character portrait image"""
path: str
"""Character's path"""
path_icon: str
"""Character's path icon"""
element: str
"""Character's element"""
element_icon: str
"""Character's element icon"""
color: str
"""Character's element color"""
traces: list[Trace] = Field(..., alias="skill")
"""The list of character's skill traces"""
light_cone: LightCone | None = None
"""Character's light cone (weapon)"""
relics: list[Relic] | None = Field(None, alias="relic")
"""The list of character's relics"""
relic_set: list[RelicSet] | None = None
"""The list of character's relic sets"""
stats: list[Stat] = Field(..., alias="property")
"""The list of character's stats"""
@root_validator(pre=True)
def dict_to_list(cls, data: dict[str, Any]):
# The keys of the original dict is not necessary, so remove them here.
if isinstance(data, dict) and data.get("relic") is not None:
if isinstance(data["relic"], dict):
data["relic"] = list(data["relic"].values())
return data
@property
def icon(self) -> str:
"""Character avatar image"""
return f"icon/character/{self.id}.png"

View File

@ -0,0 +1,71 @@
from pydantic import BaseModel, Field
class LightCone(BaseModel):
"""
Represents a light cone (weapon).
Attributes:
- name (`str`): The name of the light cone.
- rarity (`int`): The rarity of the light cone.
- superimpose (`int`): The superimpose rank of the light cone.
- level (`int`): The level of the light cone.
- icon (`str`): The light cone icon.
"""
name: str
rarity: int
superimpose: int = Field(..., alias="rank")
level: int
icon: str
class RelicProperty(BaseModel):
"""
Represents a property of a relic.
Attributes:
- name (`str`): The name of the relic property.
- value (`str`): The value of the relic property.
- icon (`str`): The property icon.
"""
name: str
value: str
icon: str
class Relic(BaseModel):
"""
Represents a relic.
Attributes:
- name (`str`): The name of the relic.
- rarity (`int`): The rarity of the relic.
- level (`int`): The level of the relic.
- main_property (`RelicProperty`): The main property of the relic.
- sub_property (list[`RelicProperty`]): The list of sub properties of the relic.
- icon (`str`): The relic icon.
"""
name: str
rarity: int
level: int
main_property: RelicProperty
sub_property: list[RelicProperty]
icon: str
class RelicSet(BaseModel):
"""
Represents a set of relics.
Attributes:
- name (`str`): The name of the relic set.
- icon (`str`): The relic set icon.
- desc (`int`): The description of the relic set.
"""
name: str
icon: str
desc: int

View File

@ -0,0 +1,64 @@
from pydantic import BaseModel, Field
class Player(BaseModel):
"""
Player basic info
Attributes:
- uid (`str`): The player's uid.
- name (`str`): The player's nickname.
- level (`int`): The player's Trailblaze level.
- icon (`str`): The player's profile picture.
- signature (`str`): The player's bio.
"""
uid: str
"""Player's uid"""
name: str
"""Player's nickname"""
level: int
"""Trailblaze level"""
icon: str
"""Profile picture"""
signature: str
"""Bio"""
class ForgottenHall(BaseModel):
"""The progress of the Forgotten Hall
Attributes:
- memory (`int`): The progress of the memory.
- memory_of_chaos_id (`int` | `None`): The ID of the memory of chaos, or None if not applicable.
- memory_of_chaos (`int` | `None`): The progress of the memory of chaos, or None if not applicable.
"""
memory: int | None = Field(None, alias="PreMazeGroupIndex")
"""The progress of the memory"""
memory_of_chaos_id: int | None = Field(None, alias="MazeGroupID")
memory_of_chaos: int | None = Field(None, alias="MazeGroupIndex")
"""The progress of the memory of chaos"""
class PlayerSpaceInfo(BaseModel):
"""Player details
Attributes:
- forgotten_hall (`ForgottenHall` | None): The progress of the Forgotten Hall, or None if not applicable.
- simulated_universes (`int`): The number of simulated universes passed.
- light_cones (`int`): The number of light cones owned.
- characters (`int`): The number of characters owned.
- achievements (`int`): The number of achievements unlocked.
"""
forgotten_hall: ForgottenHall | None = Field(None, alias="ChallengeData")
"""The progress of the Forgotten Hall"""
simulated_universes: int = Field(0, alias="PassAreaProgress")
"""Number of simulated universes passed"""
light_cones: int = Field(0, alias="LightConeCount")
"""Number of light cones owned"""
characters: int = Field(0, alias="AvatarCount")
"""Number of characters owned"""
achievements: int = Field(0, alias="AchievementCount")
"""Number of achievements unlocked"""

View File

@ -1,8 +1,10 @@
from typing import TypeVar
from .models import Character, StarrailInfoParsed
from .models.v1 import Character, StarrailInfoParsedV1
T = TypeVar("T")
ParsedData = TypeVar("ParsedData", StarrailInfoParsed, StarrailInfoParsedV1)
def remove_empty_dict(data: T) -> T:
@ -24,7 +26,7 @@ def remove_empty_dict(data: T) -> T:
return data
def replace_trailblazer_name(data: StarrailInfoParsed) -> StarrailInfoParsed:
def replace_trailblazer_name(data: StarrailInfoParsedV1) -> StarrailInfoParsedV1:
"""
Replaces the trailblazer name with the player's name.
@ -40,17 +42,17 @@ def replace_trailblazer_name(data: StarrailInfoParsed) -> StarrailInfoParsed:
return data
def remove_duplicate_character(data: StarrailInfoParsed) -> StarrailInfoParsed:
def remove_duplicate_character(data: ParsedData) -> ParsedData:
"""
Removes duplicate characters from the given StarrailInfoParsed data.
Args:
- data (`StarrailInfoParsed`): The input StarrailInfoParsed data.
- data (`ParsedData`): The input StarrailInfoParsed data.
Returns:
- `StarrailInfoParsed`: The updated StarrailInfoParsed data without duplicate characters.
- `ParsedData`: The updated StarrailInfoParsed data without duplicate characters.
"""
new_characters: list[Character] = []
new_characters = []
characters_ids: set[str] = set()
for character in data.characters:
if character.id not in characters_ids:
@ -60,19 +62,17 @@ def remove_duplicate_character(data: StarrailInfoParsed) -> StarrailInfoParsed:
return data
def merge_character_data(
new_data: StarrailInfoParsed, old_data: StarrailInfoParsed
) -> StarrailInfoParsed:
def merge_character_data(new_data: ParsedData, old_data: ParsedData) -> ParsedData:
"""
Append the old data characters to the list of new data characters.
The player's info from the old data will be omitted/discarded.
Args:
- new_data (`StarrailInfoParsed`): The new data to be merged.
- old_data (`StarrailInfoParsed`): The old data to merge into.
- new_data (`ParsedData`): The new data to be merged.
- old_data (`ParsedData`): The old data to merge into.
Returns:
- `StarrailInfoParsed`: The merged new data.
- `ParsedData`: The merged new data.
"""
for character in old_data.characters:
new_data.characters.append(character)

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "mihomo"
version = "0.0.1"
version = "1.1.0"
authors = [
{ name="KT", email="xns77477@gmail.com" },
]