PamGram/modules/apihelper/client/components/player_cards.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

233 lines
7.6 KiB
Python
Raw Normal View History

2023-06-07 16:04:58 +00:00
from decimal import Decimal
2023-05-11 15:09:39 +00:00
from typing import List, Optional, Union, Dict
import ujson
from httpx import AsyncClient, TimeoutException
from pydantic import BaseModel
2023-05-17 05:17:18 +00:00
from core.config import config
2023-05-11 15:09:39 +00:00
from modules.playercards.fight_prop import EquipmentsStats
from modules.wiki.base import WikiModel
from modules.wiki.models.relic_affix import RelicAffixAll
from utils.enkanetwork import RedisCache
from modules.playercards.file import PlayerCardsFile
2023-06-07 06:24:36 +00:00
class SkillTreePoint(BaseModel):
pointId: int
level: int
2023-05-11 15:09:39 +00:00
class Equipment(BaseModel):
2023-06-07 06:24:36 +00:00
tid: Optional[int] = 0
level: Optional[int] = 0
promotion: Optional[int] = 3
2023-05-11 15:09:39 +00:00
"""星级"""
2023-06-07 06:24:36 +00:00
rank: Optional[int] = 0
2023-05-11 15:09:39 +00:00
"""叠影"""
class SubAffix(BaseModel):
2023-06-07 06:24:36 +00:00
cnt: Optional[int] = 1
step: Optional[int] = 0
affixId: int
2023-05-11 15:09:39 +00:00
class Relic(BaseModel):
2023-06-07 06:24:36 +00:00
tid: int
level: Optional[int] = 0
mainAffixId: int
subAffixList: Optional[List[SubAffix]]
type: int
2023-05-11 15:09:39 +00:00
2023-05-22 13:30:36 +00:00
class Property(BaseModel):
name: str
2023-06-07 16:04:58 +00:00
base: float = 0.0
addition: float = 0.0
percent: bool
2023-05-22 13:30:36 +00:00
@property
def total(self):
2023-06-07 16:04:58 +00:00
total_num = (Decimal(self.base) + Decimal(self.addition)) * (Decimal(100.0) if self.percent else Decimal(1.0))
total_num = round(total_num, 2)
return f"{total_num}{'%' if self.percent else ''}"
2023-05-22 13:30:36 +00:00
2023-05-11 15:09:39 +00:00
class Avatar(BaseModel):
2023-06-07 06:24:36 +00:00
avatarId: int
skillTreeList: List[SkillTreePoint]
equipment: Optional[Equipment]
level: int
promotion: Optional[int] = 4
rank: Optional[int] = 0
relicList: Optional[List[Relic]]
2023-05-22 13:30:36 +00:00
property: Optional[List[Property]]
2023-05-11 15:09:39 +00:00
2023-06-07 06:24:36 +00:00
class RecordInfo(BaseModel):
achievementCount: Optional[int] = 0
avatarCount: Optional[int] = 0
2024-05-08 14:07:09 +00:00
bookCount: Optional[int] = 0
2023-06-07 06:24:36 +00:00
equipmentCount: Optional[int] = 0
maxRogueChallengeScore: Optional[int] = 0
2024-05-08 14:07:09 +00:00
musicCount: Optional[int] = 0
relicCount: Optional[int] = 0
2023-05-11 15:09:39 +00:00
2023-06-07 11:50:27 +00:00
class PlayerBaseInfo(BaseModel):
2023-06-07 06:24:36 +00:00
platform: Optional[str]
friendCount: Optional[int]
headIcon: Optional[int]
isDisplayAvatar: bool
level: int
worldLevel: Optional[int]
nickname: str
recordInfo: RecordInfo
signature: Optional[str]
uid: int
2023-05-11 15:09:39 +00:00
2023-06-07 11:50:27 +00:00
class PlayerInfo(PlayerBaseInfo):
avatarList: List[Avatar]
2023-05-11 15:09:39 +00:00
class PlayerCardsError(Exception):
def __init__(self, msg):
self.msg = msg
class PlayerCards:
2023-05-17 05:17:18 +00:00
url = "https://api.mihomo.me/sr_info/"
2023-05-22 13:30:36 +00:00
url2 = "https://api.mihomo.me/sr_info_parsed/"
2023-05-11 15:09:39 +00:00
prop_url = f"{WikiModel.BASE_URL}relic_config.json"
def __init__(self, redis):
self.cache = RedisCache(redis.client, key="plugin:player_cards:fake_enka_network", ex=60)
2023-05-17 05:17:18 +00:00
self.headers = {"User-Agent": config.enka_network_api_agent}
2023-05-11 15:09:39 +00:00
self.client = AsyncClient()
self.player_cards_file = PlayerCardsFile()
self.init = False
self.relic_datas_map: Dict[int, RelicAffixAll] = {}
async def async_init(self):
if self.init:
return
self.relic_datas_map.clear()
req = await self.client.get(self.prop_url)
data = req.json()
for i in data:
self.relic_datas_map[i["id"]] = RelicAffixAll(**i)
self.init = True
2023-05-22 13:30:36 +00:00
async def get_property(self, uid: str) -> Dict[int, List[Dict]]:
final_data: Dict[int, List[Dict]] = {}
try:
user = await self.client.get(self.url2 + uid, timeout=30, headers=self.headers)
if user.status_code != 200:
2023-05-22 14:32:37 +00:00
raise PlayerCardsError("请求异常")
2023-05-22 13:30:36 +00:00
data = ujson.loads(user.text)
characters = data.get("characters", [])
for character in characters:
cid = int(character.get("id", 0))
if not cid:
continue
2023-06-07 16:04:58 +00:00
datas = []
datas_map = {}
for attr in character.get("attributes", []):
prop = Property(
name=attr["name"],
base=attr["value"],
percent=attr["percent"],
)
datas.append(prop)
datas_map[prop.name] = prop
for attr in character.get("additions", []):
prop = datas_map.get(attr["name"])
if prop:
prop.addition = attr["value"]
else:
prop = Property(
name=attr["name"],
addition=attr["value"],
percent=attr["percent"],
)
datas.append(prop)
datas_map[prop.name] = prop
final_data[cid] = [i.dict() for i in datas]
2023-05-22 13:30:36 +00:00
except (TimeoutException, PlayerCardsError):
pass
return final_data
2023-05-11 15:09:39 +00:00
async def update_data(self, uid: str) -> Union[PlayerInfo, str]:
try:
data = await self.cache.get(uid)
if data is not None:
return PlayerInfo.parse_obj(data)
2023-05-22 13:30:36 +00:00
user = await self.client.get(self.url + uid, timeout=30, headers=self.headers)
2023-05-11 15:09:39 +00:00
if user.status_code != 200:
raise PlayerCardsError(f"请求异常,错误代码 {user.status_code}")
data = ujson.loads(user.text)
error_code = data.get("ErrCode", 0)
if error_code:
raise PlayerCardsError(f"请求异常,错误代码 {error_code}")
2023-06-07 06:24:36 +00:00
data = data.get("detailInfo", {})
2023-05-22 13:30:36 +00:00
props = await self.get_property(uid)
data = await self.player_cards_file.merge_info(uid, data, props)
2023-05-11 15:09:39 +00:00
await self.cache.set(uid, data)
return PlayerInfo.parse_obj(data)
except TimeoutException:
error = "服务请求超时,请稍后重试"
except PlayerCardsError as e:
error = e.msg
return error
2023-06-07 11:50:27 +00:00
async def get_player_base_info(self, uid: int) -> PlayerBaseInfo:
try:
user = await self.client.get(f"{self.url}{uid}", timeout=30, headers=self.headers)
if user.status_code != 200:
raise PlayerCardsError(f"请求异常,错误代码 {user.status_code}")
data = ujson.loads(user.text)
error_code = data.get("ErrCode", 0)
if error_code:
raise PlayerCardsError(f"请求异常,错误代码 {error_code}")
return PlayerBaseInfo.parse_obj(data["detailInfo"])
except TimeoutException as e:
raise PlayerCardsError("服务请求超时,请稍后重试") from e
2023-05-11 15:09:39 +00:00
def get_affix_by_id(self, cid: int) -> RelicAffixAll:
return self.relic_datas_map.get(cid)
def get_set_by_id(self, cid: int) -> int:
if affix := self.get_affix_by_id(cid):
return affix.set_id
return 101
def get_affix(self, relic: Relic, main: bool = True, sub: bool = True) -> List[EquipmentsStats]:
2023-06-07 06:24:36 +00:00
affix = self.get_affix_by_id(relic.tid)
2023-05-11 15:09:39 +00:00
if not affix:
return []
2023-06-07 06:24:36 +00:00
main_affix = affix.main_affix[str(relic.mainAffixId)]
2023-05-11 15:09:39 +00:00
datas = (
[
EquipmentsStats(
prop_id=main_affix.property,
2023-06-07 06:24:36 +00:00
prop_value=main_affix.get_value(relic.level),
2023-05-11 15:09:39 +00:00
)
]
if main
else []
)
if not sub:
return datas
2023-06-07 06:24:36 +00:00
if relic.subAffixList:
for sub_a in relic.subAffixList:
sub_affix = affix.sub_affix[str(sub_a.affixId)]
2023-05-11 15:09:39 +00:00
datas.append(
EquipmentsStats(
prop_id=sub_affix.property,
2023-06-07 06:24:36 +00:00
prop_value=sub_affix.get_value(sub_a.step, sub_a.cnt),
2023-05-11 15:09:39 +00:00
)
)
return datas