From 67815e510643c7f1a2bb5a2c66c3d3536ba62103 Mon Sep 17 00:00:00 2001 From: Deviant <112615715+DEViantUA@users.noreply.github.com> Date: Sat, 18 May 2024 00:40:10 +0300 Subject: [PATCH] 2.1.9 Added: * EnkaApi support - Now you can receive data from Enka and not from MiHoMo. * Added the ability to set your own color settings for the character. (https://github.com/DEViantUA/StarRailCard/blob/main/Examples/color.py) In the future we plan to add: * Support for converting Enka.py data from Seria to generate cards. * Full API wrapper for both postals Signed-off-by: Deviant <112615715+DEViantUA@users.noreply.github.com> --- starrailcard/__init__.py | 2 +- starrailcard/client.py | 24 +- starrailcard/src/api/api.py | 2 - starrailcard/src/api/enka.py | 60 +- starrailcard/src/api/enka_parsed.py | 1012 +++++++++-------- starrailcard/src/generator/style_card.py | 9 +- .../src/generator/style_relict_score.py | 14 +- starrailcard/src/generator/style_ticket.py | 14 +- .../__pycache__/StarRailCard.cpython-312.pyc | Bin 0 -> 7142 bytes .../__pycache__/api_mihomo.cpython-312.pyc | Bin 0 -> 19407 bytes .../model/__pycache__/style.cpython-312.pyc | Bin 0 -> 6999 bytes .../ukrainization_model.cpython-312.pyc | Bin 0 -> 975 bytes .../__pycache__/utils_model.cpython-312.pyc | Bin 0 -> 1609 bytes starrailcard/src/model/api_mihomo.py | 10 +- starrailcard/src/tools/options.py | 11 +- starrailcard/src/tools/pill/image_control.py | 6 +- 16 files changed, 633 insertions(+), 531 deletions(-) create mode 100644 starrailcard/src/model/__pycache__/StarRailCard.cpython-312.pyc create mode 100644 starrailcard/src/model/__pycache__/api_mihomo.cpython-312.pyc create mode 100644 starrailcard/src/model/__pycache__/style.cpython-312.pyc create mode 100644 starrailcard/src/model/__pycache__/ukrainization_model.cpython-312.pyc create mode 100644 starrailcard/src/model/__pycache__/utils_model.cpython-312.pyc diff --git a/starrailcard/__init__.py b/starrailcard/__init__.py index eeab4ae..91a8ec6 100644 --- a/starrailcard/__init__.py +++ b/starrailcard/__init__.py @@ -37,7 +37,7 @@ __title__ = 'StarRailCard.py' __author__ = 'DeviantUa' -__version__ = '2.1.8' +__version__ = '2.1.9' __license__ = 'MIT' __copyright__ = 'Copyright 2024-present DeviantUa' diff --git a/starrailcard/client.py b/starrailcard/client.py index f7f9ca5..c839caf 100644 --- a/starrailcard/client.py +++ b/starrailcard/client.py @@ -17,7 +17,7 @@ class Card: def __init__(self, lang: str = "en", character_art = None, character_id = None, seeleland: bool = False, user_font = None, save: bool = False, asset_save: bool = False, boost_speed: bool = False, remove_logo: bool = False, cache = {"maxsize": 150, "ttl": 300}, enka: bool = False, api_data: api_mihomo.MiHoMoApi = None, proxy: str = None, - user_agent: str = None): + user_agent: str = None, color: dict = None): """Main class for generating cards Args: @@ -34,6 +34,7 @@ def __init__(self, lang: str = "en", character_art = None, character_id = None, api_data (MiHoMoApi, optional): Pass your data received via: api.ApiMiHoMo(uid,"en").get() proxy (str, optional): Proxy as a string: http://111.111.111.111:8888 user_agent (str, optional): Custom User-Agent. + color: (dict), Dictionary: {"character_id_1": (255,255,255,255), "character_id_2": (0,0,0,255),...}. """ self.lang = lang self.character_art = character_art @@ -49,6 +50,7 @@ def __init__(self, lang: str = "en", character_art = None, character_id = None, self.api_data = api_data self.proxy = proxy self.user_agent = options.get_user_agent(user_agent) + self.color = color async def __aenter__(self): @@ -80,6 +82,9 @@ async def __aenter__(self): image_control._boost_speed = self.boost_speed + if isinstance(self.color, dict): + self.color = await options.get_color_user(self.color) + if self.remove_logo: print(""" Thank you for using our StarRailCard! @@ -123,7 +128,7 @@ async def create_profile(self, uid: Union[int,str], style: bool = 1, hide_uid: b """Function for generating a user profile card Args: - uid (int): UID of the user in the game. + uid (Union[int,str]): UID of the user in the game. style (int, optional): Card style. Defaults to 1. hide_uid (bool, optional): Hide UID. Defaults to False. background (str, optional): Link to custom card background. Defaults to None. @@ -137,6 +142,7 @@ async def create_profile(self, uid: Union[int,str], style: bool = 1, hide_uid: b try: data = await enka.ApiEnkaNetwork(uid, lang= self.lang).get() except Exception as e: + print(e) print("To use the EnkaNetwork API you need to download/update the asset\nExample: await enka.ApiEnkaNetwork().update_assets()") data = await api.ApiMiHoMo(uid, lang= self.lang, force_update = force_update, user_agent= self.user_agent).get() else: @@ -179,7 +185,7 @@ async def create_profile(self, uid: Union[int,str], style: bool = 1, hide_uid: b return StarRailCard.StarRail(**response) - async def create(self, uid: Union[int,str], style: bool = 1, hide_uid: bool = False, force_update: bool = False, style_settings = None, log: bool = False): + async def create(self, uid: Union[int,str], style: int = 1, hide_uid: bool = False, force_update: bool = False, style_settings = None, log: bool = False): """Function for generating character cards Args: @@ -198,6 +204,7 @@ async def create(self, uid: Union[int,str], style: bool = 1, hide_uid: bool = Fa try: data = await enka.ApiEnkaNetwork(uid, lang= self.lang).get() except Exception as e: + print(e) print("To use the EnkaNetwork API you need to download/update the asset") data = await api.ApiMiHoMo(uid, lang= self.lang, force_update = force_update, user_agent= self.user_agent).get() else: @@ -241,16 +248,21 @@ async def get_result(key): if not str(key.id) in self.character_id: return + if self.color: + color = self.color.get(str(key.id)) + else: + color = None + art = None if self.character_art: if str(key.id) in self.character_art: art = self.character_art[str(key.id)] if style == 1: - result.append(await style_relict_score.Create(key,self.translateLang,art,hide_uid,uid, self.seeleland,self.remove_logo).start()) + result.append(await style_relict_score.Create(key,self.translateLang,art,hide_uid,uid, self.seeleland,self.remove_logo, color).start()) elif style == 2: - result.append(await style_ticket.Create(key,self.translateLang,art,hide_uid,uid, self.seeleland,self.remove_logo).start()) + result.append(await style_ticket.Create(key,self.translateLang,art,hide_uid,uid, self.seeleland,self.remove_logo, color).start()) elif style == 3: - result.append(await style_card.Create(key,self.translateLang,art,hide_uid,uid, self.seeleland,self.remove_logo).start()) + result.append(await style_card.Create(key,self.translateLang,art,hide_uid,uid, self.seeleland,self.remove_logo, color).start()) except Exception as e: print(f"Error in get_result for character {key.id}: {e}") diff --git a/starrailcard/src/api/api.py b/starrailcard/src/api/api.py index 1c5a671..50b4c37 100644 --- a/starrailcard/src/api/api.py +++ b/starrailcard/src/api/api.py @@ -4,8 +4,6 @@ from ..tools import http, translator, ukrainization, options from ..model import api_mihomo from .error import StarRailCardError -from ..tools.json_data import JsonManager -from ..tools.enums import PathData _API_MIHOMO: str = "https://api.mihomo.me/sr_info_parsed/{uid}" _API_MIHOMO_NAKED: str = "https://api.mihomo.me/sr_info/{uid}" diff --git a/starrailcard/src/api/enka.py b/starrailcard/src/api/enka.py index 1aad2ea..7a97469 100644 --- a/starrailcard/src/api/enka.py +++ b/starrailcard/src/api/enka.py @@ -13,33 +13,25 @@ from ..model import api_mihomo _API_ENKA = "https://enka.network/api/hsr/uid/{uid}" -_ASSETS_ENKA = "https://raw.githubusercontent.com/EnkaNetwork/API-docs/master/store/hsr/{asset}.json" _INDEX_MIHOMO = "https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/index_new/{lang}/{index}.json" -_ASSET_NAME = [ +_INDEX_NAME = [ "avatars", + "character_promotions", + "character_ranks", + "character_skill_trees", + "character_skills", "characters", - "meta", - "ranks", - "relics", - "skills", - "skilltree", - "weps", - "hsr" -] - -_INDEX_NAME = [ - "paths", "elements", - "character_skills", - "character_skill_trees", - "properties", + "light_cone_promotions", "light_cone_ranks", - "relics", + "light_cones", + "paths", + "properties", "relic_main_affixes", - "relic_sub_affixes", "relic_sets", - "character_ranks" + "relic_sub_affixes", + "relics" ] @@ -62,7 +54,8 @@ async def get(self): if self.parsed: data = await AssetEnkaParsed(data).collect() - + else: + return data except aiohttp.ClientConnectionError: raise StarRailCardError(1, "Server is not responding") except aiohttp.ClientResponseError as e: @@ -71,31 +64,22 @@ async def get(self): if self.ua_lang: await ukrainization.TranslateDataManager().load_translate_data() - return api_mihomo.MiHoMoApi(player=data["player"], characters=data["characters"], dont_update_link=True) + return api_mihomo.MiHoMoApi(player=data["player"], characters=data["characters"], dont_update_link = False) - async def update_assets(self): - print("===START UPDATE INDEX===") + async def update_assets(self, lang = None): + print("===START UPDATE ASSET===") for name in _INDEX_NAME: - for lang in SUPPORTED_LANGUAGES: - if lang == "ua": + for langs in SUPPORTED_LANGUAGES: + if langs == "ua": continue + if not lang is None: + if langs != lang: + continue data = await http.AioSession.get(_INDEX_MIHOMO.format(lang = lang, index=name)) if not os.path.exists(PathData.ENKA_INDEX.value / lang): os.makedirs(PathData.ENKA_INDEX.value / lang) await JsonManager(PathData.ENKA_INDEX.value / lang /f"{name}.json").write(data) print(f"- Updated file: {name}") - print("===END UPDATE INDEX===") - print() - print("===START UPDATE ASSETS===") - for name in _ASSET_NAME: - if name == "hsr": - data = await http.AioSession.get(_ASSETS_ENKA.format(asset=name)) - else: - data = await http.AioSession.get(_ASSETS_ENKA.format(asset=f"honker_{name}")) - await JsonManager(PathData.ENKA.value / f"{name}.json").write(data) - print(f"- Updated file: {name}") - - print("===END UPDATE ASSETS===") - + print("===END UPDATE ASSET===") \ No newline at end of file diff --git a/starrailcard/src/api/enka_parsed.py b/starrailcard/src/api/enka_parsed.py index 282f881..fd739e4 100644 --- a/starrailcard/src/api/enka_parsed.py +++ b/starrailcard/src/api/enka_parsed.py @@ -1,543 +1,631 @@ -from ..tools.json_data import JsonManager -from ..tools.enums import PathData import json +import math +from pathlib import Path +from copy import deepcopy -_DEFFAULT_ASSETS_LINK = "https://enka.network/ui/hsr/" - -_DEFFAULT_ASSETS_HSR_LINK = "https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/{catalog}/{key}.png" -_DEFFAULT_ASSETS_HSR_LINK_SKILL = "https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/" +from ..tools.json_data import JsonManager +from ..tools.enums import PathData +CHARACTERS_LINK_ICON = "icon/{catalog}/{character_id}.png" +CHARACTERS_LINK_IMAGE = "image/{catalog}/{character_id}.png" +ENKA_INDEX = Path(__file__).parent / "assets" / "enka_api" / "index" +ENKA = Path(__file__).parent.parent / "assets" / "enka_api" -fiel = { +field = { "BaseHP": "hp", "BaseAttack": "atk", "BaseDefence": "def", } -proc = { - "false": False, - 'true': True -} class AssetEnkaParsed: - def __init__(self, data: dict, lang: str = "en") -> None: - self.data = data["detailInfo"] - self.lang = lang - - async def creat_player(self): - avatar_link = await AssetEnkaParsed.get_icon_avatar(self.data["headIcon"]) - self.player = { - "uid": 0,#self.data["uid"], - "nickname": self.data["nickname"], - "level": self.data["level"], - "is_display": self.data["isDisplayAvatar"], - "avatar": {"id": str(self.data["headIcon"]), "name": avatar_link.split("/")[-1:][0], "icon": _DEFFAULT_ASSETS_LINK + avatar_link}, - "signature": self.data["signature"], - "friend_count": self.data["friendCount"], - "world_level": self.data["worldLevel"], - "space_info": { - "pass_area_progress": self.data["recordInfo"]["challengeInfo"]["scheduleMaxLevel"], - "light_cone_count": self.data["recordInfo"]["equipmentCount"], - "avatar_count": self.data["recordInfo"]["avatarCount"], - "achievement_count": self.data["recordInfo"]["achievementCount"], - } + def __init__(self, data) -> None: + self.data = data + self.lang = "en" + + async def load_assets(self): + self.character = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "characters.json").read() + self.element = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "elements.json").read() + self.path = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "paths.json").read() + self.character_rank = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "character_ranks.json").read() + self.character_promotions = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "character_promotions.json").read() + self.skill = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "character_skills.json").read() + self.skill_trees_info = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "character_skill_trees.json").read() + self.light_cone = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "light_cones.json").read() + self.propertie = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "properties.json").read() + self.light_cone_rank = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "light_cone_ranks.json").read() + self.relics = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "relics.json").read() + self.relics_set = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "relic_sets.json").read() + self.relic_main_affixes = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "relic_main_affixes.json").read() + self.relic_sub_affixes = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "relic_sub_affixes.json").read() + self.relic_set = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "relic_sets.json").read() + self.light_cone_promotion = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "light_cone_promotions.json").read() + self.avatar = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "avatars.json").read() + + async def collect(self): + await self.load_assets() + data = { + "player": await self.get_player(), + "characters": [await self.get_character(key) for key in self.data["detailInfo"]["avatarDetailList"]] } + return data - async def creat_avatar(self): - self.charter = [] - for key in self.data["avatarDetailList"]: - charter_info = await AssetEnkaParsed.get_info_character(key["avatarId"],self.lang) - path_info = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "paths.json").read() - element_info = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "elements.json").read() - skills_info = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "character_skills.json").read() - skill_trees_info = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "character_skill_trees.json").read() - character_ranks = await JsonManager(PathData.ENKA_INDEX.value / self.lang / "character_ranks.json").read() - light_cone = await AssetEnkaParsed.add_light_cone(key,self.lang,path_info) - relics = [await AssetEnkaParsed.add_relics(keys, self.lang) for keys in key["relicList"]] - skill_trees = await AssetEnkaParsed.add_skill_trees(skill_trees_info,charter_info,skills_info,key) + async def get_memory_data(self): + data = self.data["detailInfo"]["recordInfo"]["challengeInfo"] + return { + "chaos_id": data.get("scheduleGroupId", 0), + "chaos_level": data.get("scheduleMaxLevel", 0), + "abyss_level": data.get("abyssLevel", 0), + "abyss_star_count": data.get("abyssStarCount", 0), + } + pass + + async def get_space_info(self): + data = self.data["detailInfo"]["recordInfo"] + return { + "relic_count": data["relicCount"], + "music_count": data["musicCount"], + "book_count": data["bookCount"], + "universe_level": data["maxRogueChallengeScore"], + "light_cone_count": data["equipmentCount"], + "avatar_count": data["avatarCount"], + "achievement_count": data["achievementCount"], + "memory_data": await self.get_memory_data() + } + async def get_avatar_info(self, avatar_id): + if str(avatar_id) not in self.avatar: + return None + return { + "id": avatar_id, + "name": self.avatar[avatar_id]["name"], + "icon": self.avatar[avatar_id]["icon"] + } + async def get_player(self): - data = { - "id": key["avatarId"], - "name": charter_info["AvatarName"]["name"], - "rarity": charter_info["Rarity"], - "rank": key.get("rank",0), - "level": key["level"], - "promotion": key["promotion"], - "icon": _DEFFAULT_ASSETS_HSR_LINK.format(catalog = "icon/avatar", key = key["avatarId"]), - "preview": _DEFFAULT_ASSETS_HSR_LINK.format(catalog = "image/character_preview", key = key["avatarId"]), - "portrait": _DEFFAULT_ASSETS_HSR_LINK.format(catalog = "image/character_portrait", key = key["avatarId"]), - "path": await AssetEnkaParsed.get_path(path_info,charter_info), - "rank_icons": [_DEFFAULT_ASSETS_HSR_LINK_SKILL + character_ranks.get(str(key))["icon"] for key in charter_info["RankIDList"]], - "element": await AssetEnkaParsed.get_element(element_info,charter_info["Element"]), - "skills": await AssetEnkaParsed.add_skills(element_info,charter_info,key, self.lang), - "skill_trees": skill_trees, - "light_cone": light_cone if light_cone != {} else None, - "relics": relics, - "relic_sets": await AssetEnkaParsed.add_relict_sets(key["relicList"], self.lang), - "additions": await AssetEnkaParsed.add_additions(key, self.lang, light_cone.get("attributes",[])), - "attributes": await AssetEnkaParsed.add_attributes(relics, skill_trees, self.lang), - "properties": None, #await AssetEnkaParsed.add_properties(key["skillTreeList"], self.lang), - "pos": [key.get("pos", 0)] - } + return { + "uid": self.data["detailInfo"]["uid"], + "nickname": self.data["detailInfo"]["nickname"], + "level": self.data["detailInfo"]["level"], + "world_level": self.data["detailInfo"]["worldLevel"], + "friend_count": self.data["detailInfo"]["friendCount"], + "avatar": await self.get_avatar_info(str(self.data["detailInfo"]["headIcon"])), + "signature": self.data["detailInfo"]["signature"], + "is_display": self.data["detailInfo"]["isDisplayAvatar"], + "space_info": await self.get_space_info() - self.charter.append(data) - - + + } - async def collect(self): - await self.creat_player() - await self.creat_avatar() - - await JsonManager(PathData.ENKA.value / "TEST_API.json").write(self.charter) + async def get_light_cone(self, data): + info = self.light_cone.get(str(data.get("tid"))) + + id = str(data.get("tid")) + name = info.get("name") + rarity = info.get("rarity") + rank = data.get("rank") + level = data.get("level") + promotion = data.get("promotion") + icon = info.get("icon") + preview = info.get("preview") + portrait = info.get("portrait") + path = await self.get_path(info.get("path")) + attributes = await self.get_light_cone_attribute_from_promotion(id, promotion, level) + properties = await self.get_light_cone_property_from_rank(id, rank) - return {"player": self.player, "characters":self.charter} - ''' - @classmethod - async def add_properties(cls, skillTreeList, lang): - property = await JsonManager(PathData.ENKA_INDEX.value / lang / "properties.json").read() - skill_trees_info = await JsonManager(PathData.ENKA_INDEX.value / lang / "character_skill_trees.json").read() - ''' + return { + "id": id, + "name": name, + "rarity": rarity, + "rank": rank, + "level": level, + "promotion": promotion, + "icon": icon, + "preview": preview, + "portrait": portrait, + "path": path, + "attributes": attributes, + "properties": properties, + } - @classmethod - async def add_attributes(cls, relict, skill, lang): - skill_trees_info = await JsonManager(PathData.ENKA_INDEX.value / lang / "character_skill_trees.json").read() - property = await JsonManager(PathData.ENKA_INDEX.value / lang / "properties.json").read() - - data = {} - - for key in relict: - if not key["main_affix"]["field"] in data: - data[key["main_affix"]["field"]] = { - "field": key["main_affix"]["field"], - "name": key["main_affix"]["name"], - "icon": key["main_affix"]["icon"], - "value": key["main_affix"]["value"], - "display": AssetEnkaParsed.get_display(key["main_affix"]["value"], key["main_affix"]["percent"]), - "percent": key["main_affix"]["percent"] - } - else: - data[key["main_affix"]["field"]]["value"] += key["main_affix"]["value"] - data[key["main_affix"]["field"]]["display"] = AssetEnkaParsed.get_display(data[key["main_affix"]["field"]]["value"], key["main_affix"]["percent"]) - - for keys in key["sub_affix"]: - if not keys["field"] in data: - data[keys["field"]] = { - "field": keys["field"], - "name": keys["name"], - "icon": keys["icon"], - "value": keys["value"], - "display": AssetEnkaParsed.get_display(keys["value"], keys["percent"]), - "percent": keys["percent"] - } - else: - - data[keys["field"]]["value"] += keys["value"] - data[keys["field"]]["display"] = AssetEnkaParsed.get_display(data[keys["field"]]["value"], keys["percent"]) - - for key in skill: - info = skill_trees_info.get(str(key["id"]))["levels"] - if info == []: + async def get_light_cone_property_from_rank(self, id: str, rank: int): + if id not in self.light_cone_rank: + return [] + if rank not in range(1, 5 + 1): + return [] + properties = [] + + for i in self.light_cone_rank[id]["properties"][rank - 1]: + if i["type"] not in self.propertie: continue - info = info[0]["properties"] - if info == []: + property = self.propertie[i["type"]] + properties.append( + { + "type": i["type"], + "field": property["field"], + "name": property["name"], + "icon": property["icon"], + "value": i["value"], + "display": await self.get_display(i["value"], property["percent"]), + "percent": property["percent"], + } + ) + return properties + + async def get_light_cone_attribute_from_promotion(self, id: str, promotion: int, level: int): + if id not in self.light_cone_promotion: + return [] + if promotion not in range(0, 6 + 1): # 0-6 + return [] + if level not in range(1, 80 + 1): # 1-80 + return [] + attributes = [] + for k, v in self.light_cone_promotion[id]["values"][promotion].items(): + property = None + for i in self.propertie.values(): + if i["field"] == k: + property = i + break + if property is None: continue - info = info[0] - - property_info = property.get(info["type"]) - - if not property_info["field"] in data: - data[property_info["field"]] = { - "field": property_info["field"], - "name": property_info["name"], - "icon": property_info["icon"], - "value": info["value"], - "display": AssetEnkaParsed.get_display(info["value"], property_info["percent"]), - "percent": property_info["percent"] - } - else: - - data[property_info["field"]]["value"] += info["value"] - data[property_info["field"]]["display"] = AssetEnkaParsed.get_display(data[property_info["field"]]["value"], property_info["percent"]) - - - - return [data[key] for key in data] + attributes.append( + { + "field": k, + "name": property["name"], + "icon": property["icon"], + "value": v["base"] + v["step"] * (level - 1), + "display": await self.get_display(v["base"] + v["step"] * (level - 1), property["percent"]), + "percent": property["percent"], + } + ) + return attributes - @classmethod - async def add_additions(cls, data,lang, light_cone): - promotion_id = data["promotion"] - avatarId = data["avatarId"] - level = data["level"] - - if level > 1: - level = data["level"]-1 - - property = await JsonManager(PathData.ENKA_INDEX.value / lang / "properties.json").read() - meta = await JsonManager(PathData.ENKA.value / "meta.json").read() - meta = meta["avatar"].get(str(avatarId)).get(str(promotion_id)) + async def get_character(self, data): + id = str(data.get("avatarId")) + name = self.character.get(str(id)).get("name") + rarity = self.character.get(str(id)).get("rarity") + rank = data.get("rank", 0) + level = data.get("level") + promotion = data.get("promotion", 0) + icon = self.character.get(str(id)).get("icon") + preview = self.character.get(str(id)).get("preview") + portrait = self.character.get(str(id)).get("portrait") + rank_icons = [await self.get_rank_icons(key) for key in self.character.get(str(id)).get("ranks")] + path = await self.get_path(self.character.get(str(id)).get("path")) + element = await self.get_element(self.character.get(str(id)).get("element")) + skills = await self.get_skill(data, str(id), self.character.get(str(id)).get("element")) + skill_trees = await self.get_skill_trees(data, str(id)) + light_cone = await self.get_light_cone(data.get("equipment")) if data.get("equipment") else None + attributes = await self.get_attributes(str(id), promotion, level) + properties = await self.get_properties(str(id), data["skillTreeList"]) + + relic_infos = [await self.get_relic_info(relic) for relic in data["relicList"]] + relics = [ + relic_info for relic_info in relic_infos if relic_info is not None + ] + relic_sets = await self.get_relic_sets_info(relics) if relics else [] + + attributes = await self.merge_attribute( + [ + attributes, + light_cone["attributes"] if light_cone else [], + ] + ) + + relic_properties = [] + for relic in relics: + if relic["main_affix"]: + relic_properties.append(relic["main_affix"]) + relic_properties += [ + { + "type": affix["type"], + "field": affix["field"], + "name": affix["name"], + "icon": affix["icon"], + "value": affix["value"], + "display": affix["display"], + "percent": affix["percent"], + } for affix in relic["sub_affix"] + ] - name = { - "hp": "HPBase", - "atk": "AttackBase", - "def": "DefenceBase", - "spd": "SpeedBase", - "crit_rate": "CriticalChance", - "crit_dmg": "CriticalDamage", + for relic_set in relic_sets: + relic_properties += relic_set["properties"] + + properties = await self.merge_property( + [ + properties, + light_cone["properties"] if light_cone else [], + relic_properties, + ] + ) + + additions = await self.get_additions(attributes, properties) + + + return { + "id": id, + "name": name, + "rarity": rarity, + "rank": rank, + "level": level, + "promotion": promotion, + "icon": icon, + "preview": preview, + "portrait": portrait, + "rank_icons": rank_icons, + "path": path, + "element": element, + "skills": skills, + "skill_trees": skill_trees, + "light_cone": light_cone, + + "relics": relics, + "relic_sets": relic_sets, + "attributes": attributes, + "additions": additions, + "properties": properties, + "pos": [data.get("pos", 0)], } - base = [ - { - "field":"hp", - "name":"BaseHP", - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + "icon/property/IconMaxHP.png", - "value": 0, - "display": None, - "percent": False - }, - { - "field":"atk", - "name": "BaseAttack", - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + "icon/property/IconAttack.png", - "value": 0, - "display": None, - "percent": False - }, - { - "field":"def", - "name": "BaseDefence", - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + "icon/property/IconDefence.png", - "value": 0, - "display": None, - "percent": False - }, - { - "field":"spd", - "name": "SpeedDelta", - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + "icon/property/IconSpeed.png", - "value": 0, - "display": None, - "percent": False - }, - { - "field":"crit_rate", - "name": "CriticalChanceBase", - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + "icon/property/IconCriticalChance.png", - "value":0., - "display": None, - "percent": True - }, - { - "field":"crit_dmg", - "name": "CriticalDamageBase", - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + "icon/property/IconCriticalDamage.png", - "value":0, - "display": None, - "percent": True - } - ] - - for key in base: - name_meta = name.get(key["field"]) - key["name"] = property.get(key["name"])["name"] - if not key["percent"]: - if light_cone != []: - for keys in light_cone: - if key["field"] == keys["field"]: - key["value"] = meta[name_meta] + (meta.get(name_meta.replace("Base", "Add"), 0) * level) + keys["value"] - else: - key["value"] = meta[name_meta] + (meta.get(name_meta.replace("Base", "Add"), 0) * level) + async def get_additions(self, attributes, properties): + attribute_dict = {} + addition_dict = {} + for attribute in attributes: + if not attribute["field"] in addition_dict: + attribute_dict[attribute["field"]] = attribute["value"] + for property in properties: + if ( + self.propertie[property["type"]]["ratio"] + and property["field"] in attribute_dict + ): + value = property["value"] * attribute_dict[property["field"]] else: - key["value"] = meta[name_meta] - key["display"] = AssetEnkaParsed.get_display(key["value"], key["percent"]) - - return base - - @classmethod - async def add_relict_sets(cls, data, lang): - - new_data = [] - sets_num = {} - done = [] - data_relics = await JsonManager(PathData.ENKA.value / "relics.json").read() - data_relics_sets = await JsonManager(PathData.ENKA_INDEX.value / lang / "relic_sets.json").read() - - for key in data: - relics = data_relics.get(str(key["tid"])) - if not relics["SetID"] in sets_num: - sets_num[relics["SetID"]] = 1 + value = property["value"] + if property["field"] not in addition_dict: + addition_dict[property["field"]] = value else: - sets_num[relics["SetID"]] += 1 - - property = await JsonManager(PathData.ENKA_INDEX.value / lang / "properties.json").read() - - for key in data: - relics = data_relics.get(str(key["tid"])) - if sets_num[relics["SetID"]] < 2 or relics["SetID"] in done: + addition_dict[property["field"]] += value + additions = [] + for k, v in addition_dict.items(): + property = None + for i in self.propertie.values(): + if i["field"] == k: + property = i + break + + if property is None: continue - relics_sets = data_relics_sets.get(str(relics["SetID"])) - - index_desc = 0 - if sets_num[relics["SetID"]] >= 4: - index_desc = 1 - - properties = relics_sets["properties"][index_desc] - if properties == []: - properties = relics_sets["properties"][0] - new_data.append({"id": str(relics["SetID"]), - "name": relics_sets["name"], - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + relics_sets["icon"], - "num": sets_num[relics["SetID"]], - "desc": relics_sets["desc"][index_desc], - "properties":[ - { - "type": properties[0]["type"], - "field": property.get(properties[0]["type"])["field"], - "name": property.get(properties[0]["type"])["name"], - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + property.get(properties[0]["type"])["icon"], - "value": properties[0]["value"], - "display": AssetEnkaParsed.get_display(properties[0]["value"], property.get(properties[0]["type"])["percent"]), - "percent": property.get(properties[0]["type"])["percent"] - } if properties != [] else [] - ] + additions.append( + { + "field": k, + "name": property["name"], + "icon": property["icon"], + "value": v, + "display": await self.get_display(v, property["percent"]), + "percent": property["percent"], } ) - done.append(relics["SetID"]) - - return new_data - - - @classmethod - async def add_relics(cls, data,lang): - - relics = await JsonManager(PathData.ENKA.value / "relics.json").read() - relics = relics.get(str(data["tid"])) - - relicsMiHoMo = await JsonManager(PathData.ENKA_INDEX.value / lang /"relics.json").read() - relicsMiHoMo = relicsMiHoMo.get(str(data["tid"])) - - hash = await JsonManager(PathData.ENKA.value / "hsr.json").read() - hash = hash.get(lang) - - main_affix = await JsonManager(PathData.ENKA_INDEX.value / lang / "relic_main_affixes.json").read() - main_affix = main_affix.get(relicsMiHoMo["main_affix_id"]) - main_affix = main_affix["affixes"].get(str(data["mainAffixId"])) - - - property = await JsonManager(PathData.ENKA_INDEX.value / lang / "properties.json").read() - - data = { - "id": data["tid"], - "name": relicsMiHoMo["name"], - "set_id": data["_flat"]["setID"], - "set_name": hash.get(str(data["_flat"]["setName"])), - "rarity": relics["Rarity"], - "level": data["level"], - "icon": f"https://enka.network/ui/hsr/{relics['Icon']}", - "main_affix":{ - "type": main_affix["property"], - "field": property.get(main_affix["property"])["field"], - "name": property.get(main_affix["property"])["name"], - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + property.get(main_affix["property"])["icon"], - "value": data["_flat"]["props"][0]["value"], - "display": AssetEnkaParsed.get_display(data["_flat"]["props"][0]["value"], property.get(main_affix["property"])["percent"]), - "percent": property.get(main_affix["property"])["percent"] - }, - "sub_affix": [ await AssetEnkaParsed.add_sub_affix(key, index, property, lang, data["_flat"]["props"]) for index, key in enumerate(data["subAffixList"], start = 2)] - } - - return data - - @classmethod - async def add_sub_affix(cls, affix, index, property, lang, flat): - affixId = str(affix["affixId"]) - sub_affix = await JsonManager(PathData.ENKA_INDEX.value / lang / "relic_sub_affixes.json").read() - sub_affix = sub_affix.get(str(index)) - - return { - "type": sub_affix["affixes"][affixId]["property"], - "field":property.get(sub_affix["affixes"][affixId]["property"])["field"], - "name": property.get(sub_affix["affixes"][affixId]["property"])["name"], - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + property.get(sub_affix["affixes"][affixId]["property"])["icon"], - "value": flat[index-1]["value"], - "display": AssetEnkaParsed.get_display(flat[index-1]["value"], property.get(sub_affix["affixes"][affixId]["property"])["percent"]), - "percent":property.get(sub_affix["affixes"][affixId]["property"])["percent"], - "count": affix.get("cnt", 0), - "step": affix.get("step", 0) - } - + return additions - @classmethod - async def add_light_cone(cls, key,lang,path_info): - if key.get("equipment", {}) != {}: - data = await AssetEnkaParsed.get_info_light_cone(key["equipment"]["tid"],lang) - property = await JsonManager(PathData.ENKA_INDEX.value / lang / "properties.json").read() - light_cone_ranks = await JsonManager(PathData.ENKA_INDEX.value / lang / "light_cone_ranks.json").read() - - return { - "id": key["equipment"]["tid"], - "name": data["EquipmentName"]["name"], - "rarity": data["Rarity"], - "rank": key["equipment"]["rank"], - "level": key["equipment"]["level"], - "promotion": key["equipment"]["promotion"], - "icon": _DEFFAULT_ASSETS_HSR_LINK.format(catalog = "icon/light_cone", key = key["equipment"]["tid"]), - "preview": _DEFFAULT_ASSETS_HSR_LINK.format(catalog = "image/light_cone_preview", key = key["equipment"]["tid"]), - "portrait": _DEFFAULT_ASSETS_HSR_LINK.format(catalog = "image/light_cone_portrait", key = key["equipment"]["tid"]), - "path": await AssetEnkaParsed.get_path(path_info,data), - "attributes": [ - { - "field": fiel.get(keys["type"]), - "name": property.get(keys["type"])["name"], - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + property.get(keys["type"])["icon"], - "value": keys["value"], - "display": AssetEnkaParsed.get_display(keys["value"],property.get(keys["type"])["percent"]), - "percent":property.get(keys["type"])["percent"] - } - for keys in key["equipment"]["_flat"]["props"]], - - "properties": [{ - "type": keys["type"], - "field": property.get(keys["type"])["field"], - "name": property.get(keys["type"])["name"], - "icon": property.get(keys["type"])["icon"], - "value": keys["value"], - "display": AssetEnkaParsed.get_display(keys["value"],property.get(keys["type"])["percent"]), - "percent": property.get(keys["type"])["percent"] - } for keys in light_cone_ranks.get(str(key["equipment"]["tid"]))["properties"][key["equipment"]["rank"]-1] ] - } + async def get_attributes(self, character_id, promotion, level): + + if character_id not in self.character_promotions: + return [] + if promotion not in range(0, 6 + 1): # 0-6 + return [] + if level not in range(1, 80 + 1): # 1-80 + return [] + attributes = [] + for k, v in self.character_promotions[character_id]["values"][promotion].items(): + property = None + for i in self.propertie.values(): + if i["field"] == k: + property = i + break + if property is None: + continue - return {} + attributes.append( + { + "field": k, + "name": property["name"], + "icon": property["icon"], + "value": v["base"] + v["step"] * (level - 1), + "display": await self.get_display(v["base"] + v["step"] * (level - 1), property["percent"]), + "percent": property["percent"] + } + ) + return attributes - @classmethod - async def get_info_light_cone(cls, ids,lang): - data = await JsonManager(PathData.ENKA.value / "weps.json").read() - - charter = data.get(str(ids)) - - hash = await JsonManager(PathData.ENKA.value / "hsr.json").read() - - charter["EquipmentName"]["name"] = hash.get(lang).get(str(charter["EquipmentName"]["Hash"])) - - return charter + async def get_properties(self, characters_id, info): + skill_trees = self.character[characters_id]["skill_trees"] + properties = [] + + for skill_tree in info: + pointId = str(skill_tree["pointId"]) + if pointId in skill_trees and pointId in self.skill_trees_info: + property_list = (self.skill_trees_info[pointId]["levels"][skill_tree["level"] - 1]["properties"]) + for i in property_list: + if self.propertie.get(i.get("type")) is None or i["value"] <= 0: + continue + property = self.propertie[i["type"]] + properties.append( + { + "type": i["type"], + "field": property["field"], + "name": property["name"], + "icon": property["icon"], + "value": i["value"], + "display": await self.get_display( i["value"], property["percent"]), + "percent": property["percent"], + "type": i["type"], + } + ) + return properties - @classmethod - async def add_skill_trees(cls, skill_trees_info,charter_info,skills_info,key): - + async def get_skill_trees(self, data, character_id): return [{ - "id": keys["pointId"], - "level": keys["level"], - "anchor": skill_trees_info[str(keys["pointId"])]["anchor"], - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + skill_trees_info[str(keys["pointId"])]["icon"], - "max_level": AssetEnkaParsed.get_max_level(skill_trees_info[str(keys["pointId"])],charter_info,skills_info,key.get("rank",0)), - "parent": AssetEnkaParsed.get_parent(skill_trees_info[str(keys["pointId"])]), - } for keys in key["skillTreeList"]] + "id": str(key["pointId"]), + "level": key["level"], + "anchor": self.skill_trees_info.get(str(key["pointId"]))["anchor"], + "icon": self.skill_trees_info.get(str(key["pointId"]))["icon"], + "max_level": await self.get_max_level(self.character.get(character_id)["ranks"], str(key["pointId"]),key.get("rank",0)), + "parent": await self.get_parent(key["pointId"]) + } for key in data["skillTreeList"]] - @classmethod - async def add_skills(cls, element_info,charter_info,key,lang): - skill_trees_info = await JsonManager(PathData.ENKA_INDEX.value / lang / "character_skill_trees.json").read() - skills_info = await JsonManager(PathData.ENKA_INDEX.value / lang / "character_skills.json").read() + async def get_skill(self, data, character_data, element): + + character_data = self.character.get(character_data) - data = [] + response= [] - for keys in key["skillTreeList"]: + for keys in data["skillTreeList"]: pointId = str(keys["pointId"]) - if skill_trees_info.get(pointId)["level_up_skills"] == []: + if self.skill_trees_info.get(pointId)["level_up_skills"] == []: continue - skills_info_id = str(skill_trees_info.get(pointId)["level_up_skills"][0]["id"]) + skills_info_id = str(self.skill_trees_info.get(pointId)["level_up_skills"][0]["id"]) - if not int(skills_info_id) in charter_info["SkillList"]: + if not str(skills_info_id) in character_data["skills"]: continue - info = skills_info.get(skills_info_id) + info = self.skill.get(skills_info_id) - max_level = skill_trees_info.get(pointId)["max_level"] + max_level = self.skill_trees_info.get(pointId)["max_level"] if keys["level"] > max_level: max_level = info["max_level"] - data.append( + response.append( { "id": skills_info_id, "name": info["name"], "level": keys["level"], "max_level": max_level, - "element": await AssetEnkaParsed.get_element(element_info, info["element"]), + "element": await self.get_element(element), "type": info["type"], "type_text": info["type_text"], "effect": info["effect"], "effect_text": info["effect_text"], "simple_desc": info["simple_desc"], "desc": info["desc"], - "icon": _DEFFAULT_ASSETS_HSR_LINK_SKILL + info["icon"], + "icon": info["icon"], } ) - return data - - @classmethod - async def get_icon_avatar(cls, ids): - data = await JsonManager(PathData.ENKA.value / "avatars.json").read() + return response + + async def get_relic_info(self, data): + if str(data["tid"]) not in self.relics: + return None - return data.get(str(ids))["Icon"] - - @classmethod - async def get_info_character(cls, ids,lang): - data = await JsonManager(PathData.ENKA.value / "characters.json").read() + relics_info = self.relics.get(str(data["tid"])) + + info = { + "id": str(data["tid"]), + "name": relics_info["name"], + "set_id": str(data["_flat"]["setID"]), + "set_name": self.relics_set[relics_info["set_id"]]["name"], + "rarity": relics_info["rarity"], + "level": data["level"], + "icon": relics_info['icon'], + "main_affix": await self.get_relic_main_affix(str(data["tid"]), data["level"], str(data["mainAffixId"])), + "sub_affix": await self.get_relic_sub_affix(str(data["tid"]), data["subAffixList"]) + } - charter = data.get(str(ids)) + return info + + async def get_relic_sub_affix(self, relict_id, sub_affix_info): + if relict_id not in self.relics: + return [] - hash = await JsonManager(PathData.ENKA.value / "hsr.json").read() + sub_affix_group = self.relics[relict_id]["sub_affix_id"] + if sub_affix_group not in self.relic_sub_affixes: + return [] - charter["AvatarFullName"]["name"] = hash.get(lang).get(str(charter["AvatarFullName"]["Hash"])) - charter["AvatarName"]["name"] = hash.get(lang).get(str(charter["AvatarName"]["Hash"])) + properties = [] + for sub_affix in sub_affix_info: + if str(sub_affix["affixId"]) not in self.relic_sub_affixes[sub_affix_group]["affixes"]: + continue + affix = self.relic_sub_affixes[sub_affix_group]["affixes"][str(sub_affix["affixId"]) ] - return charter - - @classmethod - async def get_element(cls, element_info,element): - if element != "": - return {"id": element_info[element]["id"], - "name": element_info[element]["name"], - "color": element_info[element]["color"], - "icon": _DEFFAULT_ASSETS_HSR_LINK.format(catalog = "icon/element", key = element_info[element]["id"].replace("Thunder", "Lightning")) + property = self.propertie[affix["property"]] + + properties.append( + { + "type": affix["property"], + "field": property["field"], + "name": property["name"], + "icon": property["icon"], + "value": affix["base"] * sub_affix["cnt"] + affix["step"] * sub_affix.get("step", 0), + "display": await self.get_display(affix["base"] * sub_affix["cnt"] + affix["step"] * sub_affix.get("step", 0), property["percent"]), + "percent": property["percent"], + "count": sub_affix["cnt"], + "step": sub_affix.get("step", 0) } - else: + ) + return properties + + async def get_relic_main_affix(self, relict_id, level, main_affix_id): + if str(relict_id) not in self.relics: return None - - @classmethod - async def get_path(cls, path_info,charter_info): + if not main_affix_id: + return None + + main_affix_group = self.relics[str(relict_id)]["main_affix_id"] + + if main_affix_group not in self.relic_main_affixes or main_affix_id not in self.relic_main_affixes[main_affix_group]["affixes"]: + return None + + affix = self.relic_main_affixes[main_affix_group]["affixes"][main_affix_id] + + property = self.propertie[affix["property"]] + return { - "id": path_info[charter_info["AvatarBaseType"]]["id"], - "name": path_info[charter_info["AvatarBaseType"]]["name"], - "icon": _DEFFAULT_ASSETS_HSR_LINK.format(catalog = "icon/path", key = path_info[charter_info["AvatarBaseType"]]["text"]), - } + "type": affix["property"], + "field": property["field"], + "name": property["name"], + "icon": property["icon"], + "value":affix["base"] + + affix["step"] * level, + "display": await self.get_display(affix["base"] + + affix["step"] * level, property["percent"]), + "percent": property["percent"] + } + + async def get_relic_sets_info(self, data): + set_num = {} + for relic in data: + if relic["set_id"] not in set_num: + set_num[relic["set_id"]] = 1 + else: + set_num[relic["set_id"]] += 1 + + relic_sets = [] + for k, v in set_num.items(): + info = self.relic_set[str(k)] + if v >= 2: + prop = [{ + "type": i["type"], + "field": self.propertie.get(i["type"])["field"], + "name": self.propertie.get(i["type"])["name"], + "icon": self.propertie.get(i["type"])["icon"], + "value": i["value"], + "display": await self.get_display(i["value"], self.propertie.get(i["type"])["percent"]), + "percent": self.propertie.get(i["type"])["percent"], + } for i in info["properties"][0]] + + relic_sets.append( + { + "id": k, + "name": info["name"], + "icon": info["icon"], + "num": 2, + "desc": info["desc"][0], + "properties": prop, + } + ) + if v >= 4: + prop = [ + { + "type": i["type"], + "field": self.propertie.get(i["type"])["field"], + "name": self.propertie.get(i["type"])["name"], + "icon": self.propertie.get(i["type"])["icon"], + "value": i["value"], + "display": await self.get_display(i["value"], self.propertie.get(i["type"])["percent"]), + "percent": self.propertie.get(i["type"])["percent"], + } for i in info["properties"][1] if len(info["properties"]) > 1 + ] + + relic_sets.append( + { + "id": k, + "name": info["name"], + "icon": info["icon"], + "num": 4, + "desc": info["desc"][1] if len(info["desc"]) > 1 else "", + "properties": prop, + } + ) + return relic_sets + + async def merge_property(self, properties): + property_dict = {} + for property_list in properties: + for property in property_list: + if isinstance(property, list): + if len(property) == 0: + continue + property = property[0] + + if property["type"] not in property_dict: + property_dict[property["type"]] = {} + property_dict[property["type"]]["value"] = property["value"] + property_dict[property["type"]]["origin"] = deepcopy(property) + else: + property_dict[property["type"]]["value"] += property["value"] + property_res = [] + + for v in property_dict.values(): + + info = v["origin"] + info["value"] = v["value"] + info["display"] = await self.get_display(v["value"], info["percent"]) + + property_res.append(info) + return property_res + + async def merge_attribute(self, attributes): + attribute_dict = {} + for attribute_list in attributes: + for attribute in attribute_list: + if attribute["field"] not in attribute_dict: + attribute_dict[attribute["field"]] = {} + attribute_dict[attribute["field"]]["value"] = attribute["value"] + attribute_dict[attribute["field"]]["origin"] = deepcopy(attribute) + else: + attribute_dict[attribute["field"]]["value"] += attribute["value"] + attribute_res = [] + for v in attribute_dict.values(): + attribute_info = v["origin"] + attribute_info["value"] = v["value"] + attribute_info["display"] = await self.get_display( + v["value"], attribute_info["percent"] + ) + attribute_res.append(attribute_info) + + return attribute_res + + async def get_parent(self, skill_id): + parent = self.skill_trees_info.get(str(skill_id)).get("pre_points") + if parent == []: + return None + else: + return parent[0] + + async def get_rank_icons(self, rank_id): + return self.character_rank.get(rank_id).get("icon") + + async def get_path(self, path): + return {"id": self.path.get(path)["id"], "name": self.path.get(path)["name"], "icon": self.path.get(path)["icon"]} - @classmethod - def get_max_level(cls, skill_trees_info,charter_info,skills_info,rank): + async def get_element(self, element): + return {"id": self.element.get(element)["id"], "name": self.element.get(element)["name"], "color": self.element.get(element)["color"], "icon": self.element.get(element)["icon"]} + + async def get_max_level(self, ranks_list,skill_id,rank): + + skill_trees_info = self.skill_trees_info.get(skill_id) + max_level = skill_trees_info["max_level"] if skill_trees_info.get("level_up_skills", []) != []: - if int(skill_trees_info.get("level_up_skills")[0]["id"]) in charter_info["RankIDList"][:rank]: - max_level = skills_info[skill_trees_info.get("level_up_skills")[0]["id"]]["max_level"] + if int(skill_trees_info.get("level_up_skills")[0]["id"]) in ranks_list[:rank]: + max_level = self.skill[skill_trees_info.get("level_up_skills")[0]["id"]]["max_level"] return max_level - @classmethod - def get_parent(cls, parent): - if parent["pre_points"] != []: - return parent["pre_points"][0] - return None - - @classmethod - def get_display(cls, value,percent): + async def get_display(self, value, percent): if percent: - percentage_value = value * 100 - return f"{percentage_value:.1f}%" + return format(math.floor(value * 1000) / 10.0, ".1f") + "%" else: - if value < 1: - value = value * 100 - - return str(round(value)) \ No newline at end of file + return f"{math.floor(value)}" \ No newline at end of file diff --git a/starrailcard/src/generator/style_card.py b/starrailcard/src/generator/style_card.py index dcf5ba4..f31d319 100644 --- a/starrailcard/src/generator/style_card.py +++ b/starrailcard/src/generator/style_card.py @@ -25,11 +25,11 @@ } class Create: - def __init__(self, data, lang, art, hide_uid, uid, seeleland,remove_logo) -> None: + def __init__(self, data, lang, art, hide_uid, uid, seeleland,remove_logo, color) -> None: self.remove_logo = remove_logo self.data = data self.lang = lang - self.art = art #Image.open("modified_exampl.gif")#art + self.art = art self.hide_uid = hide_uid self.uid = uid @@ -38,7 +38,10 @@ def __init__(self, data, lang, art, hide_uid, uid, seeleland,remove_logo) -> Non self.seeleland = seeleland self.total_eff = 0 - self.element_color = self.data.element.color.rgba + if not color is None: + self.element_color = color + else: + self.element_color = self.data.element.color.rgba diff --git a/starrailcard/src/generator/style_relict_score.py b/starrailcard/src/generator/style_relict_score.py index d0e293e..3c50569 100644 --- a/starrailcard/src/generator/style_relict_score.py +++ b/starrailcard/src/generator/style_relict_score.py @@ -30,7 +30,7 @@ async def get_cone_frame(x): return await _of.light_cone_frame_three class Create: - def __init__(self, data, lang, art, hide_uid, uid, seeleland,remove_logo) -> None: + def __init__(self, data, lang, art, hide_uid, uid, seeleland,remove_logo, color) -> None: self.remove_logo = remove_logo self.data = data self.lang = lang @@ -43,7 +43,10 @@ def __init__(self, data, lang, art, hide_uid, uid, seeleland,remove_logo) -> Non self.seeleland = seeleland self.total_eff = 0 - self.element_color = self.data.element.color.rgba + if not color is None: + self.element_color = color + else: + self.element_color = self.data.element.color.rgba async def create_bacground(self): self.background = Image.new(RelictScore.RGBA, RelictScore.background_size, (0,0,0,0)) @@ -151,7 +154,7 @@ async def create_stats(self): combined_attributes = {} dop = {} - + for attribute in self.data.attributes + self.data.additions: field = attribute.field if field in combined_attributes: @@ -260,7 +263,6 @@ async def create_relict(self,relict): relict_line = await _of.relict_line background_main.alpha_composite(relict_frame) - image = await pill.get_download_img(relict.icon, size= RelictScore.relict_icon_size) background_image.alpha_composite(image,RelictScore.relict_icon_position) background.paste(background_image,(0,0),relict_maska.convert("L")) @@ -467,8 +469,7 @@ async def start(self): await self.create_bacground() - - + async with anyio.create_task_group() as tasks: tasks.start_soon(self.create_light_cone) tasks.start_soon(self.create_stats) @@ -495,7 +496,6 @@ async def process(func, i): functools.partial(self.create_relict, key) for key in self.data.relics ]) - await self.create_score_total() await self.build_relict() diff --git a/starrailcard/src/generator/style_ticket.py b/starrailcard/src/generator/style_ticket.py index a48f6cd..2f00983 100644 --- a/starrailcard/src/generator/style_ticket.py +++ b/starrailcard/src/generator/style_ticket.py @@ -25,7 +25,7 @@ class Create: - def __init__(self, data, lang, art, hide_uid, uid, seeleland, remove_logo) -> None: + def __init__(self, data, lang, art, hide_uid, uid, seeleland, remove_logo, color) -> None: self.data = data self.remove_logo = remove_logo self.lang = lang @@ -34,7 +34,11 @@ def __init__(self, data, lang, art, hide_uid, uid, seeleland, remove_logo) -> No self.uid = uid self.seeleland = seeleland self.total_eff = 0 - self.element_color = self.data.element.color.rgba + self.color = color + if not color is None: + self.element_color = color + else: + self.element_color = self.data.element.color.rgba self.gif = False self.GIFT_BG = [] @@ -63,6 +67,9 @@ async def create_background(self): if ll > 0.6: self.background.alpha_composite(background_dark) else: + if not self.color is None: + line,self.element_color = await pill.recolor_image(line, self.element_color[:3], light = True) + self.background = Image.new(Ticket.RGBA, Ticket.background_size, Ticket.background_default_color) user_image = await pill.get_center_scale(Ticket.splash_size, await pill.get_download_img(self.data.portrait)) position_art = Ticket.position_splash_art @@ -619,7 +626,8 @@ async def start(self): self.element_color = await pill.get_colors(self.art, 15, common=True, radius=5, quality=800) await self.create_background() else: - self.element_color = (255,213,167,255) + if self.color is None: + self.element_color = (255,213,167,255) await self.create_background() async with anyio.create_task_group() as tasks: diff --git a/starrailcard/src/model/__pycache__/StarRailCard.cpython-312.pyc b/starrailcard/src/model/__pycache__/StarRailCard.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..753b07281292e43aa294202d5cc09a07f4493d93 GIT binary patch literal 7142 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 literal 0 HcmV?d00001 diff --git a/starrailcard/src/model/__pycache__/api_mihomo.cpython-312.pyc b/starrailcard/src/model/__pycache__/api_mihomo.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..700af051b4128f0b788cd6ba2951c561dd360d55 GIT binary patch literal 19407 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 literal 0 HcmV?d00001 diff --git a/starrailcard/src/model/__pycache__/utils_model.cpython-312.pyc b/starrailcard/src/model/__pycache__/utils_model.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b7d12dee23552b0c757c867311f05113126a5fe GIT binary patch literal 1609 zcmcIk&uiR96dtWcTCMhn?YOq%Cbkny4-0Lfw1>8&fhK`ssGUQ}q015>ug3OJNxPky zjiW*iDJ|{Zq|~~qm}SRr1aDQy?NhzGjE>W z`(}Qt*KGvj_=kVUwvNyr3gpAclxKHAIYu7xG#>?8OVgBI@=Jl<(lt~oh zPauXGEu2~nu$nVgBej;nYRy>9sM56mRNI=GtlV%p+3tG82VmXq3EB-^zoWeQ$j(4n zUIeu?54B2n5YY)Sk2%d)Z+xcg))-0MnfkNPBZ10wNM z1{CxPxi3h_Ay29InZMICrR6wkf#XQqae}Vb_my6AoG<&XKlLyS2S}X@Cg&W-4a2T* z)xUDbd5oTc51s9SxcOoG4kwJace|WB?b{)_#ppfK9zGiWIQ(h&boAZu=ix6`Mh{0{ z51$TyZFAu==2E}oGS3Og;Y)@#XPq{rL;QAMP@k*Ri2rskVkR7w3JVne9*A$zxUn4b zL6G1}WBWqrdm>0qM?Lh9h*~P9)#3sD5lL{P`0#xi%MI{g<8vxKmZ}E`FL?q zPw>*Xx)|Rae3amez~og4bu()n1D~8*C?db%sLcv^tLh zk+(+G^=xyl!tT#UwT%RCWCxz&wQ060(O0wWI(YtrZ8imYXDX{?`zX}5C40U;b zWM5WytG*O_gS+YuP78R7SEv82_Uyp(Wc4D?4F&Bm^(;zLL_HerPy3wS>NJb=n3s=! zZe5$cOxM|)uv3GNzXRfLUDLEP8)+NAqe~NPXe)7Lf?%378ru5z(FDOXxuR>Y#~+*_ In9_V+0Gkef+5i9m literal 0 HcmV?d00001 diff --git a/starrailcard/src/model/api_mihomo.py b/starrailcard/src/model/api_mihomo.py index 89eb0ab..643e632 100644 --- a/starrailcard/src/model/api_mihomo.py +++ b/starrailcard/src/model/api_mihomo.py @@ -2,7 +2,7 @@ # All rights reserved. from pydantic import BaseModel, Field -from typing import List, Optional,Final +from typing import List, Optional,Final, Union from ..tools.ukrainization import TranslateDataManager @@ -22,7 +22,6 @@ def hex_to_rgba(hex_code): return red, green, blue, alpha class MemoryInfo(BaseModel): - level: Optional[int] chaos_id: Optional[int] chaos_level: Optional[int] abyss_level: Optional[int] = Field(0, alias= "abyssLevel") @@ -57,7 +56,7 @@ def __init__(self, *args, **kwargs): self.name = TranslateDataManager._data.avatar.get(self.id, self.name) class Player(BaseModel): - uid: Optional[str] + uid: Optional[Union[str,int]] nickname: Optional[str] level: int world_level: int @@ -143,7 +142,7 @@ class RelicSet(BaseModel): icon: Optional[str] num: int desc: Optional[str] - properties: List[RelicSetProperties] + properties: List[RelicSetProperties] = Field([], alias="properties") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -295,7 +294,7 @@ class Character(BaseModel): skill_trees: List[SkillTree] light_cone: Optional[LightCone] relics: Optional[List[Relic]] - relic_sets: Optional[List[RelicSet]] + relic_sets: Optional[List[RelicSet]] = Field([], alias="relic_sets") attributes: List[CharacterAttributes] additions: List[CharacterAttributes] properties: Optional[List[CharacterProperties]] @@ -357,6 +356,7 @@ def __init__(self, *args, **kwargs): for attribute in character.attributes: attribute.icon = MAIN_LINK.format(icon=attribute.icon) + for addition in character.additions: addition.icon = MAIN_LINK.format(icon=addition.icon) diff --git a/starrailcard/src/tools/options.py b/starrailcard/src/tools/options.py index 85cbbb7..5cd9feb 100644 --- a/starrailcard/src/tools/options.py +++ b/starrailcard/src/tools/options.py @@ -148,7 +148,16 @@ def max_lvl(x): return max - +async def get_color_user(color): + processed_dict = {} + for key, value in color.items(): + if isinstance(value, tuple): + if len(value) >= 3 and len(value) <= 4: + if all(0 <= x <= 255 for x in value): + processed_dict[key] = value + if processed_dict != {}: + return processed_dict + return None async def get_character_art(character_art, style=None): processed_dict = {} diff --git a/starrailcard/src/tools/pill/image_control.py b/starrailcard/src/tools/pill/image_control.py index f38bcb5..ff928e6 100644 --- a/starrailcard/src/tools/pill/image_control.py +++ b/starrailcard/src/tools/pill/image_control.py @@ -81,7 +81,7 @@ async def download_image(link, headers=None): image = await http.AioSession.get(link, headers=headers, response_format="bytes") return image except: - raise TypeError(f"Error Dowload image: {link}") + raise TypeError(f"Error Download image: {link}") async def save_image(image, full_path): directory = os.path.dirname(full_path) @@ -95,7 +95,7 @@ async def open_image(image_data): try: return Image.open(BytesIO(image_data)).convert("RGBA") except Exception as e: - raise TypeError(f"Error Open image: {e}") + raise TypeError(f"Error Open image: {image_data}") async def get_download_img(link, size=None, thumbnail_size=None, gif=False): cache_key = json.dumps((link, size, thumbnail_size), sort_keys=True) @@ -119,7 +119,7 @@ async def get_download_img(link, size=None, thumbnail_size=None, gif=False): "referer": "https://www.pixiv.net/", } image = await download_image(link, headers_p) - + if _boost_speed: if "StarRailRes" in link: full_path = os.path.join(assets, f"/boost_speed/{link.split('master')[1]}".lstrip('/'))