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 import aiohttp
from .errors import HttpRequestError from .errors import HttpRequestError, InvalidParams, UserNotFound
from .models import StarrailInfoParsed from .models import StarrailInfoParsed
from .models.v1 import StarrailInfoParsedV1
from .tools import remove_empty_dict, replace_trailblazer_name from .tools import remove_empty_dict, replace_trailblazer_name
@ -48,6 +49,8 @@ class MihomoAPI:
self, self,
uid: int | str, uid: int | str,
language: Language, language: Language,
*,
params: dict[str, str] = {},
) -> typing.Any: ) -> typing.Any:
""" """
Makes an HTTP request to the API. Makes an HTTP request to the API.
@ -61,18 +64,32 @@ class MihomoAPI:
Raises: Raises:
HttpRequestError: If the HTTP request fails. 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) url = self.BASE_URL + "/" + str(uid)
params = {}
if language != Language.CHS: if language != Language.CHS:
params.update({"lang": language.value}) params.update({"lang": language.value})
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as response: async with session.get(url, params=params) as response:
if response.status == 200: match response.status:
return await response.json(encoding="utf-8") case 200:
else: return await response.json(encoding="utf-8")
raise HttpRequestError(response.status, str(response.reason)) 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: async def fetch_user(self, uid: int) -> StarrailInfoParsed:
""" """
@ -86,8 +103,23 @@ class MihomoAPI:
""" """
data = await self.request(uid, self.lang) data = await self.request(uid, self.lang)
data = remove_empty_dict(data)
data = StarrailInfoParsed.parse_obj(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) data = replace_trailblazer_name(data)
return data return data

View File

