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
|