Merge pull request #448 from CMHopeSunshine/sr_dev

This commit is contained in:
惜月 2023-06-12 20:28:41 +08:00 committed by GitHub
commit 23d5a6e10b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 657 additions and 1 deletions

View File

@ -0,0 +1,87 @@
from nonebot import on_command
from nonebot.params import CommandArg
from nonebot.plugin import PluginMetadata
from nonebot.adapters.onebot.v11 import Message, MessageEvent
from LittlePaimon.utils.message import MessageBuild
from .data_handle import set_uid, get_uid, update_info, get_info
from .draw import draw_character
__plugin_meta__ = PluginMetadata(
name='星穹铁道面板查询',
description='星穹铁道面板查询',
usage='...',
extra={
'author': '惜月',
'version': '3.0',
'priority': 6,
},
)
panel_cmd = on_command("星铁面板", aliases={"崩铁面板", "星穹铁道面板", "sr"}, priority=1, block=True, state={
'pm_name': '星穹铁道面板',
'pm_description': '查询星穹铁道面板',
'pm_usage': '星铁面板',
'pm_priority': 1
})
update_cmd = on_command("更新星铁面板", aliases={"更新崩铁面板", "更新星穹铁道面板", "sr update"}, priority=1, block=True, state={
'pm_name': '星穹铁道面板更新',
'pm_description': '绑定星穹面板更新',
'pm_usage': '更新星铁面板 uid',
'pm_priority': 2
})
bind_cmd = on_command("星铁绑定", aliases={"崩铁绑定", "星穹铁道绑定", "srb", "绑定星铁", "绑定崩铁", "绑定星穹铁道"}, priority=1, block=True, state={
'pm_name': '星穹铁道绑定',
'pm_description': '绑定星穹铁道uid',
'pm_usage': '星铁绑定uid',
'pm_priority': 3
})
@panel_cmd.handle()
async def panel_cmd_handler(event: MessageEvent, args: Message = CommandArg()):
name = args.extract_plain_text().strip()
if not name:
await panel_cmd.finish("请给出要查询的角色名全称")
uid = get_uid(str(event.user_id))
if not uid:
await panel_cmd.finish("请先使用命令[星铁绑定 uid]来绑定星穹铁道UID")
data = get_info(uid, name)
if not data:
await panel_cmd.finish("你还没有该角色的面板数据哦,请将该角色放在你的游戏主页中,使用命令[更新星铁面板]来更新")
try:
image = await draw_character(data, uid)
except Exception as e:
await panel_cmd.finish(f"绘制星穹铁道面板时出现了错误:{e}")
await panel_cmd.finish(MessageBuild.Image(image, quality=80, mode='RGB'), at_sender=True)
@bind_cmd.handle()
async def bind_cmd_handler(event: MessageEvent, args: Message = CommandArg()):
uid = args.extract_plain_text().strip()
if not uid:
await bind_cmd.finish("请给出要绑定的uid")
if not (uid.isdigit() and len(uid) == 9):
await bind_cmd.finish("UID格式不对哦~")
set_uid(str(event.user_id), uid)
await bind_cmd.finish(f"成功绑定星铁UID{uid}", at_sender=True)
@update_cmd.handle()
async def update_cmd_handler(event: MessageEvent, args: Message = CommandArg()):
uid = args.extract_plain_text().strip()
if not uid:
uid = get_uid(str(event.user_id))
elif not (uid.isdigit() and len(uid) == 9):
await update_cmd.finish("UID格式不对哦~")
if not uid:
await update_cmd.finish("请先绑定UID或在本命令后面加上UID")
set_uid(str(event.user_id), uid)
msg = await update_info(uid)
await update_cmd.finish(msg, at_sender=True)

View File