@ -1,9 +1,19 @@
class HttpRequestError(Exception): class BaseException(Exception):
"""Http request failed""" """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 status: int = 0
reason: str = "" reason: str = ""
message: str = ""
def __init__( def __init__(
self, self,
@ -18,3 +28,15 @@ class HttpRequestError(Exception):
self.reason = reason self.reason = reason
self.message = message self.message = message
super().__init__(message, *args) 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 .base import *
from .character import * from .character import *
from .combat import *
from .equipment import * from .equipment import *
from .player import * from .player import *

View File

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

View File

@ -2,66 +2,55 @@ from typing import Any
from pydantic import BaseModel, Field, root_validator from pydantic import BaseModel, Field, root_validator
from .combat import Attribute, Element, Path, Property
from .equipment import LightCone, Relic, RelicSet 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): class Trace(BaseModel):
""" """
Represents a character's skill trace. Represents a character's skill trace.
Attributes: Attributes:
- id (`int`): The ID of the trace.
- name (`str`): The name 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 (`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. - icon (`str`): The trace icon.
""" """
id: int
"""The ID of the trace"""
name: str name: str
"""The name of the trace""" """The name of the trace"""
level: int 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 type: str
"""The type of the trace""" """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 icon: str
"""The trace icon""" """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): class Character(BaseModel):
""" """
Represents a character. Represents a character.
@ -72,26 +61,25 @@ class Character(BaseModel):
- name (`str`): The character's name. - name (`str`): The character's name.
- rarity (`int`): The character's rarity. - rarity (`int`): The character's rarity.
- level (`int`): The character's level. - level (`int`): The character's level.
- Eidolon - ascension (`int`): Ascension level.
- eidolon (`int`): The character's eidolon rank. - 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 - Image
- icon (`str`): The character avatar image - icon (`str`): The character avatar image
- preview (`str`): The character's preview image. - preview (`str`): The character's preview image.
- portrait (`str`): The character's portrait image. - portrait (`str`): The character's portrait image.
- Combat type - Combat
- path (`str`): The character's path. - path (`Path`): The character's path.
- path_icon (`str`): The character's path icon. - element (`Element`): The character's element.
- element (`str`): The character's element.
- element_icon (`str`): The character's element icon.
- color (`str`): The character's element color.
- Equipment - Equipment
- traces (list[`Trace`]): The list of character's skill traces. - 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. - 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. - 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. - 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 (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 id: str
@ -102,52 +90,35 @@ class Character(BaseModel):
"""Character's rarity""" """Character's rarity"""
level: int level: int
"""Character's level""" """Character's level"""
ascension: int = Field(..., alias="promotion")
"""Ascension Level"""
eidolon: int = Field(..., alias="rank") eidolon: int = Field(..., alias="rank")
"""Character's eidolon 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 preview: str
"""Character preview image""" """Character preview image"""
portrait: str portrait: str
"""Character portrait image""" """Character portrait image"""
path: str path: Path
"""Character's path""" """Character's path"""
path_icon: str element: Element
"""Character's path icon"""
element: str
"""Character's element""" """Character's element"""
element_icon: str
"""Character's element icon"""
color: str traces: list[Trace] = Field(..., alias="skills")
"""Character's element color"""
traces: list[Trace] = Field(..., alias="skill")
"""The list of character's skill traces""" """The list of character's skill traces"""
light_cone: LightCone | None = None light_cone: LightCone | None = None
"""Character's light cone (weapon)""" """Character's light cone (weapon)"""
relics: list[Relic] | None = Field(None, alias="relic") relics: list[Relic] = []
"""The list of character's relics""" """The list of character's relics"""
relic_set: list[RelicSet] | None = None relic_sets: list[RelicSet] = []
"""The list of character's relic sets""" """The list of character's relic sets"""
stats: list[Stat] = Field(..., alias="property")
"""The list of character's stats"""
@root_validator(pre=True) attributes: list[Attribute]
def dict_to_list(cls, data: dict[str, Any]): """The list of character's attributes"""
# The keys of the original dict is not necessary, so remove them here. additions: list[Attribute]
if isinstance(data, dict) and data.get("relic") is not None: """The list of character's additional attributes"""
if isinstance(data["relic"], dict): properties: list[Property]
data["relic"] = list(data["relic"].values()) """The list of character's properties"""
return data
@property
def icon(self) -> str:
"""Character avatar image"""
return f"icon/character/{self.id}.png"

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 pydantic import BaseModel, Field
from .combat import Attribute, Path, Property
class LightCone(BaseModel): class LightCone(BaseModel):
""" """
Represents a light cone (weapon). Represents a light cone (weapon).
Attributes: Attributes:
- id (`int`): The ID of the light cone.
- name (`str`): The name of the light cone. - name (`str`): The name of the light cone.
- rarity (`int`): The rarity of the light cone. - rarity (`int`): The rarity of the light cone.
- superimpose (`int`): The superimpose rank of the light cone. - superimpose (`int`): The superimpose rank of the light cone.
- level (`int`): The level 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 name: str
"""The name of the light cone"""
rarity: int rarity: int
"""The rarity of the light cone"""
superimpose: int = Field(..., alias="rank") superimpose: int = Field(..., alias="rank")
"""The superimpose rank of the light cone"""
level: int level: int
"""The level of the light cone"""
ascension: int = Field(..., alias="promotion")
"""The ascension level of the light cone"""
icon: str icon: str
"""The light cone icon image"""
preview: str
class RelicProperty(BaseModel): """The light cone preview image"""
""" portrait: str
Represents a property of a relic. """The light cone portrait image"""
path: Path
Attributes: """The path of the light cone"""
- name (`str`): The name of the relic property. attributes: list[Attribute]
- value (`str`): The value of the relic property. """The list of attributes of the light cone"""
- icon (`str`): The property icon. properties: list[Property]
""" """The list of properties of the light cone"""
name: str
value: str
icon: str
class Relic(BaseModel): class Relic(BaseModel):
@ -40,7 +53,10 @@ class Relic(BaseModel):
Represents a relic. Represents a relic.
Attributes: Attributes:
- id (`int`): The ID of the relic.
- name (`str`): The name 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. - rarity (`int`): The rarity of the relic.
- level (`int`): The level of the relic. - level (`int`): The level of the relic.
- main_property (`RelicProperty`): The main property of the relic. - main_property (`RelicProperty`): The main property of the relic.
@ -48,12 +64,24 @@ class Relic(BaseModel):
- icon (`str`): The relic icon. - icon (`str`): The relic icon.
""" """
id: int
"""The ID of the relic"""
name: str 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 rarity: int
"""The rarity of the relic"""
level: int level: int
main_property: RelicProperty """The level of the relic"""
sub_property: list[RelicProperty] 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 icon: str
"""The relic icon"""
class RelicSet(BaseModel): class RelicSet(BaseModel):
@ -61,11 +89,17 @@ class RelicSet(BaseModel):
Represents a set of relics. Represents a set of relics.
Attributes: Attributes:
- id (`int`): The ID of the relic set.
- name (`str`): The name of the relic set. - name (`str`): The name of the relic set.
- icon (`str`): The relic set icon. - desc (`str`): The description of the relic set.
- desc (`int`): 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 name: str
icon: str """The name of the relic set"""
desc: int 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): class Avatar(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""" """Profile picture"""
signature: str
"""Bio""" id: int
name: str
icon: str
class ForgottenHall(BaseModel): class ForgottenHall(BaseModel):
@ -30,21 +14,31 @@ class ForgottenHall(BaseModel):
Attributes: Attributes:
- memory (`int`): The progress of the memory. - 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_id (`int`): 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 (`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""" """The progress of the memory"""
memory_of_chaos_id: int | None = Field(None, alias="MazeGroupID") memory_of_chaos_id: int = Field(..., alias="maze_group_id")
memory_of_chaos: int | None = Field(None, alias="MazeGroupIndex") """The ID of the memory of chaos"""
memory_of_chaos: int = Field(..., alias="maze_group_index")
"""The progress of the memory of chaos""" """The progress of the memory of chaos"""
class PlayerSpaceInfo(BaseModel): class Player(BaseModel):
"""Player details """
Player basic info
Attributes: 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. - forgotten_hall (`ForgottenHall` | None): The progress of the Forgotten Hall, or None if not applicable.
- simulated_universes (`int`): The number of simulated universes passed. - simulated_universes (`int`): The number of simulated universes passed.
- light_cones (`int`): The number of light cones owned. - light_cones (`int`): The number of light cones owned.
@ -52,13 +46,28 @@ class PlayerSpaceInfo(BaseModel):
- achievements (`int`): The number of achievements unlocked. - 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""" """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""" """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""" """Number of light cones owned"""
characters: int = Field(0, alias="AvatarCount") characters: int = Field(0, alias="avatar_count")
"""Number of characters owned""" """Number of characters owned"""
achievements: int = Field(0, alias="AchievementCount") achievements: int = Field(0, alias="achievement_count")
"""Number of achievements unlocked""" """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 typing import TypeVar
from .models import Character, StarrailInfoParsed from .models import Character, StarrailInfoParsed
from .models.v1 import Character, StarrailInfoParsedV1
T = TypeVar("T") T = TypeVar("T")
ParsedData = TypeVar("ParsedData", StarrailInfoParsed, StarrailInfoParsedV1)
def remove_empty_dict(data: T) -> T: def remove_empty_dict(data: T) -> T:
@ -24,7 +26,7 @@ def remove_empty_dict(data: T) -> T:
return data 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. Replaces the trailblazer name with the player's name.
@ -40,17 +42,17 @@ def replace_trailblazer_name(data: StarrailInfoParsed) -> StarrailInfoParsed:
return data return data
def remove_duplicate_character(data: StarrailInfoParsed) -> StarrailInfoParsed: def remove_duplicate_character(data: ParsedData) -> ParsedData:
""" """
Removes duplicate characters from the given StarrailInfoParsed data. Removes duplicate characters from the given StarrailInfoParsed data.
Args: Args:
- data (`StarrailInfoParsed`): The input StarrailInfoParsed data. - data (`ParsedData`): The input StarrailInfoParsed data.
Returns: 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() characters_ids: set[str] = set()
for character in data.characters: for character in data.characters:
if character.id not in characters_ids: if character.id not in characters_ids:
@ -60,19 +62,17 @@ def remove_duplicate_character(data: StarrailInfoParsed) -> StarrailInfoParsed:
return data return data
def merge_character_data( def merge_character_data(new_data: ParsedData, old_data: ParsedData) -> ParsedData:
new_data: StarrailInfoParsed, old_data: StarrailInfoParsed
) -> StarrailInfoParsed:
""" """
Append the old data characters to the list of new data characters. Append the old data characters to the list of new data characters.
The player's info from the old data will be omitted/discarded. The player's info from the old data will be omitted/discarded.
Args: Args:
- new_data (`StarrailInfoParsed`): The new data to be merged. - new_data (`ParsedData`): The new data to be merged.
- old_data (`StarrailInfoParsed`): The old data to merge into. - old_data (`ParsedData`): The old data to merge into.
Returns: Returns:
- `StarrailInfoParsed`: The merged new data. - `ParsedData`: The merged new data.
""" """
for character in old_data.characters: for character in old_data.characters:
new_data.characters.append(character) new_data.characters.append(character)

View File

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