diff --git a/pyproject.toml b/pyproject.toml index f95e119..e7bac04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ requires-python = ">=3.10" dependencies = [ "hishel[httpx]>=1.1.5", "httpx>=0.28.1", - "seerapi-models>=1.0.2", + "seerapi-models>=1.2.2,<1.3.0", "typing-extensions>=4.15.0", ] diff --git a/seerapi/_client.py b/seerapi/_client.py index c38055b..c52e09c 100644 --- a/seerapi/_client.py +++ b/seerapi/_client.py @@ -5,14 +5,23 @@ from hishel.httpx import AsyncCacheClient from httpx import URL from httpx._urls import QueryParams -from seerapi_models.common import ResourceRef - -from seerapi._model_map import MODEL_MAP, ModelName, T_ModelInstance +from seerapi_models.common import NamedData, ResourceRef + +from seerapi._model_map import ( + MODEL_MAP, + ModelName, + NamedModelName, + T_ModelInstance, + T_NamedModelInstance, +) from seerapi._models import PagedResponse, PageInfo ResourceArg: TypeAlias = ( ModelName | type[T_ModelInstance] | ResourceRef[T_ModelInstance] ) +NamedResourceArg: TypeAlias = ( + NamedModelName | type[T_NamedModelInstance] | ResourceRef[T_NamedModelInstance] +) def _parse_url_params(url: str) -> QueryParams: @@ -145,3 +154,19 @@ async def create_generator(page_info: PageInfo): page_info = paged_response.next return create_generator(PageInfo(offset=0, limit=10)) + + async def get_by_name( + self, resource_name: NamedResourceArg[T_NamedModelInstance], name: str + ) -> NamedData[T_NamedModelInstance]: + res_name = self._get_resource_name(resource_name) + model_type = MODEL_MAP[res_name] + response = await self._client.get(f'/{res_name}/{name}') + response.raise_for_status() + return NamedData.model_validate( + { + 'data': { + id: model_type.model_validate(item) + for id, item in response.json()['data'].items() + } + } + ) diff --git a/seerapi/_client.pyi b/seerapi/_client.pyi index 3556266..c9411e9 100644 --- a/seerapi/_client.pyi +++ b/seerapi/_client.pyi @@ -5,9 +5,9 @@ from typing_extensions import Self from hishel.httpx import AsyncCacheClient from httpx import URL import seerapi_models as M -from seerapi_models.common import ResourceRef +from seerapi_models.common import NamedData, ResourceRef -from seerapi._model_map import T_ModelInstance +from seerapi._model_map import T_ModelInstance, T_NamedModelInstance from seerapi._models import PagedResponse, PageInfo class SeerAPI: @@ -575,3 +575,155 @@ class SeerAPI: async def list( self, resource_name: type[T_ModelInstance] ) -> AsyncGenerator[T_ModelInstance, None]: ... + @overload + async def get_by_name( + self, resource_name: Literal['battle_effect'], name: str + ) -> NamedData[M.BattleEffect]: ... + @overload + async def get_by_name( + self, resource_name: Literal['battle_effect_type'], name: str + ) -> NamedData[M.BattleEffectCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet_effect'], name: str + ) -> NamedData[M.PetEffect]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet_effect_group'], name: str + ) -> NamedData[M.PetEffectGroup]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet_variation'], name: str + ) -> NamedData[M.VariationEffect]: ... + @overload + async def get_by_name( + self, resource_name: Literal['energy_bead'], name: str + ) -> NamedData[M.EnergyBead]: ... + @overload + async def get_by_name( + self, resource_name: Literal['equip'], name: str + ) -> NamedData[M.Equip]: ... + @overload + async def get_by_name( + self, resource_name: Literal['suit'], name: str + ) -> NamedData[M.Suit]: ... + @overload + async def get_by_name( + self, resource_name: Literal['equip_type'], name: str + ) -> NamedData[M.EquipType]: ... + @overload + async def get_by_name( + self, resource_name: Literal['soulmark_tag'], name: str + ) -> NamedData[M.SoulmarkTagCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['element_type'], name: str + ) -> NamedData[M.ElementType]: ... + @overload + async def get_by_name( + self, resource_name: Literal['element_type_combination'], name: str + ) -> NamedData[M.TypeCombination]: ... + @overload + async def get_by_name( + self, resource_name: Literal['item'], name: str + ) -> NamedData[M.Item]: ... + @overload + async def get_by_name( + self, resource_name: Literal['item_category'], name: str + ) -> NamedData[M.ItemCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['gem'], name: str + ) -> NamedData[M.Gem]: ... + @overload + async def get_by_name( + self, resource_name: Literal['gem_category'], name: str + ) -> NamedData[M.GemCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['skill_activation_item'], name: str + ) -> NamedData[M.SkillActivationItem]: ... + @overload + async def get_by_name( + self, resource_name: Literal['skill_stone'], name: str + ) -> NamedData[M.SkillStone]: ... + @overload + async def get_by_name( + self, resource_name: Literal['skill_stone_category'], name: str + ) -> NamedData[M.SkillStoneCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['mintmark'], name: str + ) -> NamedData[M.Mintmark]: ... + @overload + async def get_by_name( + self, resource_name: Literal['ability_mintmark'], name: str + ) -> NamedData[M.AbilityMintmark]: ... + @overload + async def get_by_name( + self, resource_name: Literal['skill_mintmark'], name: str + ) -> NamedData[M.SkillMintmark]: ... + @overload + async def get_by_name( + self, resource_name: Literal['universal_mintmark'], name: str + ) -> NamedData[M.UniversalMintmark]: ... + @overload + async def get_by_name( + self, resource_name: Literal['mintmark_class'], name: str + ) -> NamedData[M.MintmarkClassCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['mintmark_type'], name: str + ) -> NamedData[M.MintmarkTypeCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet'], name: str + ) -> NamedData[M.Pet]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet_gender'], name: str + ) -> NamedData[M.PetGenderCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet_vipbuff'], name: str + ) -> NamedData[M.PetVipBuffCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet_mount_type'], name: str + ) -> NamedData[M.PetMountTypeCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet_skin'], name: str + ) -> NamedData[M.PetSkin]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet_archive_story_book'], name: str + ) -> NamedData[M.PetArchiveStoryBook]: ... + @overload + async def get_by_name( + self, resource_name: Literal['pet_encyclopedia_entry'], name: str + ) -> NamedData[M.PetEncyclopediaEntry]: ... + @overload + async def get_by_name( + self, resource_name: Literal['skill'], name: str + ) -> NamedData[M.Skill]: ... + @overload + async def get_by_name( + self, resource_name: Literal['skill_hide_effect'], name: str + ) -> NamedData[M.SkillHideEffect]: ... + @overload + async def get_by_name( + self, resource_name: Literal['skill_category'], name: str + ) -> NamedData[M.SkillCategory]: ... + @overload + async def get_by_name( + self, resource_name: Literal['skill_effect_type_tag'], name: str + ) -> NamedData[M.SkillEffectTypeTag]: ... + @overload + async def get_by_name( + self, resource_name: type[T_NamedModelInstance], name: str + ) -> NamedData[T_NamedModelInstance]: ... + @overload + async def get_by_name( + self, resource_name: ResourceRef[T_NamedModelInstance], name: str + ) -> NamedData[T_NamedModelInstance]: ... diff --git a/seerapi/_model_map.py b/seerapi/_model_map.py index 542f422..1b60c8f 100644 --- a/seerapi/_model_map.py +++ b/seerapi/_model_map.py @@ -3,8 +3,7 @@ import seerapi_models as M from seerapi_models.common import BaseResModel -# 所有可用的模型路径名称 -ModelName: TypeAlias = Literal[ +NamedModelName: TypeAlias = Literal[ 'battle_effect', 'battle_effect_type', 'pet_effect', @@ -14,8 +13,6 @@ 'equip', 'suit', 'equip_type', - 'equip_effective_occasion', - 'soulmark', 'soulmark_tag', 'element_type', 'element_type_combination', @@ -23,7 +20,6 @@ 'item_category', 'gem', 'gem_category', - 'gem_generation_category', 'skill_activation_item', 'skill_stone', 'skill_stone_category', @@ -33,30 +29,77 @@ 'universal_mintmark', 'mintmark_class', 'mintmark_type', - 'mintmark_rarity', 'pet', - 'pet_class', 'pet_gender', 'pet_vipbuff', 'pet_mount_type', 'pet_skin', - 'pet_skin_category', - 'pet_archive_story_entry', 'pet_archive_story_book', 'pet_encyclopedia_entry', 'skill', - 'skill_effect_type', - 'skill_effect_param', 'skill_hide_effect', 'skill_category', 'skill_effect_type_tag', +] + +# 所有可用的模型路径名称 +ModelName: TypeAlias = Literal[ + NamedModelName, + 'equip_effective_occasion', + 'soulmark', + 'gem_generation_category', + 'mintmark_rarity', + 'pet_class', + 'pet_skin_category', + 'pet_archive_story_entry', + 'skill_effect_type', + 'skill_effect_param', 'eid_effect', ] ModelInstance: TypeAlias = BaseResModel +NamedModelInstance: TypeAlias = ( + M.BattleEffect + | M.BattleEffectCategory + | M.PetEffect + | M.PetEffectGroup + | M.VariationEffect + | M.EnergyBead + | M.Equip + | M.Suit + | M.EquipType + | M.SoulmarkTagCategory + | M.ElementType + | M.TypeCombination + | M.Item + | M.ItemCategory + | M.Gem + | M.GemCategory + | M.SkillActivationItem + | M.SkillStone + | M.SkillStoneCategory + | M.Mintmark + | M.AbilityMintmark + | M.SkillMintmark + | M.UniversalMintmark + | M.MintmarkClassCategory + | M.MintmarkTypeCategory + | M.Pet + | M.PetGenderCategory + | M.PetVipBuffCategory + | M.PetMountTypeCategory + | M.PetSkin + | M.PetArchiveStoryBook + | M.PetEncyclopediaEntry + | M.Skill + | M.SkillHideEffect + | M.SkillCategory + | M.SkillEffectTypeTag +) ModelType: TypeAlias = type[ModelInstance] T_ModelInstance = TypeVar('T_ModelInstance', bound=ModelInstance) +T_NamedModelInstance = TypeVar('T_NamedModelInstance', bound=NamedModelInstance) MODEL_MAP: dict[ModelName, ModelType] = { 'battle_effect': M.BattleEffect,