mirror of
https://github.com/KimigaiiWuyi/GenshinUID.git
synced 2025-05-30 04:00:31 +08:00
449 lines
14 KiB
Python
449 lines
14 KiB
Python
import asyncio
|
|
from pathlib import Path
|
|
|
|
from nonebot.log import logger
|
|
from PIL import Image, ImageDraw
|
|
|
|
from ..utils.get_cookies.get_cookies import GetCookies
|
|
from ..utils.mhy_api.get_mhy_data import get_character
|
|
from ..utils.download_resource.download_url import download_file
|
|
from ..utils.draw_image_tools.send_image_tool import convert_img
|
|
from ..utils.draw_image_tools.draw_image_tool import get_simple_bg
|
|
from ..utils.genshin_fonts.genshin_fonts import genshin_font_origin
|
|
|
|
# 确定路径
|
|
TEXT_PATH = Path(__file__).parent / 'texture2d'
|
|
RESOURCE_PATH = Path(__file__).parents[1] / 'resource'
|
|
CHAR_PATH = RESOURCE_PATH / 'chars'
|
|
WEAPON_PATH = RESOURCE_PATH / 'weapon'
|
|
CHAR_STAND_PATH = RESOURCE_PATH / 'char_stand'
|
|
REL_PATH = RESOURCE_PATH / 'reliquaries'
|
|
FETTER_PATH = RESOURCE_PATH / 'texture2d' / 'fetter'
|
|
TALENT_PATH = RESOURCE_PATH / 'texture2d' / 'talent'
|
|
WEAPON_BG_PATH = RESOURCE_PATH / 'texture2d' / 'weapon'
|
|
LEVEL_PATH = RESOURCE_PATH / 'texture2d' / 'level'
|
|
|
|
# 打开图片
|
|
char_card_bg4 = Image.open(TEXT_PATH / 'char_card_bg4.png')
|
|
char_card_bg5 = Image.open(TEXT_PATH / 'char_card_bg5.png')
|
|
char_card_fg = Image.open(TEXT_PATH / 'char_card_fg.png')
|
|
char_card_mask = Image.open(TEXT_PATH / 'char_card_mask.png')
|
|
role_info_fg = Image.open(TEXT_PATH / 'role_info_fg.png')
|
|
|
|
char_card8_bg4 = Image.open(TEXT_PATH / 'char_card8_bg4.png')
|
|
char_card8_bg5 = Image.open(TEXT_PATH / 'char_card8_bg5.png')
|
|
char_card8_fg = Image.open(TEXT_PATH / 'char_card8_fg.png')
|
|
char_card8_mask = Image.open(TEXT_PATH / 'char_card8_mask.png')
|
|
|
|
|
|
# 生成几个字号
|
|
gs_font_16 = genshin_font_origin(16)
|
|
gs_font_23 = genshin_font_origin(23)
|
|
gs_font_28 = genshin_font_origin(28)
|
|
gs_font_40 = genshin_font_origin(40)
|
|
|
|
# 文字颜色
|
|
text_color = (68, 66, 64)
|
|
|
|
|
|
async def get_fetter_pic(fetter: int) -> Image.Image:
|
|
return Image.open(FETTER_PATH / f'fetter_{fetter}.png')
|
|
|
|
|
|
async def get_talent_pic(talent: int) -> Image.Image:
|
|
return Image.open(TALENT_PATH / f'talent_{talent}.png')
|
|
|
|
|
|
async def get_weapon_pic(weapon_rarity: int) -> Image.Image:
|
|
return Image.open(WEAPON_BG_PATH / f'weapon_bg{weapon_rarity}.png')
|
|
|
|
|
|
async def get_level_pic(level: int) -> Image.Image:
|
|
return Image.open(LEVEL_PATH / f'level_{level}.png')
|
|
|
|
|
|
async def _draw_char_full_pic(img: Image.Image, char_data: dict, index: int):
|
|
result = Image.new('RGBA', (250, 150), (0, 0, 0, 0))
|
|
char_card_img = Image.new('RGBA', (250, 150), (0, 0, 0, 0))
|
|
if char_data['rarity'] >= 5:
|
|
char_card_img.paste(char_card_bg5, (0, 0))
|
|
else:
|
|
char_card_img.paste(char_card_bg4, (0, 0))
|
|
# 确认武器路径
|
|
weapon_pic_path = WEAPON_PATH / f'{char_data["weapon"]["name"]}.png'
|
|
# 不存在自动下载
|
|
if not weapon_pic_path.exists():
|
|
await download_file(
|
|
char_data['weapon']['icon'],
|
|
5,
|
|
f'{char_data["weapon"]["name"]}.png',
|
|
)
|
|
# 粘贴武器图片
|
|
weapon_pic = Image.open(weapon_pic_path).convert('RGBA')
|
|
weapon_pic_scale = weapon_pic.resize((50, 50))
|
|
# 确认角色头像路径
|
|
char_pic_path = CHAR_PATH / f'{char_data["id"]}.png'
|
|
# 不存在自动下载
|
|
if not char_pic_path.exists():
|
|
await download_file(char_data['icon'], 1, f'{char_data["id"]}.png')
|
|
# 粘贴角色头像
|
|
char_img = Image.open(CHAR_PATH / f'{char_data["id"]}.png').convert('RGBA')
|
|
# 缩放至合适大小
|
|
char_img_scale = char_img.resize((148, 148))
|
|
char_card_img.paste(char_img_scale, (-20, 5), char_img_scale)
|
|
char_card_img.paste(char_card_fg, (0, 0), char_card_fg)
|
|
# 命座和好感的图片
|
|
fetter_pic = await get_fetter_pic(char_data['fetter'])
|
|
talent_pic = await get_talent_pic(char_data['actived_constellation_num'])
|
|
char_card_img.paste(fetter_pic, (17, 110), fetter_pic)
|
|
char_card_img.paste(talent_pic, (177, 24), talent_pic)
|
|
# 武器
|
|
weapon_bg_pic = await get_weapon_pic(char_data['weapon']['rarity'])
|
|
char_card_img.paste(weapon_bg_pic, (105, 83), weapon_bg_pic)
|
|
char_card_img.paste(weapon_pic_scale, (105, 83), weapon_pic_scale)
|
|
char_draw = ImageDraw.Draw(char_card_img)
|
|
# 写字
|
|
char_draw.text(
|
|
(114, 37),
|
|
f'Lv{char_data["level"]}',
|
|
text_color,
|
|
gs_font_23,
|
|
anchor='lm',
|
|
)
|
|
char_draw.text(
|
|
(162, 96),
|
|
f'Lv{char_data["weapon"]["level"]}',
|
|
text_color,
|
|
gs_font_23,
|
|
anchor='lm',
|
|
)
|
|
char_draw.text(
|
|
(162, 123),
|
|
f'精炼{char_data["weapon"]["affix_level"]}',
|
|
text_color,
|
|
gs_font_23,
|
|
anchor='lm',
|
|
)
|
|
result.paste(char_card_img, (0, 0), char_card_mask)
|
|
img.paste(
|
|
result,
|
|
(15 + (index % 4) * 265, 1199 + (index // 4) * 160),
|
|
result,
|
|
)
|
|
|
|
|
|
async def draw_pic(uid: str):
|
|
# 获取Cookies# 获取Cookies
|
|
data_def = GetCookies()
|
|
retcode = await data_def.get_useable_cookies(uid)
|
|
if retcode:
|
|
return retcode
|
|
use_cookies = data_def.useable_cookies
|
|
raw_data = data_def.raw_data
|
|
|
|
# 记录数据
|
|
if raw_data:
|
|
raw_data = raw_data['data']
|
|
char_data = raw_data['avatars']
|
|
else:
|
|
return '没有找到角色信息!'
|
|
|
|
char_ids = []
|
|
for i in char_data:
|
|
char_ids.append(i['id'])
|
|
|
|
char_rawdata = await get_character(uid, char_ids, use_cookies)
|
|
char_datas = char_rawdata['data']['avatars']
|
|
|
|
for index, i in enumerate(char_datas):
|
|
if i['rarity'] > 5:
|
|
char_datas[index]['rarity'] = 3
|
|
break
|
|
|
|
char_datas.sort(
|
|
key=lambda x: (
|
|
-x['rarity'],
|
|
-x['fetter'],
|
|
-x['actived_constellation_num'],
|
|
)
|
|
)
|
|
|
|
# 确定角色占用行数
|
|
char_num = len(char_datas)
|
|
char_hang = (
|
|
1 + (char_num - 1) // 4
|
|
if char_num > 8
|
|
else (char_num // 2) + (char_num % 2)
|
|
)
|
|
|
|
# 获取背景图片各项参数
|
|
based_w = 1080
|
|
if char_num > 8:
|
|
based_h = 1165 + char_hang * 160 + 50
|
|
else:
|
|
based_h = 1165 + char_hang * 260 + 50
|
|
img = await get_simple_bg(based_w, based_h)
|
|
white_overlay = Image.new('RGBA', (based_w, based_h), (255, 251, 242, 211))
|
|
img.paste(white_overlay, (0, 0), white_overlay)
|
|
char_import = Image.open(
|
|
CHAR_STAND_PATH / f'{char_datas[0]["id"]}.png'
|
|
).convert('RGBA')
|
|
img.paste(char_import, (-540, -180), char_import)
|
|
img.paste(role_info_fg, (0, 0), role_info_fg)
|
|
|
|
# 绘制基础信息文字
|
|
text_draw = ImageDraw.Draw(img)
|
|
text_draw.text((65, 468), f'UID{uid}', text_color, gs_font_40, anchor='lm')
|
|
# 已获角色
|
|
text_draw.text(
|
|
(1024, 569),
|
|
str(raw_data['stats']['avatar_number']),
|
|
text_color,
|
|
gs_font_40,
|
|
anchor='rm',
|
|
)
|
|
# 活跃天数
|
|
text_draw.text(
|
|
(1024, 294),
|
|
str(raw_data['stats']['active_day_number']),
|
|
text_color,
|
|
gs_font_40,
|
|
anchor='rm',
|
|
)
|
|
# 成就数量
|
|
text_draw.text(
|
|
(1024, 386),
|
|
str(raw_data['stats']['achievement_number']),
|
|
text_color,
|
|
gs_font_40,
|
|
anchor='rm',
|
|
)
|
|
# 深渊
|
|
text_draw.text(
|
|
(1024, 477),
|
|
raw_data['stats']['spiral_abyss'],
|
|
text_color,
|
|
gs_font_40,
|
|
anchor='rm',
|
|
)
|
|
|
|
# 世界探索
|
|
world_exp = raw_data['world_explorations']
|
|
world_list = []
|
|
# 须弥占坑 & 城市补足
|
|
for city_index in range(1, 9):
|
|
world_list.append(
|
|
{
|
|
'id': city_index,
|
|
'exp': ['0.0%'],
|
|
'extra': [{'name': '等阶', 'level': 0}],
|
|
}
|
|
)
|
|
for world_part in world_exp:
|
|
# 添加探索值
|
|
temp = {
|
|
'id': world_part['id'],
|
|
'exp': [f'{world_part["exploration_percentage"] / 10}%'],
|
|
'extra': [{'name': '等阶', 'level': world_part['level']}],
|
|
}
|
|
# 添加属性值
|
|
for offering in world_part['offerings']:
|
|
temp['extra'].append(
|
|
{'name': offering['name'], 'level': offering['level']}
|
|
)
|
|
world_list[world_part['id'] - 1] = temp
|
|
world_list.sort(key=lambda x: (-x['id']), reverse=True)
|
|
# 令层岩地下和地上合并
|
|
world_list[5]['exp'].append(world_list[6]['exp'][0])
|
|
# 移除地下
|
|
world_list.pop(6)
|
|
# 添加宝箱信息和锚点
|
|
chest_data = [
|
|
'magic_chest_number',
|
|
'common_chest_number',
|
|
'exquisite_chest_number',
|
|
'precious_chest_number',
|
|
'luxurious_chest_number',
|
|
# 'way_point_number',
|
|
]
|
|
for status_index, status in enumerate(chest_data):
|
|
world_list.append(
|
|
{
|
|
'id': 500 + status_index,
|
|
'exp': [str(raw_data['stats'][status])],
|
|
'extra': [],
|
|
}
|
|
)
|
|
task = []
|
|
for world_index, world in enumerate(world_list):
|
|
task.append(_draw_world_exp_pic(img, text_draw, world, world_index))
|
|
await asyncio.gather(*task)
|
|
|
|
tasks = []
|
|
if char_num > 8:
|
|
for index, char in enumerate(char_datas):
|
|
tasks.append(
|
|
_draw_char_full_pic(
|
|
img,
|
|
char,
|
|
index,
|
|
)
|
|
)
|
|
else:
|
|
for index, char in enumerate(char_datas):
|
|
tasks.append(
|
|
_draw_char_8_pic(
|
|
img,
|
|
char,
|
|
index,
|
|
)
|
|
)
|
|
await asyncio.gather(*tasks)
|
|
|
|
res = await convert_img(img)
|
|
logger.info('[查询角色信息]绘图已完成,等待发送!')
|
|
return res
|
|
|
|
|
|
async def _draw_world_exp_pic(
|
|
img: Image.Image,
|
|
text_draw: ImageDraw.ImageDraw,
|
|
world: dict,
|
|
world_index: int,
|
|
):
|
|
offset_x = 258
|
|
offset_y = 171
|
|
for world_exp_index, world_exp in enumerate(world['exp']):
|
|
text_draw.text(
|
|
(
|
|
260 + world_index % 4 * offset_x,
|
|
700 + world_index // 4 * offset_y + world_exp_index * 28,
|
|
),
|
|
world_exp,
|
|
text_color,
|
|
gs_font_28,
|
|
anchor='rm',
|
|
)
|
|
for offering_index, offering in enumerate(world['extra']):
|
|
if offering["name"] == "等阶":
|
|
level_pic = await get_level_pic(offering["level"])
|
|
img.paste(
|
|
level_pic,
|
|
(
|
|
199 + world_index % 4 * offset_x,
|
|
650 + world_index // 4 * offset_y,
|
|
),
|
|
level_pic,
|
|
)
|
|
else:
|
|
text_draw.text(
|
|
(
|
|
260 + world_index % 4 * offset_x,
|
|
(len(world['exp']) - 1) * 28
|
|
+ 711
|
|
+ world_index // 4 * offset_y
|
|
+ offering_index * 23,
|
|
),
|
|
f'{str(offering["name"])}:{str(offering["level"])}',
|
|
text_color,
|
|
gs_font_23,
|
|
anchor='rm',
|
|
)
|
|
|
|
|
|
async def _draw_char_8_pic(img: Image.Image, char_data: dict, index: int):
|
|
"""
|
|
绘制8人角色图片
|
|
"""
|
|
result = Image.new('RGBA', (510, 225), (0, 0, 0, 0))
|
|
char_card_img = Image.new('RGBA', (510, 225), (0, 0, 0, 0))
|
|
if char_data['rarity'] >= 5:
|
|
char_card_img.paste(char_card8_bg5, (0, 0))
|
|
else:
|
|
char_card_img.paste(char_card8_bg4, (0, 0))
|
|
# 确认武器路径
|
|
weapon_pic_path = WEAPON_PATH / f'{char_data["weapon"]["name"]}.png'
|
|
# 不存在自动下载
|
|
if not weapon_pic_path.exists():
|
|
await download_file(
|
|
char_data['weapon']['icon'],
|
|
5,
|
|
f'{char_data["weapon"]["name"]}.png',
|
|
)
|
|
# 粘贴武器图片
|
|
weapon_pic = Image.open(weapon_pic_path).convert('RGBA')
|
|
weapon_pic_scale = weapon_pic.resize((50, 50))
|
|
# 确认角色头像路径
|
|
char_pic_path = CHAR_PATH / f'{char_data["id"]}.png'
|
|
# 不存在自动下载
|
|
if not char_pic_path.exists():
|
|
await download_file(char_data['icon'], 1, f'{char_data["id"]}.png')
|
|
# 粘贴角色图片
|
|
char_img = Image.open(CHAR_PATH / f'{char_data["id"]}.png').convert('RGBA')
|
|
|
|
# 角色立绘
|
|
char_stand_img = Image.open(
|
|
CHAR_STAND_PATH / f'{char_data["id"]}.png'
|
|
).convert('RGBA')
|
|
char_stand_img.putalpha(
|
|
char_stand_img.getchannel('A').point(
|
|
lambda x: round(x * 0.4) if x > 0 else 0
|
|
)
|
|
)
|
|
char_card_img.paste(char_stand_img, (-912, -313), char_stand_img)
|
|
char_card_img.paste(char_img, (-14, -28), char_img)
|
|
char_card_img.paste(char_card8_fg, (0, 0), char_card8_fg)
|
|
fetter_pic = await get_fetter_pic(char_data['fetter'])
|
|
talent_pic = await get_talent_pic(char_data['actived_constellation_num'])
|
|
weapon_bg_pic = await get_weapon_pic(char_data['weapon']['rarity'])
|
|
char_card_img.paste(fetter_pic, (355, 27), fetter_pic)
|
|
char_card_img.paste(talent_pic, (435, 24), talent_pic)
|
|
char_card_img.paste(weapon_bg_pic, (21, 158), weapon_bg_pic)
|
|
char_card_img.paste(weapon_pic_scale, (21, 158), weapon_pic_scale)
|
|
for equip_index, equip in enumerate(char_data['reliquaries']):
|
|
equip_bg = await get_weapon_pic(equip['rarity'])
|
|
equip_pic = (
|
|
Image.open(REL_PATH / f'{equip["name"]}.png')
|
|
.convert('RGBA')
|
|
.resize((50, 50))
|
|
)
|
|
char_card_img.paste(equip_bg, (242 + equip_index * 50, 170), equip_bg)
|
|
char_card_img.paste(
|
|
equip_pic, (242 + equip_index * 50, 170), equip_pic
|
|
)
|
|
char_draw = ImageDraw.Draw(char_card_img)
|
|
char_draw.text(
|
|
(254, 36),
|
|
f'Lv{char_data["level"]}',
|
|
text_color,
|
|
gs_font_40,
|
|
anchor='lm',
|
|
)
|
|
char_draw.text(
|
|
(81, 196),
|
|
f'Lv{char_data["weapon"]["level"]}',
|
|
text_color,
|
|
gs_font_23,
|
|
anchor='lm',
|
|
)
|
|
char_draw.text(
|
|
(81, 168),
|
|
f'{char_data["weapon"]["name"]}',
|
|
text_color,
|
|
gs_font_23,
|
|
anchor='lm',
|
|
)
|
|
char_draw.text(
|
|
(138, 196),
|
|
f'精炼{char_data["weapon"]["affix_level"]}',
|
|
text_color,
|
|
gs_font_23,
|
|
anchor='lm',
|
|
)
|
|
result.paste(char_card_img, (0, 0), char_card8_mask)
|
|
img.paste(
|
|
result,
|
|
(15 + (index % 2) * 520, 1199 + (index // 2) * 250),
|
|
result,
|
|
)
|