import json import asyncio from pathlib import Path from typing import Tuple, Union from nonebot.log import logger from PIL import Image, ImageDraw 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 from ..utils.alias.avatarId_and_name_covert import name_to_avatar_id TEXT_PATH = Path(__file__).parent / 'texture2d' PLAYER_PATH = Path(__file__).parents[1] / 'player' RESOURCE_PATH = Path(__file__).parents[1] / 'resource' CHAR_PATH = RESOURCE_PATH / 'chars' STAND_PATH = RESOURCE_PATH / 'char_stand' WEAPON_PATH = RESOURCE_PATH / 'weapon' title_mask = Image.open(TEXT_PATH / 'title_mask.png') gs_font_16 = genshin_font_origin(16) gs_font_23 = genshin_font_origin(23) gs_font_26 = genshin_font_origin(26) gs_font_28 = genshin_font_origin(28) gs_font_40 = genshin_font_origin(40) first_color = (29, 29, 29) brown_color = (41, 25, 0) red_color = (255, 66, 66) green_color = (74, 189, 119) CHANGE_MAP = {'常驻祈愿': 'normal', '角色祈愿': 'char', '武器祈愿': 'weapon'} async def _get_tag(level: int) -> Image.Image: return Image.open(TEXT_PATH / f'tag_{level}.png') async def _draw_card( img: Image.Image, xy_point: Tuple[int, int], type: str, name: str, gacha_num: int, ): card_img = Image.open(TEXT_PATH / 'item_bg.png') card_img_draw = ImageDraw.Draw(card_img) point = (1, 0) text_point = (55, 124) if type == '角色': name = await name_to_avatar_id(name) item_pic = ( Image.open(CHAR_PATH / f'{name}.png') .convert('RGBA') .resize((108, 108)) ) else: item_pic = ( Image.open(WEAPON_PATH / f'{name}.png') .convert('RGBA') .resize((108, 108)) ) card_img.paste(item_pic, point, item_pic) if gacha_num >= 81: text_color = red_color elif gacha_num <= 55: text_color = green_color else: text_color = brown_color card_img_draw.text( text_point, f'{gacha_num}抽', text_color, gs_font_23, 'mm' ) img.paste(card_img, xy_point, card_img) async def draw_gachalogs_img(uid: str) -> Union[bytes, str]: path = PLAYER_PATH / str(uid) / 'gacha_logs.json' if not path.exists(): return '你还没有祈愿数据噢~\n请添加Stoken后使用命令`刷新抽卡记录`更新祈愿数据~' with open(path, 'r', encoding='UTF-8') as f: gacha_data = json.load(f) # 数据初始化 total_data = {} for i in ['常驻祈愿', '角色祈愿', '武器祈愿']: total_data[i] = { 'total': 0, 'avg': 0, 'remain': 0, 'item': '', 'list': [], } data_list = gacha_data['data'][i] is_not_first = False num_temp = [] num = 1 for data in data_list[::-1]: if data['rank_type'] == '5': data['gacha_num'] = num # 排除第一个 if is_not_first: num_temp.append(num) if num_temp == []: is_not_first = True total_data[i]['list'].append(data) num = 1 total_data[i]['total'] += 1 else: num += 1 if i == '武器祈愿': total_data[i]['item'] = '班尼特' elif i == '常驻祈愿': total_data[i]['item'] = '刻晴' else: g = {} for k in total_data[i]['list']: if k['name'] in g: g[k['name']] += 1 else: g[k['name']] = 1 total_data[i]['item'] = max(g, key=lambda x: g[x]) total_data[i]['item'] = await name_to_avatar_id(total_data[i]['item']) total_data[i]['remain'] = num - 1 if len(num_temp) == 0: total_data[i]['avg'] = 0 else: total_data[i]['avg'] = float( '{:.2f}'.format(sum(num_temp) / len(num_temp)) ) # 常量偏移数据 single_y = 150 title_y = 300 # 计算图片尺寸 normal_y = ( 1 + ((total_data['常驻祈愿']['total'] - 1) // 6) ) * single_y + title_y char_y = ( 1 + ((total_data['角色祈愿']['total'] - 1) // 6) ) * single_y + title_y weapon_y = ( 1 + ((total_data['武器祈愿']['total'] - 1) // 6) ) * single_y + title_y # 获取背景图片各项参数 based_w = 800 based_h = normal_y + char_y + weapon_y + 200 white_overlay = Image.new('RGBA', (based_w, based_h), (255, 255, 255, 220)) bg_img = await get_simple_bg(based_w, based_h) bg_img.paste(white_overlay, (0, 0), white_overlay) # 总标题 all_title = Image.open(TEXT_PATH / 'all_title.png') all_title_draw = ImageDraw.Draw(all_title) # 写UID all_title_draw.text((47, 181), f'{uid}', first_color, gs_font_26, 'lm') bg_img.paste(all_title, (0, 0), all_title) # 处理title # {'total': 0, 'avg': 0, 'remain': 0, 'list': []} type_list = ['常驻祈愿', '角色祈愿', '武器祈愿'] y_extend = 0 for index, i in enumerate(type_list): title = Image.open(TEXT_PATH / 'gahca_title.png') title_pic = ( Image.open(STAND_PATH / f'{total_data[i]["item"]}.png') .convert('RGBA') .resize((1292, 792)) ) temp_pic = Image.new('RGBA', (800, 300), (0, 0, 0, 0)) temp_pic.paste(title_pic, (-70, -56), title_pic) temp_mask = Image.new('RGBA', (800, 300), (0, 0, 0, 0)) temp_mask.paste(temp_pic, (0, 0), title_mask) temp_mask.putalpha( temp_mask.getchannel('A').point( lambda x: round(x * 0.6) if x > 0 else 0 ) ) title = Image.alpha_composite(title, temp_mask) if total_data[i]['avg'] == 0: level = 3 else: # 非酋 <= 90 # 小非 <= 80 # 稳定 <= 72 # 小欧 <= 60 # 欧皇 <= 43 # 武器统一减10 for num_index, num in enumerate([42, 58, 68, 75, 90]): if i == '武器祈愿': num -= 10 if total_data[i]['avg'] <= num: level = 5 - num_index break else: level = 3 tag_pic = await _get_tag(level) tag_pic = tag_pic.resize((208, 88)) title.paste(tag_pic, (35, 54), tag_pic) title_draw = ImageDraw.Draw(title) # 卡池 title_draw.text((245, 86), i, first_color, gs_font_40, 'lm') # 抽卡时间 if gacha_data['data'][i]: first_time = gacha_data['data'][i][0]['time'].split(' ')[0] else: first_time = '暂未抽过卡!' title_draw.text((245, 123), first_time, first_color, gs_font_28, 'lm') # 平均抽卡数量 title_draw.text( (108, 209), str(total_data[i]['avg']), first_color, gs_font_40, 'mm', ) title_draw.text( (261, 209), str(gacha_data[f'{CHANGE_MAP[i]}_gacha_num']), first_color, gs_font_40, 'mm', ) title_draw.text( (104, 160), str(total_data[i]['remain']), red_color, gs_font_28, 'mm', ) y_extend += ( (1 + ((total_data[type_list[index - 1]]['total'] - 1) // 6)) * 150 if index != 0 else 0 ) y = index * title_y + y_extend + 200 bg_img.paste(title, (0, y), title) tasks = [] for item_index, item in enumerate(total_data[i]['list']): item_x = (item_index % 6) * 129 + 20 item_y = (item_index // 6) * 150 + y + title_y xy_point = (item_x, item_y) tasks.append( _draw_card( bg_img, xy_point, item['item_type'], item['name'], item['gacha_num'], ) ) await asyncio.gather(*tasks) tasks.clear() # 发送图片 res = await convert_img(bg_img) logger.info('[查询抽卡]绘图已完成,等待发送!') return res