@ -0,0 +1,59 @@
from typing import Optional
from LittlePaimon.utils.path import USER_DATA_PATH
from LittlePaimon.utils.files import load_json, save_json
from LittlePaimon.utils.requests import aiorequests
from .models import Character
API = "https://api.mihomo.me/sr_info_parsed/"
def set_uid(user_id: str, uid: str):
data = load_json(USER_DATA_PATH / "star_rail_uid.json")
data[user_id] = uid
save_json(data, USER_DATA_PATH / "star_rail_uid.json")
def get_uid(user_id: str) -> Optional[str]:
data = load_json(USER_DATA_PATH / "star_rail_uid.json")
return data.get(user_id)
async def update_info(uid: str) -> str:
try:
path = USER_DATA_PATH / "star_rail_info" / f"{uid}.json"
resp = await aiorequests.get(
API + uid, headers={"User-Agent": "LittlePaimon/3.0"}, follow_redirects=True
)
data = resp.json()
if "player" not in data and "characters" not in data:
return "获取星穹铁道面板数据失败请检查uid是否正确或者稍后再试"
if not path.exists():
save_json(data, path)
else:
old_data = load_json(path)
old_data["player"] = data["player"]
# 如果旧数据和新数据都有相同ID的角色就更新旧数据否则就添加新数据
for new_char in data["characters"]:
for old_char in old_data["characters"]:
if new_char["id"] == old_char["id"]:
old_char.update(new_char)
break
else:
old_data["characters"].append(new_char)
save_json(old_data, path)
new_char_name = " ".join([char["name"] for char in data["characters"]])
return f"成功更新以下星穹铁道角色面板:\n{new_char_name}\n可以使用“星铁面板 角色名”命令查看面板"
except Exception as e:
return f"获取星穹铁道面板数据失败:{e}"
def get_info(uid: str, chara_name: str) -> Optional[Character]:
path = USER_DATA_PATH / "star_rail_info" / f"{uid}.json"
if not path.exists():
return None
data = load_json(path)
for char in data["characters"]:
if char["name"] == chara_name:
return Character.parse_obj(char)
return None

View File

