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, )