diff --git a/LittlePaimon/config/data/alias.json b/LittlePaimon/config/data/alias.json index 2e7e1bf..fb28d15 100644 --- a/LittlePaimon/config/data/alias.json +++ b/LittlePaimon/config/data/alias.json @@ -62,7 +62,8 @@ "10000102": ["散兵", "伞兵", "国崩", "卢本伟", "大炮", "sb"], "10000103": ["迪希雅"], "10000104": ["艾尔海森", "苏"], - "10000105": ["白术"] + "10000105": ["白术"], + "10000106": ["莱依拉"] }, "武器": { "磐岩结绿": [ @@ -641,6 +642,19 @@ "西福斯的月光", "西福斯", "月光" + ], + "赤沙之杖": [ + "赤沙之杖", + "赤沙" + ], + "风信之锋": [ + "风信之锋", + "风信" + ], + "玛海菈的水色": [ + "玛海菈的水色", + "玛海菈", + "水色" ] }, "圣遗物": { diff --git a/LittlePaimon/plugins/Paimon_Info/abyss_statistics.py b/LittlePaimon/plugins/Paimon_Info/abyss_statistics.py index afbe2e4..17e5578 100644 --- a/LittlePaimon/plugins/Paimon_Info/abyss_statistics.py +++ b/LittlePaimon/plugins/Paimon_Info/abyss_statistics.py @@ -1,45 +1,99 @@ import datetime +from typing import Dict, Any, Tuple from collections import defaultdict from nonebot import get_bot from LittlePaimon.database.models import AbyssInfo -from LittlePaimon.utils.image import PMImage, font_manager as fm +from LittlePaimon.utils import aiorequests +from LittlePaimon.utils.files import load_image +from LittlePaimon.config import RESOURCE_BASE_PATH +from LittlePaimon.utils.image import PMImage, font_manager as fm, get_qq_avatar from LittlePaimon.utils.message import MessageBuild +async def get_user_avatar(user_id: str, size: Tuple[int, int] = (60, 60)): + img = await get_qq_avatar(user_id) + await img.resize(size) + await img.to_circle('circle') + return img + + +async def get_group_avatar(group_id: str): + img = await aiorequests.get_img(f'https://p.qlogo.cn/gh/{group_id}/{group_id}/100', size=(110, 110)) + img = PMImage(img) + await img.to_circle('circle') + return img + + + async def get_statistics(group_id: int): if not (info_list := await AbyssInfo.all()): return '本群还没有深渊战斗数据哦!' member_list = await get_bot().get_group_member_list(group_id=group_id) member_id_list = [str(member['user_id']) for member in member_list] - info_list = [info for info in info_list if info.user_id in member_id_list and info.total_battle and info.total_star and info.max_damage and info.max_take_damage] + info_list = [info for info in info_list if + info.user_id in member_id_list and info.total_battle and info.total_star and info.max_damage and info.max_take_damage] now = datetime.datetime.now() - if 1 <= now.day < 15: + if now.day <= 15: left_day = 1 right_day = 15 else: left_day = 16 right_day = 31 - info_list = [info for info in info_list if left_day <= info.update_time.day <= right_day] + info_list = [info for info in info_list if left_day <= info.update_time.day <= right_day and info.update_time.month == now.month] if not info_list: return '本群还没有深渊战斗数据哦!' + elif len(info_list) < 3: + return '本群深渊有效战斗数据不足3人,无法生成统计图!' + for info in info_list: + for member in member_list: + if info.user_id == str(member['user_id']): + info.nickname = member['card'] or member['nickname'] + break + data = { + '群号': str(group_id), + '群名称': (await get_bot().get_group_info(group_id=group_id))['group_name'], + } # 数据数 info_num = len(info_list) + data['总人数'] = info_num # 满星人数 full_star_num = len([info for info in info_list if info.total_star >= 36]) + data['满星人数'] = full_star_num # 平均星数 average_star = round(sum(info.total_star for info in info_list) / info_num, 1) + data['平均星数'] = average_star # 平均战斗次数 average_battle_num = round(sum(info.total_battle for info in info_list) / info_num, 1) + data['平均战斗次数'] = average_battle_num # 最高伤害角色 - max_damage = max(info_list, key=lambda x: x.max_damage.value) - # print(max_damage) - # return '123' - max_damage_user = [m for m in member_list if str(m['user_id']) == max_damage.user_id][0] - max_damage_user = max_damage_user['card'] or max_damage_user['nickname'] + # max_damage = max(info_list, key=lambda x: x.max_damage.value) + max_damage = sorted(info_list, key=lambda x: x.max_damage.value, reverse=True) + data['最高伤害'] = [ + { + '图标': info.max_damage.icon, + '稀有度': info.max_damage.rarity, + '等级': info.max_damage.level, + '数值': info.max_damage.value, + # '命座': (await Character.filter(user_id=info.user_id, uid=info.uid, character_id=info.max_damage.character_id).first()).constellation, + '用户名': info.nickname, + 'qq号': info.user_id, + 'uid': info.uid, + } for info in max_damage[:3] + ] # 最多承伤角色 - max_take_damage = max(info_list, key=lambda x: x.max_take_damage.value) - max_take_damage_user = [m for m in member_list if str(m['user_id']) == max_take_damage.user_id][0] - max_take_damage_user = max_take_damage_user['card'] or max_take_damage_user['nickname'] + max_take_damage = sorted(info_list, key=lambda x: x.max_take_damage.value, reverse=True) + data['最高承受伤害'] = [ + { + '图标': info.max_take_damage.icon, + '稀有度': info.max_take_damage.rarity, + '等级': info.max_take_damage.level, + '数值': info.max_take_damage.value, + # '命座': (await Character.filter(user_id=info.user_id, uid=info.uid, character_id=info.max_damage.character_id).first()).constellation, + '用户名': info.nickname, + 'qq号': info.user_id, + 'uid': info.uid, + } for info in max_take_damage[:3] + ] # 11、12层阵容 battle_characters_up11 = defaultdict(lambda: 0) battle_characters_down11 = defaultdict(lambda: 0) @@ -49,32 +103,127 @@ async def get_statistics(group_id: int): if floor11 := info.floors.get(11): for battles in floor11.battles_up: for character in battles.characters: - battle_characters_up11[character.name] += 1 + battle_characters_up11[f'{character.icon}-{character.rarity}'] += 1 for battles in floor11.battles_down: for character in battles.characters: - battle_characters_down11[character.name] += 1 + battle_characters_down11[f'{character.icon}-{character.rarity}'] += 1 if floor12 := info.floors.get(12): for battles in floor12.battles_up: for character in battles.characters: - battle_characters_up12[character.name] += 1 + battle_characters_up12[f'{character.icon}-{character.rarity}'] += 1 for battles in floor12.battles_down: for character in battles.characters: - battle_characters_down12[character.name] += 1 - up_sort11 = [character[0] for character in - sorted(battle_characters_up11.items(), key=lambda x: x[1], reverse=True)[:4]] - down_sort11 = [character[0] for character in - sorted(battle_characters_down11.items(), key=lambda x: x[1], reverse=True)[:4]] - up_sort12 = [character[0] for character in - sorted(battle_characters_up12.items(), key=lambda x: x[1], reverse=True)[:4]] - down_sort12 = [character[0] for character in - sorted(battle_characters_down12.items(), key=lambda x: x[1], reverse=True)[:4]] + battle_characters_down12[f'{character.icon}-{character.rarity}'] += 1 + data['11层上半'] = sorted(battle_characters_up11.items(), key=lambda x: x[1], reverse=True)[:4] + data['11层下半'] = sorted(battle_characters_down11.items(), key=lambda x: x[1], reverse=True)[:4] + data['12层上半'] = sorted(battle_characters_up12.items(), key=lambda x: x[1], reverse=True)[:4] + data['12层下半'] = sorted(battle_characters_down12.items(), key=lambda x: x[1], reverse=True)[:4] + return await draw_statistics_img(data) + + +async def draw_statistics_img(data: Dict[str, Any]): + img = PMImage(image=RESOURCE_BASE_PATH / 'abyss' / 'statistics_bg.png') + box = { + '4big': await load_image(RESOURCE_BASE_PATH / 'abyss' / '4bigbox.png'), + '4small': await load_image(RESOURCE_BASE_PATH / 'abyss' / '4smallbox.png'), + '5big': await load_image(RESOURCE_BASE_PATH / 'abyss' / '5bigbox.png'), + '5small': await load_image(RESOURCE_BASE_PATH / 'abyss' / '5smallbox.png'), + '4bg': await load_image(RESOURCE_BASE_PATH / 'icon' / 'star4_no_line.png', size=(95, 95)), + '5bg': await load_image(RESOURCE_BASE_PATH / 'icon' / 'star5_no_line.png', size=(95, 95)) + } + avatar = await get_group_avatar(data['群号']) + await img.paste(avatar, (38, 47)) + await img.text(data['群名称'], 162, 63, fm.get('hywh', 48), '#040404') + await img.text(data['群号'], 165, 116, fm.get('hywh', 36), '#040404') + await img.text(f'CREATED BY LITTLEPAIMON AT {datetime.datetime.now().strftime("%m-%d %H:%M")}', + 1033, 194, fm.get('bahnschrift_regular.ttf', 30), '#8c4c2e', 'right') + await img.text(str(data['总人数']), (233, 352), 296, fm.get('bahnschrift_regular.ttf', 48), '#040404', 'center') + await img.text(str(data['平均星数']), (492, 586), 296, fm.get('bahnschrift_regular.ttf', 48), '#040404', 'center') + await img.text(str(data['平均战斗次数']), (698, 840), 296, fm.get('bahnschrift_regular.ttf', 48), '#040404', 'center') + # 满星人数 + full_num = [data['满星人数'] / data['总人数'], 1 - data['满星人数'] / data['总人数']] + now_used_width = 36 + if full_num[0] > 0.03: + await img.draw_rectangle((now_used_width, 402, now_used_width + int(1007 * full_num[0]), 449), '#b6d6f2') + await img.text(str(data['满星人数']), now_used_width + 18, 413, fm.get('bahnschrift_regular.ttf', 30), '#3d6e99') + now_used_width += int(1007 * full_num[0]) + if full_num[1] > 0.03: + await img.draw_rectangle((now_used_width, 402, now_used_width + int(1007 * full_num[1]), 449), '#c8b6f2') + await img.text(str(data['总人数'] - data['满星人数']), now_used_width + 18, 413, fm.get('bahnschrift_regular.ttf', 30), '#593d99') + # 最高伤害 + await img.paste(box[f'{data["最高伤害"][0]["稀有度"]}big'], (39, 512)) + await img.paste(box[f'{data["最高伤害"][1]["稀有度"]}small'], (39, 664)) + await img.paste(box[f'{data["最高伤害"][2]["稀有度"]}small'], (296, 664)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{data["最高伤害"][0]["图标"]}.png', size=(124, 124)), + (44, 525)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{data["最高伤害"][1]["图标"]}.png', size=(82, 82)), + (45, 673)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{data["最高伤害"][2]["图标"]}.png', size=(82, 82)), + (300, 673)) + await img.paste(await get_user_avatar(data['最高伤害'][0]['qq号']), (112, 588)) + await img.paste(await get_user_avatar(data['最高伤害'][1]['qq号'], (42, 42)), (87, 713)) + await img.paste(await get_user_avatar(data['最高伤害'][2]['qq号'], (42, 42)), (344, 713)) + await img.text('最强一击', 180, 515, fm.get('hywh', 30), '#040404') + await img.text('第二名', 139, 668, fm.get('hywh', 22), '#040404') + await img.text('第三名', 397, 668, fm.get('hywh', 22), '#040404') + await img.text(str(data['最高伤害'][0]['数值']), 180, 544, fm.get('bahnschrift_regular.ttf', 72), '#040404') + await img.text(str(data['最高伤害'][1]['数值']), 137, 690, fm.get('bahnschrift_regular.ttf', 44), '#040404') + await img.text(str(data['最高伤害'][1]['数值']), 395, 690, fm.get('bahnschrift_regular.ttf', 44), '#040404') + await img.text(data['最高伤害'][0]['用户名'][:11], 180, 610, fm.get('hywh', 30), '#040404') + await img.text(data['最高伤害'][1]['用户名'][:6], 137, 726, fm.get('hywh', 22), '#040404') + await img.text(data['最高伤害'][2]['用户名'][:6], 395, 726, fm.get('hywh', 22), '#040404') + + # 最高承伤 + await img.paste(box[f'{data["最高承受伤害"][0]["稀有度"]}big'], (554, 512)) + await img.paste(box[f'{data["最高承受伤害"][1]["稀有度"]}small'], (552, 664)) + await img.paste(box[f'{data["最高承受伤害"][2]["稀有度"]}small'], (808, 664)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{data["最高承受伤害"][0]["图标"]}.png', size=(124, 124)), + (560, 525)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{data["最高承受伤害"][1]["图标"]}.png', size=(82, 82)), + (557, 673)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{data["最高承受伤害"][2]["图标"]}.png', size=(82, 82)), + (815, 673)) + await img.paste(await get_user_avatar(data['最高承受伤害'][0]['qq号']), (623, 588)) + await img.paste(await get_user_avatar(data['最高承受伤害'][1]['qq号'], (42, 42)), (600, 713)) + await img.paste(await get_user_avatar(data['最高承受伤害'][2]['qq号'], (42, 42)), (857, 713)) + await img.text('最多承伤', 693, 515, fm.get('hywh', 30), '#040404') + await img.text('第二名', 652, 668, fm.get('hywh', 22), '#040404') + await img.text('第三名', 910, 668, fm.get('hywh', 22), '#040404') + await img.text(str(data['最高承受伤害'][0]['数值']), 693, 544, fm.get('bahnschrift_regular.ttf', 72), '#040404') + await img.text(str(data['最高承受伤害'][1]['数值']), 650, 690, fm.get('bahnschrift_regular.ttf', 44), '#040404') + await img.text(str(data['最高承受伤害'][2]['数值']), 908, 690, fm.get('bahnschrift_regular.ttf', 44), '#040404') + await img.text(data['最高承受伤害'][0]['用户名'][:11], 693, 610, fm.get('hywh', 30), '#040404') + await img.text(data['最高承受伤害'][1]['用户名'][:6], 650, 726, fm.get('hywh', 22), '#040404') + await img.text(data['最高承受伤害'][2]['用户名'][:6], 908, 726, fm.get('hywh', 22), '#040404') + + tag = await load_image(RESOURCE_BASE_PATH / 'general' / 'tag.png') + # 角色出场率 + for i in range(4): + icon, rarity, count = data['11层上半'][i][0].split('-')[0], data['11层上半'][i][0].split('-')[1], data['11层上半'][i][1] + await img.paste(box[f'{rarity}bg'], (182 + i * 101, 867)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{icon}.png', size=(95, 95)), (182 + i * 101, 867)) + await img.paste(tag, (236 + i * 101, 942)) + await img.text(str(count), (236 + i * 101, 277 + i * 101), 943, fm.get('bahnschrift_regular.ttf', 19), 'white', 'center') + + icon, rarity, count = data['11层下半'][i][0].split('-')[0], data['11层下半'][i][0].split('-')[1], data['11层下半'][i][1] + await img.paste(box[f'{rarity}bg'], (642 + i * 101, 867)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{icon}.png', size=(95, 95)), (642 + i * 101, 867)) + await img.paste(tag, (696 + i * 101, 942)) + await img.text(str(count), (696 + i * 101, 737 + i * 101), 943, fm.get('bahnschrift_regular.ttf', 19), 'white', 'center') + + icon, rarity, count = data['12层上半'][i][0].split('-')[0], data['12层上半'][i][0].split('-')[1], data['12层上半'][i][1] + await img.paste(box[f'{rarity}bg'], (182 + i * 101, 983)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{icon}.png', size=(95, 95)), (182 + i * 101, 983)) + await img.paste(tag, (236 + i * 101, 1058)) + await img.text(str(count), (236 + i * 101, 278 + i * 101), 1059, fm.get('bahnschrift_regular.ttf', 19), 'white', 'center') + + icon, rarity, count = data['12层下半'][i][0].split('-')[0], data['12层下半'][i][0].split('-')[1], data['12层下半'][i][1] + await img.paste(box[f'{rarity}bg'], (642 + i * 101, 983)) + await img.paste(await load_image(RESOURCE_BASE_PATH / 'avatar' / f'{icon}.png', size=(95, 95)), (642 + i * 101, 983)) + await img.paste(tag, (696 + i * 101, 1058)) + await img.text(str(count), (696 + i * 101, 737 + i * 101), 1059, fm.get('bahnschrift_regular.ttf', 19), 'white', 'center') + i += 1 + + return MessageBuild.Image(img, mode='RGB', quality=80) - text = f'本群群友{now.strftime("%m月%d日")}深渊统计情况:\n满星人数/总人数: {full_star_num}/{info_num}\n平均星数: {average_star}\n平均战斗次数: {average_battle_num}\n最高伤害角色: {max_damage_user}的{max_damage.max_damage.name}({max_damage.max_damage.value})\n' \ - f'最高承伤角色: {max_take_damage_user}的{max_take_damage.max_take_damage.name}({max_take_damage.max_take_damage.value})\n' \ - f'11层出场率最高角色:\n 上半:{" ".join(up_sort11)}\n 下半:{" ".join(down_sort11)}\n' \ - f'12层出场率最高角色:\n 上半:{" ".join(up_sort12)}\n 下半:{" ".join(down_sort12)}\n' \ - f'Created by LittlePaimon' - img = PMImage(size=(500, 33 * 15), color=(255, 255, 255, 255)) - await img.text_box(text.replace('\n', '^'), (10, 490), (10, 33 * 15 - 10), fm.get('hywh', 25), 'black') - return MessageBuild.Image(img, mode='RGB') diff --git a/LittlePaimon/utils/brower.py b/LittlePaimon/utils/brower.py index cbec8fd..bff1e9c 100644 --- a/LittlePaimon/utils/brower.py +++ b/LittlePaimon/utils/brower.py @@ -135,6 +135,8 @@ class AsyncPlaywright: path = Path(path) try: page = await cls.goto(url, wait_until=wait_until, **kwargs) + if page is None: + return MessageSegment.text('截图失败,无法访问网页,请稍候再试') await page.set_viewport_size(viewport_size) if element: if isinstance(element, str):