@ -0,0 +1,260 @@
import asyncio
from LittlePaimon.utils.image import PMImage, load_image
from LittlePaimon.utils.image import font_manager as fm
from LittlePaimon.utils.path import RESOURCE_BASE_PATH
from .models import Character, Relic, Element
PATH = RESOURCE_BASE_PATH / "star_rail"
LEVEL_COLOR = ["#404040", "#519d57", "#2f8dac", "#d16fe9", "#f5ad1e"]
ELEMENT_COLOR = {
"": "#d16fe9",
"": "#2f9de0",
"": "#42c28c",
"": "#e93d44",
"物理": "#8c8b8f",
"量子": "#723de8",
"虚数": "#ebca47",
}
async def draw_relic(relic: Relic, element: Element) -> PMImage:
# 背景
bg = PMImage(await load_image(PATH / f"遗器-{element.name}.png"))
# 名称
await bg.text(relic.name, 19, 12, fm.get("汉仪雅酷黑", 30), "white")
# 图标
await bg.paste(
await load_image(PATH / "relic" / relic.icon.split("/")[-1], size=(106, 106)),
(4, 63),
)
# 稀有度
await bg.draw_rectangle((6, 138, 47, 160), LEVEL_COLOR[relic.rarity - 1])
# 等级
await bg.text(
f"+{relic.level}",
(6, 47),
(138, 160),
fm.get("bahnschrift_regular", 24),
"white",
"center",
)
# 主属性名
await bg.text(
relic.main_affix.name.rstrip("提高"),
328,
67,
fm.get("汉仪润圆", 36),
"#333333",
"right",
)
# 主属性值
await bg.text(
"+" + relic.main_affix.display, 328, 123, fm.get("汉仪润圆", 36), "#333333", "right"
)
# 副属性
for i, affix in enumerate(relic.sub_affix):
# 副属性名
await bg.text(affix.name, 6, 178 + i * 56, fm.get("汉仪润圆", 36), "#333333")
# 副属性值
await bg.text(
"+" + affix.display,
328,
178 + i * 56,
fm.get("汉仪润圆", 36),
"#333333",
"right",
)
return bg
async def draw_character(chara: Character, uid: str):
# 背景
bg = PMImage(await load_image(PATH / f"背景-{chara.element.name}.png"))
# 等级
await bg.text(
f"UID{uid} - Lv.{chara.level}",
36,
20,
fm.get("bahnschrift_regular", 36),
ELEMENT_COLOR[chara.element.name],
)
# 角色立绘
pic = await load_image(
PATH / "character_portrait" / chara.portrait.split("/")[-1],
size=(1300, 1300),
crop=(338, 146, 886, 1176),
)
await bg.paste(pic, (510, 28))
await bg.paste(await load_image(PATH / f"立绘边框-{chara.element.name}.png"), (500, 13))
# 星魂图标
circle = await load_image(PATH / "星魂圆.png")
circle_lock = await load_image(PATH / "星魂圆暗.png")
for i in range(6):
await bg.paste(circle, (524, 443 + i * 104))
icon = PMImage(
await load_image(
PATH / "skill" / chara.rank_icons[i].split("/")[-1], size=(76, 76)
)
)
await bg.paste(icon, (536, 453 + i * 104))
if i >= chara.rank:
await bg.paste(circle_lock, (524, 443 + i * 104))
# 星魂文字
await bg.text(
f"星魂{chara.rank}", (388, 488), 17, fm.get("汉仪雅酷黑", 30), "white", "center"
)
# 角色名称
await bg.text(chara.name, 480, (59, 154), fm.get("汉仪雅酷黑", 72), "white", "right")
# 属性
for i, attr in enumerate(("生命值", "攻击力", "防御力", "速度", "暴击率", "暴击伤害")):
base = next((a for a in chara.attributes if a.name == attr), None)
extra = next((a for a in chara.additions if a.name == attr), None)
if attr in ("暴击率", "暴击伤害"):
n = "0%"
total = (
str(
round(
((base.value if base else 0) + (extra.value if extra else 0))
* 100,
1,
)
)
+ "%"
)
else:
n = "0"
total = str(
int((base.value if base else 0) + (extra.value if extra else 0))
)
await bg.text(
total,
400,
180 + i * 56,
fm.get("bahnschrift_regular", 36),
"#333333",
"right",
)
await bg.text(
base.display if base else n,
483,
175 + i * 56,
fm.get("bahnschrift_regular", 24),
"#333333",
"right",
)
await bg.text(
"+" + (extra.display if extra else n),
483,
196 + i * 56,
fm.get("bahnschrift_regular", 24),
"#5a9922",
"right",
)
for i, attr in enumerate(
("效果抵抗", "效果命中", "击破特攻", "能量恢复效率", f"{chara.element.name}属性伤害提高")
):
affix = next((a for a in chara.properties if a.name == attr), None)
if affix is None:
value = "0%" if attr != "能量恢复效率" else "100%"
else:
value = (
affix.display
if attr != "能量恢复效率"
else (str(round((1 + affix.value) * 100, 1)) + "%")
)
await bg.text(
value,
483,
516 + i * 56,
fm.get("bahnschrift_regular", 36),
"#333333",
"right",
)
# 光锥图标
await bg.paste(
await load_image(
PATH / "light_cone" / chara.light_cone.icon.split("/")[-1], size=(96, 96)
),
(24, 802),
)
# 光锥稀有度
await bg.paste(
await load_image(PATH / f"光锥{chara.light_cone.rarity}星.png"), (123, 813)
)
# 光锥名称
await bg.text(chara.light_cone.name, 138, 847, fm.get("汉仪雅酷黑", 30), "#333333")
# 光锥等级
await bg.draw_rectangle((279, 813, 346, 841), ELEMENT_COLOR[chara.element.name])
await bg.text(
f"lv.{chara.light_cone.level}",
(279, 346),
(813, 841),
fm.get("bahnschrift_regular", 30),
"white",
"center",
)
# 光锥叠影
await bg.draw_rectangle(
(353, 813, 427, 841), LEVEL_COLOR[chara.light_cone.rank - 1]
)
await bg.text(
f"叠影{chara.light_cone.rank}",
(353, 427),
(813, 841),
fm.get("汉仪雅酷黑", 24),
"white",
"center",
)
# 行迹
for i, skill in enumerate(chara.skills[:4]):
# 图标
await bg.paste(
await load_image(PATH / "skill" / skill.icon.split("/")[-1], size=(70, 70)),
(50 + 113 * i, 962),
)
# 等级
await bg.draw_rectangle(
(65 + 113 * i, 1024, 104 + 113 * i, 1056),
LEVEL_COLOR[(skill.level // 2 - 1) if skill.level < 10 else 5],
)
await bg.text(
str(skill.level),
(65 + 113 * i, 104 + 113 * i),
1021,
fm.get("汉仪雅酷黑", 30),
"white",
"center",
)
# 遗器
await asyncio.gather(
*[
bg.paste(
await draw_relic(relic, chara.element),
((19 + (i % 3) * 353, (1131 if i < 3 else 1537))),
)
for i, relic in enumerate(chara.relics)
]
)
# 其他文字
await bg.text("CREATED BY LITTLEPAIMON", 20, 1078, fm.get("汉仪雅酷黑", 30), "#252525")
await bg.text("数据源 MiHoMo", 1060, 1078, fm.get("汉仪雅酷黑", 30), "#252525", "right")
return bg

View File

@ -0,0 +1,240 @@
from typing import List, Optional
from enum import Enum
from pydantic import BaseModel, Field
class Avatar(BaseModel):
id: str
name: str
icon: str
class ChallengeData(BaseModel):
maze_group_id: int
maze_group_index: int
pre_maze_group_index: int
class SpaceInfo(BaseModel):
challenge_data: ChallengeData
pass_area_progress: int
light_cone_count: int
avatar_count: int
achievement_count: int
class Player(BaseModel):
uid: str
nickname: str
level: int
world_level: int
friend_count: int
avatar: Avatar
signature: str
is_display: bool
space_info: SpaceInfo
class Element(BaseModel):
id: str
name: str
color: str
icon: str
class Path(BaseModel):
id: str
name: str
icon: str
class SkillType(str, Enum):
Normal = "Normal"
"""普攻"""
BPSkill = "BPSkill"
"""战技"""
Ultra = "Ultra"
"""终结技"""
Talent = "Talent"
"""天赋"""
MazeNormal = "MazeNormal"
"""dev_连携"""
Maze = "Maze"
"""秘技"""
class Skill(BaseModel):
id: str
name: str
level: int
max_level: int
element: Optional[Element]
type: SkillType
type_text: str
effect: str
simple_desc: str
desc: str
icon: str
class SkillTree(BaseModel):
id: str
level: int
icon: str
class Attribute(BaseModel):
field: str
name: str
icon: str
value: float
display: str
percent: bool
class Property(BaseModel):
type: str
field: str
name: str
icon: str
value: float
display: str
percent: bool
class LightCone(BaseModel):
id: str
name: str
rarity: int
rank: int
level: int
promotion: int
icon: str
preview: str
portrait: str
path: Path
attributes: List[Attribute]
properties: List[Property]
class RelicAffix(BaseModel):
type: str
field: str
name: str
icon: str
value: float
display: str
percent: bool
class Relic(BaseModel):
id: str
name: str
set_id: str
set_name: str
rarity: int
level: int
icon: str
main_affix: RelicAffix
sub_affix: List[RelicAffix]
class RelicSet(BaseModel):
id: str
name: str
icon: str
num: int
desc: str
properties: List[Property]
class Character(BaseModel):
id: str
name: str
rarity: int
rank: int
level: int
promotion: int
icon: str
preview: str
portrait: str
rank_icons: List[str]
path: Path
element: Element
skills: List[Skill]
skill_trees: List[SkillTree]
light_cone: LightCone
relics: List[Relic]
relic_sets: List[RelicSet]
attributes: List[Attribute]
additions: List[Attribute]
properties: List[Property]
class StarRailInfo(BaseModel):
player: Player
characters: List[Character]
# class SubAffix(BaseModel):
# affix_id: int = Field(alias="affixId")
# cnt: int
# step: Optional[int]
# class Relic(BaseModel):
# exp: Optional[int]
# type: int
# tid: int
# sub_affix_list: List[SubAffix] = Field(alias="subAffixList")
# level: int
# main_affix_id: int = Field(alias="mainAffixId")
# class SkillTree(BaseModel):
# point_id: int
# level: int
# class Equipment(BaseModel):
# level: int
# tid: int
# promotion: int
# rank: int
# class AvatarDetail(BaseModel):
# pos: Optional[int]
# avatar_id: int = Field(alias="avatarId")
# promotion: int
# skill_tree_list: List[SkillTree] = Field(alias="skillTreeList")
# relic_list: List = Field(alias="relicList")
# equipment: Equipment
# rank: Optional[int]
# level: int
# class ChallengeInfo(BaseModel):
# schedule_max_level: int = Field(alias="scheduleMaxLevel")
# schedule_group_id: int = Field(alias="scheduleGroupId")
# none_schedule_max_level: int = Field(alias="noneScheduleMaxLevel")
# class RecordInfo(BaseModel):
# achievement_count: int = Field(alias="achievementCount")
# challenge_info: ChallengeInfo = Field(alias="challengeInfo")
# equipment_count: int = Field(alias="equipmentCount")
# max_rogue_challenge_score: int = Field(alias="maxRogueChallengeScore")
# avatar_count: int = Field(alias="avatarCount")
# class StarRailInfo(BaseModel):
# world_level: int = Field(alias="worldLevel")
# platform: str
# friend_count: int = Field(alias="friendCount")
# signature: str
# is_display_list: bool = Field(alias="isDisplayList")
# avatar_detail_list: List[AvatarDetail] = Field(alias="avatarDetailList")
# record_info: RecordInfo = Field(alias="recordInfo")
# head_icon: int = Field(alias="headIcon")
# nickname: str
# assist_avatar_detail: AvatarDetail = Field(alias="assistAvatarDetail")
# level: int
# uid: int

View File

@ -105,8 +105,18 @@ async def check_resource():
(RESOURCE_BASE_PATH / '原神图标资源.zip').unlink()
logger.info('资源检查', '<g>资源下载完成</g>')
except Exception:
logger.warning('资源检查', '下载<m>资源</m>时<r>出错</r>,请尝试更换<m>github资源地址</m>')
logger.warning('资源检查', '下载<m>资源</m>时<r>出错</r>,请尝试更换<m>github资源地址</m>')
else:
if not (RESOURCE_BASE_PATH / 'LittlePaimon' / 'star_rail').is_dir():
try:
await aiorequests.download(
url=f'{config.github_proxy}https://raw.githubusercontent.com/CMHopeSunshine/LittlePaimonRes/main/star_rail.zip',
save_path=RESOURCE_BASE_PATH / 'star_rail.zip')
zipfile.ZipFile(RESOURCE_BASE_PATH / 'star_rail.zip').extractall(RESOURCE_BASE_PATH / 'LittlePaimon' / 'star_rail')
(RESOURCE_BASE_PATH / 'star_rail.zip').unlink()
logger.info('资源检查', '<g>星穹铁道相关资源下载完成</g>')
except Exception:
logger.warning('资源检查', '下载<m>星穹铁道资源</m>时<r>出错</r>,请尝试更换<m>github资源地址</m>')
try:
resource_list = await aiorequests.get(
f'{config.github_proxy}https://raw.githubusercontent.com/CMHopeSunshine/LittlePaimonRes/main/resources_list.json',