import time import asyncio from pathlib import Path from typing import Union, Optional from nonebot.log import logger from PIL import Image, ImageDraw from ..utils.get_cookies.get_cookies import GetCookies 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' CHAR_STAND_PATH = RESOURCE_PATH / 'char_stand' CHAR_SIDE_PATH = RESOURCE_PATH / 'char_side' TALENT_PATH = RESOURCE_PATH / 'texture2d' / 'talent' abyss_title_pic = Image.open(TEXT_PATH / 'abyss_title.png') char_mask = Image.open(TEXT_PATH / 'char_mask.png') char_frame = Image.open(TEXT_PATH / 'char_frame.png') text_title_color = (29, 29, 29) text_floor_color = (30, 31, 25) genshin_font_70 = genshin_font_origin(70) genshin_font_32 = genshin_font_origin(32) genshin_font_27 = genshin_font_origin(27) async def get_abyss_star_pic(star: int) -> Image.Image: star_pic = Image.open(TEXT_PATH / f'star{star}.png') return star_pic async def get_rarity_pic(rarity: int) -> Image.Image: rarity_pic = Image.open(TEXT_PATH / f'rarity{rarity}.png') return rarity_pic async def get_talent_pic(talent: int) -> Image.Image: return Image.open(TALENT_PATH / f'talent_{talent}.png') async def get_rank_data(data: dict, path: Path): char_id = data[0]['avatar_id'] # 只下载侧视图 if path == CHAR_SIDE_PATH: # 确认角色头像路径 char_side_path = CHAR_PATH / f'{char_id}.png' # 不存在自动下载 if not char_side_path.exists(): await download_file(data[0]['avatar_icon'], 3, f'{char_id}.png') char_pic = Image.open(path / f'{char_id}.png').convert('RGBA') if path == CHAR_STAND_PATH: char_pic = char_pic.resize((862, 528), Image.Resampling.BICUBIC) elif path == CHAR_SIDE_PATH: char_pic = char_pic.resize((60, 60), Image.Resampling.BICUBIC) rank_value = str(data[0]['value']) return char_pic, rank_value async def _draw_abyss_card( char: dict, talent_num: str, floor_pic: Image.Image, index_char: int, index_part: int, ): char_card = Image.new('RGBA', (150, 190), (0, 0, 0, 0)) # 根据稀有度获取背景 char_bg = await get_rarity_pic(char['rarity']) # 确认角色头像路径 char_pic_path = CHAR_PATH / f'{char["id"]}.png' # 不存在自动下载 if not char_pic_path.exists(): await download_file(char['icon'], 1, f'{char["id"]}.png') char_pic = ( Image.open(char_pic_path) .convert('RGBA') .resize((150, 150), Image.Resampling.LANCZOS) # type: ignore ) char_img = Image.new('RGBA', (150, 190), (0, 0, 0, 0)) char_img.paste(char_pic, (0, 3), char_pic) char_bg = Image.alpha_composite(char_bg, char_img) char_card.paste(char_bg, (0, 0), char_mask) char_card = Image.alpha_composite(char_card, char_frame) talent_pic = await get_talent_pic(int(talent_num)) char_card.paste(talent_pic, (83, 156), talent_pic) char_card_draw = ImageDraw.Draw(char_card) char_card_draw.text( (9, 172), f'Lv.{char["level"]}', font=genshin_font_27, fill=text_floor_color, anchor='lm', ) floor_pic.paste( char_card, (0 + 155 * index_char, 50 + index_part * 195), char_card, ) async def _draw_floor_card( level_star: int, floor_pic: Image.Image, bg_img: Image.Image, time_str: str, index_floor: int, ): star_pic = await get_abyss_star_pic(level_star) floor_pic.paste(star_pic, (420, -5), star_pic) floor_pic_draw = ImageDraw.Draw(floor_pic) floor_pic_draw.text( (31, 25), time_str, font=genshin_font_27, fill=text_floor_color, anchor='lm', ) bg_img.paste(floor_pic, (5, 415 + index_floor * 440), floor_pic) async def draw_abyss_img( uid: str, floor: Optional[int] = None, schedule_type: str = '1', ) -> Union[bytes, str]: # 获取Cookies data_def = GetCookies() retcode = await data_def.get_useable_cookies(uid) if retcode: return retcode raw_data = data_def.raw_data raw_abyss_data = await data_def.get_spiral_abyss_data(schedule_type) # 获取数据 if raw_abyss_data: raw_abyss_data = raw_abyss_data['data'] else: return '没有获取到深渊数据' if raw_data: char_data = raw_data['data']['avatars'] else: return '没有获取到角色数据' char_temp = {} # 获取查询者数据 is_unfull = False if floor: floor = floor - 9 if floor < 0: return '楼层不能小于9层!' if len(raw_abyss_data['floors']) >= floor + 1: floors_data = raw_abyss_data['floors'][floor] else: return '你还没有挑战该层!' else: if len(raw_abyss_data['floors']) == 0: return '你还没有挑战本期深渊!\n可以使用[上期深渊]命令查询上期~' floors_data = raw_abyss_data['floors'][-1] levels_num = len(floors_data['levels']) if floors_data['levels'][0]['battles']: floors_title = str(floors_data['index']) + '层' else: floors_title = '统计' is_unfull = True # 获取背景图片各项参数 based_w = 625 based_h = 415 if is_unfull else 415 + levels_num * 440 white_overlay = Image.new('RGBA', (based_w, based_h), (255, 255, 255, 188)) bg_img = await get_simple_bg(based_w, based_h) bg_img.paste(white_overlay, (0, 0), white_overlay) abyss_title = Image.new('RGBA', (625, 415), (0, 0, 0, 0)) damage_rank = raw_abyss_data['damage_rank'] defeat_rank = raw_abyss_data['defeat_rank'] take_damage_rank = raw_abyss_data['take_damage_rank'] normal_skill_rank = raw_abyss_data['normal_skill_rank'] energy_skill_rank = raw_abyss_data['energy_skill_rank'] dmg_pic, dmg_val = await get_rank_data(damage_rank, CHAR_STAND_PATH) defeat_pic, defeat_val = await get_rank_data(defeat_rank, CHAR_SIDE_PATH) ( take_damage_pic, take_damage_val, ) = await get_rank_data(take_damage_rank, CHAR_SIDE_PATH) ( normal_skill_pic, normal_skill_val, ) = await get_rank_data(normal_skill_rank, CHAR_SIDE_PATH) ( energy_skill_pic, energy_skill_val, ) = await get_rank_data(energy_skill_rank, CHAR_SIDE_PATH) abyss_title.paste(dmg_pic, (13, -42), dmg_pic) abyss_title = Image.alpha_composite(abyss_title, abyss_title_pic) abyss_title.paste(defeat_pic, (5, 171), defeat_pic) abyss_title.paste(take_damage_pic, (5, 171 + 54), take_damage_pic) abyss_title.paste(normal_skill_pic, (5, 171 + 54 * 2), normal_skill_pic) abyss_title.paste(energy_skill_pic, (5, 171 + 54 * 3), energy_skill_pic) abyss_title_draw = ImageDraw.Draw(abyss_title) abyss_title_draw.text( (41, 95), f'深渊{floors_title}', font=genshin_font_70, fill=text_title_color, anchor='lm', ) abyss_title_draw.text( (41, 139), f'UID{uid}', font=genshin_font_27, fill=text_title_color, anchor='lm', ) abyss_title_draw.text( (610, 282), dmg_val, font=genshin_font_32, fill=text_title_color, anchor='rm', ) abyss_title_draw.text( (610, 357), str(raw_abyss_data['total_battle_times']), font=genshin_font_32, fill=text_title_color, anchor='rm', ) abyss_title_draw.text( (64, 217), defeat_val, font=genshin_font_27, fill=text_title_color, anchor='lm', ) abyss_title_draw.text( (64, 217 + 54), take_damage_val, font=genshin_font_27, fill=text_title_color, anchor='lm', ) abyss_title_draw.text( (64, 217 + 54 * 2), normal_skill_val, font=genshin_font_27, fill=text_title_color, anchor='lm', ) abyss_title_draw.text( (64, 217 + 54 * 3), energy_skill_val, font=genshin_font_27, fill=text_title_color, anchor='lm', ) bg_img.paste(abyss_title, (0, 0), abyss_title) if is_unfull: pass else: task = [] for index_floor, level in enumerate(floors_data['levels']): floor_pic = Image.new('RGBA', (615, 440), (0, 0, 0, 0)) level_star = level['star'] timestamp = int(level['battles'][0]['timestamp']) time_array = time.localtime(timestamp) time_str = time.strftime('%Y-%m-%d %H:%M:%S', time_array) for index_part, battle in enumerate(level['battles']): for index_char, char in enumerate(battle['avatars']): # 获取命座 if char["id"] in char_temp: talent_num = char_temp[char["id"]] else: for i in char_data: if i["id"] == char["id"]: talent_num = str( i["actived_constellation_num"] ) char_temp[char["id"]] = talent_num break task.append( _draw_abyss_card( char, talent_num, # type: ignore floor_pic, index_char, index_part, ) ) await asyncio.gather(*task) task.clear() task.append( _draw_floor_card( level_star, floor_pic, bg_img, time_str, index_floor ) ) await asyncio.gather(*task) res = await convert_img(bg_img) logger.info('[查询深渊信息]绘图已完成,等待发送!') return res