From 8a9690c4cd544e4e6604325a567da45edeb2616f Mon Sep 17 00:00:00 2001 From: CMHopeSunshine <277073121@qq.com> Date: Sat, 21 May 2022 17:16:12 +0800 Subject: [PATCH] =?UTF-8?q?=E9=83=A8=E5=88=86=E9=9D=99=E6=80=81=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E5=AD=98=E6=9C=AC=E5=9C=B0=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Paimon_Chat/__init__.py | 19 ++----- Paimon_Info/draw_abyss_info.py | 54 +++---------------- Paimon_Info/draw_daily_note.py | 12 +++-- Paimon_Info/draw_month_info.py | 15 +++--- Paimon_Info/draw_player_card.py | 96 ++++++++++++--------------------- Paimon_Info/get_data.py | 12 ++--- Paimon_Wiki/__init__.py | 42 ++++----------- Paimon_Wiki/blue.py | 4 +- utils/decorator.py | 30 +++++++++++ utils/file_handler.py | 25 +++++++-- utils/message_util.py | 34 ++++++++++++ 11 files changed, 160 insertions(+), 183 deletions(-) diff --git a/Paimon_Chat/__init__.py b/Paimon_Chat/__init__.py index bc037fe..262fa97 100644 --- a/Paimon_Chat/__init__.py +++ b/Paimon_Chat/__init__.py @@ -1,7 +1,5 @@ import random -import requests -from ssl import SSLCertVerificationError from nonebot import on_regex, on_command, logger from nonebot.matcher import matchers from nonebot.rule import Rule @@ -10,6 +8,8 @@ from nonebot.exception import FinishedException from utils.config import config from utils.auth_util import FreqLimiter2 +from utils.message_util import MessageBuild +from utils.file_handler import load_json_from_url voice_url = 'https://static.cherishmoon.fun/LittlePaimon/voice/' chat_lmt = FreqLimiter2(60) @@ -28,10 +28,7 @@ async def update_paimon_voice(event: MessageEvent): try: old_len = len([m for m in matchers[10] if m.plugin_name == 'Paimon_Chat']) matchers[10] = [m for m in matchers[10] if m.plugin_name != 'Paimon_Chat'] - try: - voice_list = requests.get('https://static.cherishmoon.fun/LittlePaimon/voice/voice_list.json').json() - except SSLCertVerificationError: - voice_list = requests.get('http://static.cherishmoon.fun/LittlePaimon/voice/voice_list.json').json() + voice_list = load_json_from_url('https://static.cherishmoon.fun/LittlePaimon/voice/voice_list.json') for key, value in voice_list.items(): create_matcher(key, value['pattern'], value['cooldown'], value['pro'], value['files']) new_len = len(voice_list) - old_len @@ -61,19 +58,13 @@ def create_matcher(chat_word: str, pattern: str, cooldown: int, pro: float, resp if '.mp3' not in response: await hammer.finish(response) else: - try: - await hammer.finish(MessageSegment.record(file=voice_url + response)) - except SSLCertVerificationError: - await hammer.finish(MessageSegment.record(file=voice_url.replace('https', 'http') + response)) + await hammer.finish(await MessageBuild.StaticRecord(url=f'LittlePaimon/voice/{response}')) except FinishedException: raise except Exception as e: logger.error('派蒙发送语音失败', e) -try: - voice_list = requests.get('https://static.cherishmoon.fun/LittlePaimon/voice/voice_list.json').json() -except SSLCertVerificationError: - voice_list = requests.get('http://static.cherishmoon.fun/LittlePaimon/voice/voice_list.json').json() +voice_list = load_json_from_url('https://static.cherishmoon.fun/LittlePaimon/voice/voice_list.json') for k, v in voice_list.items(): create_matcher(k, v['pattern'], v['cooldown'], v['pro'], v['files']) diff --git a/Paimon_Info/draw_abyss_info.py b/Paimon_Info/draw_abyss_info.py index b4b5506..c35b0f4 100644 --- a/Paimon_Info/draw_abyss_info.py +++ b/Paimon_Info/draw_abyss_info.py @@ -5,6 +5,7 @@ from PIL import Image, ImageDraw, ImageFont from utils import aiorequests from utils.message_util import MessageBuild +from utils.file_handler import load_image res_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'res') @@ -33,12 +34,12 @@ def get_chan_time(timeStamp1, timeStamp2): async def draw_abyss_floor_card(floor, floor_n): - floor_img = Image.open(os.path.join(res_path, 'abyss', f'floor{floor_n}_long.png')).convert('RGBA') + floor_img = load_image(os.path.join(res_path, 'abyss', f'floor{floor_n}_long.png'), mode='RGBA') floor_draw = ImageDraw.Draw(floor_img) floor_draw.text((590, 68), f"{floor['star']}/9", font=get_font(30), fill='white') h, h1, h2 = 188.3, 230, 360 for j in floor['levels']: - star = Image.open(os.path.join(res_path, 'abyss', 'star.png')).convert('RGBA') + star = load_image(os.path.join(res_path, 'abyss', 'star.png'), mode='RGBA') star_w = 500 for i in range(0, j['star']): floor_img.alpha_composite(star, (star_w, h1 + 94)) @@ -54,8 +55,7 @@ async def draw_abyss_floor_card(floor, floor_n): for role in battles[0]['avatars']: id = role['id'] level = role['level'] - role_img = Image.open(os.path.join(res_path, 'role_card', f'{id}.png')).convert('RGBA') - role_img = role_img.resize((90, 110)) + role_img = load_image(os.path.join(res_path, 'role_card', f'{id}.png'), size=(90, 110), mode='RGBA') role_draw = ImageDraw.Draw(role_img) role_draw.text((25, 86), f'Lv.{level}', font=get_font(18), fill='black') floor_img.alpha_composite(role_img, (w, h1)) @@ -66,8 +66,7 @@ async def draw_abyss_floor_card(floor, floor_n): for role in battles[1]['avatars']: id = role['id'] level = role['level'] - role_img = Image.open(os.path.join(res_path, 'role_card', f'{id}.png')).convert('RGBA') - role_img = role_img.resize((90, 110)) + role_img = load_image(os.path.join(res_path, 'role_card', f'{id}.png'), size=(90, 110), mode='RGBA') role_draw = ImageDraw.Draw(role_img) role_draw.text((25, 86), f'Lv.{level}', font=get_font(18), fill='black') floor_img.alpha_composite(role_img, (w, h2)) @@ -95,7 +94,7 @@ async def draw_abyss_card(data, uid, floor_num): total_star += str(d['star']) + '-' total_star = total_star.strip('-') + ']' time = (get_open_time(int(data['start_time']), int(data['end_time']))) - top_img = Image.open(os.path.join(res_path, 'abyss', 'abyss_total.png')).convert('RGBA') + top_img = load_image(os.path.join(res_path, 'abyss', 'abyss_total.png'), mode='RGBA') top_draw = ImageDraw.Draw(top_img) top_draw.text((15, 22), f'UID:{uid}', font=get_font(21), fill='white') top_draw.text((510, 22), time, font=get_font(21), fill='white') @@ -106,71 +105,32 @@ async def draw_abyss_card(data, uid, floor_num): for role in data['reveal_rank']: id = role['avatar_id'] times = role['value'] - role_img = Image.open(os.path.join(res_path, 'role_card', f'{id}.png')).convert('RGBA') - role_img = role_img.resize((90, 110)) + role_img = load_image(os.path.join(res_path, 'role_card', f'{id}.png'), size=(90, 110), mode='RGBA') role_draw = ImageDraw.Draw(role_img) role_draw.text((25, 86), f'{times}次', font=get_font(18), fill='black') top_img.alpha_composite(role_img, (width, 165)) width += 150 defeat_rank = data['defeat_rank'][0] - # if not os.path.exists(os.path.join(res_path, 'role_side_card', f"{defeat_rank['avatar_id']}.png")): - # async with ClientSession() as session: - # defeat_rank_img = await (await session.get(defeat_rank['avatar_icon'])).read() - # defeat_rank_img = Image.open(BytesIO(defeat_rank_img)).convert("RGBA").resize((60, 60)) - # defeat_rank_img.save(os.path.join(res_path, 'role_side_card', f"{defeat_rank['avatar_id']}.png")) - # else: - # defeat_rank_img = Image.open(os.path.join(res_path, 'role_side_card', f"{defeat_rank['avatar_id']}.png")) defeat_rank_img = await aiorequests.get_img(url=defeat_rank['avatar_icon'], size=(60, 60), mode='RGBA') top_draw.text((160, 343), str(defeat_rank['value']), font=get_font(21), fill='white') top_img.alpha_composite(defeat_rank_img, (280, 320)) damage_rank = data['damage_rank'][0] - # if not os.path.exists(os.path.join(res_path, 'role_side_card', f"{damage_rank['avatar_id']}.png")): - # async with ClientSession() as session: - # damage_rank_img = await (await session.get(damage_rank['avatar_icon'])).read() - # damage_rank_img = Image.open(BytesIO(damage_rank_img)).convert("RGBA").resize((60, 60)) - # damage_rank_img.save(os.path.join(res_path, 'role_side_card', f"{damage_rank['avatar_id']}.png")) - # else: - # damage_rank_img = Image.open(os.path.join(res_path, 'role_side_card', f"{damage_rank['avatar_id']}.png")) damage_rank_img = await aiorequests.get_img(url=damage_rank['avatar_icon'], size=(60, 60), mode='RGBA') top_draw.text((495, 343), str(damage_rank['value']), font=get_font(21), fill='white') top_img.alpha_composite(damage_rank_img, (590, 320)) take_damage_rank = data['take_damage_rank'][0] - # if not os.path.exists(os.path.join(res_path, 'role_side_card', f"{take_damage_rank['avatar_id']}.png")): - # async with ClientSession() as session: - # take_damage_rank_img = await (await session.get(take_damage_rank['avatar_icon'])).read() - # take_damage_rank_img = Image.open(BytesIO(take_damage_rank_img)).convert("RGBA").resize((60, 60)) - # take_damage_rank_img.save(os.path.join(res_path, 'role_side_card', f"{take_damage_rank['avatar_id']}.png")) - # else: - # take_damage_rank_img = Image.open( - # os.path.join(res_path, 'role_side_card', f"{take_damage_rank['avatar_id']}.png")) take_damage_rank_img = await aiorequests.get_img(url=take_damage_rank['avatar_icon'], size=(60, 60), mode='RGBA') top_draw.text((180, 389), str(take_damage_rank['value']), font=get_font(21), fill='white') top_img.alpha_composite(take_damage_rank_img, (280, 365)) energy_skill_rank = data['energy_skill_rank'][0] - # if not os.path.exists(os.path.join(res_path, 'role_side_card', f"{energy_skill_rank['avatar_id']}.png")): - # async with ClientSession() as session: - # energy_skill_rank_img = await (await session.get(energy_skill_rank['avatar_icon'])).read() - # energy_skill_rank_img = Image.open(BytesIO(energy_skill_rank_img)).convert("RGBA").resize((60, 60)) - # energy_skill_rank_img.save(os.path.join(res_path, 'role_side_card', f"{energy_skill_rank['avatar_id']}.png")) - # else: - # energy_skill_rank_img = Image.open( - # os.path.join(res_path, 'role_side_card', f"{energy_skill_rank['avatar_id']}.png")) energy_skill_rank_img = await aiorequests.get_img(url=energy_skill_rank['avatar_icon'], size=(60, 60), mode='RGBA') top_draw.text((530, 389), str(energy_skill_rank['value']), font=get_font(21), fill='white') top_img.alpha_composite(energy_skill_rank_img, (590, 365)) normal_skill_rank = data['normal_skill_rank'][0] - # if not os.path.exists(os.path.join(res_path, 'role_side_card', f"{normal_skill_rank['avatar_id']}.png")): - # async with ClientSession() as session: - # normal_skill_rank_img = await (await session.get(normal_skill_rank['avatar_icon'])).read() - # normal_skill_rank_img = Image.open(BytesIO(normal_skill_rank_img)).convert("RGBA").resize((60, 60)) - # normal_skill_rank_img.save(os.path.join(res_path, 'role_side_card', f"{normal_skill_rank['avatar_id']}.png")) - # else: - # normal_skill_rank_img = Image.open( - # os.path.join(res_path, 'role_side_card', f"{normal_skill_rank['avatar_id']}.png")) normal_skill_rank_img = await aiorequests.get_img(url=normal_skill_rank['avatar_icon'], size=(60, 60), mode='RGBA') top_draw.text((195, 435), str(normal_skill_rank['value']), font=get_font(21), fill='white') top_img.alpha_composite(normal_skill_rank_img, (280, 410)) diff --git a/Paimon_Info/draw_daily_note.py b/Paimon_Info/draw_daily_note.py index 148de61..ca9d9bd 100644 --- a/Paimon_Info/draw_daily_note.py +++ b/Paimon_Info/draw_daily_note.py @@ -7,6 +7,7 @@ from PIL import Image, ImageDraw, ImageFont from utils import aiorequests from utils.message_util import MessageBuild +from utils.file_handler import load_image res_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'res') @@ -40,9 +41,10 @@ async def draw_daily_note_card(data, uid): elif data['retcode'] != 0: return f'派蒙获取数据失败了,获取状态:\n{data["message"]},{data["retcode"]}' data = data['data'] - circle_img = Image.open(os.path.join(res_path, 'daily_note', '透明圆.png')) - finished_icon = Image.open(os.path.join(res_path, 'daily_note', 'finished.png')) - bg_img = Image.open(os.path.join(res_path, 'daily_note', 'ssbq.png')).convert("RGBA") + circle_img = load_image(os.path.join(res_path, 'daily_note', '透明圆.png')) + finished_icon = load_image(os.path.join(res_path, 'daily_note', 'finished.png')) + bg_img = load_image(os.path.join(res_path, 'daily_note', 'ssbq.png'), mode='RGBA') + bg_draw = ImageDraw.Draw(bg_img) # uid文字 bg_draw.text((152, 251), f"uid{uid}", fill='#5680d2', font=get_font(60, 'number.ttf')) @@ -143,8 +145,8 @@ async def draw_daily_note_card(data, uid): bg_draw.text((1408, 1588), last_finish_str, fill="#5680d2", font=get_font(60, '优设标题黑.ttf')) - role_img = Image.open(os.path.join(res_path, 'emoticons', random.choice(os.listdir(os.path.join(res_path, 'emoticons'))))).convert('RGBA') - role_img = role_img.resize((int(role_img.size[0] * 3.5), int(role_img.size[1] * 3.5)), Image.ANTIALIAS) + + role_img = load_image(os.path.join(res_path, 'emoticons', random.choice(os.listdir(os.path.join(res_path, 'emoticons')))), size=3.5, mode='RGBA') bg_img.alpha_composite(role_img, (1220, 200)) now = datetime.datetime.now().strftime('%m月%d日%H:%M') bg_draw.text((554, 1794), 'Created by LittlePaimon·' + now, fill='#5680d2', font=get_font(40, '优设标题黑.ttf')) diff --git a/Paimon_Info/draw_month_info.py b/Paimon_Info/draw_month_info.py index f7a6b8b..a9c73a3 100644 --- a/Paimon_Info/draw_month_info.py +++ b/Paimon_Info/draw_month_info.py @@ -4,6 +4,7 @@ import random import matplotlib.pyplot as plt from PIL import Image, ImageDraw, ImageFont from utils.message_util import MessageBuild +from utils.file_handler import load_image res_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'res') @@ -17,8 +18,8 @@ def get_font_bd(size): async def get_box(t, num): - box = Image.open(os.path.join(res_path, 'monthinfo', 'box.png')).convert('RGBA') - img = Image.open(os.path.join(res_path, 'monthinfo', f'{t}.png')).convert('RGBA') + box = load_image(os.path.join(res_path, 'monthinfo', 'box.png'), mode='RGBA') + img = load_image(os.path.join(res_path, 'monthinfo', f'{t}.png'), mode='RGBA') box.alpha_composite(img, (11, 11)) box_draw = ImageDraw.Draw(box) box_draw.text((83, 18), f'{t}:', font=get_font(25), fill='black') @@ -56,9 +57,9 @@ async def draw_monthinfo_card(data): elif data['retcode'] != 0: return f'派蒙获取数据失败了,获取状态:\n{data["message"]},{data["retcode"]}' data = data['data'] - bg_img = Image.open(os.path.join(res_path, 'monthinfo', 'bg.png')).convert('RGBA') + bg_img = load_image(os.path.join(res_path, 'monthinfo', 'bg.png'), mode='RGBA') bg_draw = ImageDraw.Draw(bg_img) - line = Image.open(os.path.join(res_path, 'monthinfo', 'line.png')).convert('RGBA') + line = load_image(os.path.join(res_path, 'monthinfo', 'line.png'), mode='RGBA') # 顶标题 bg_draw.text((60, 42), f'旅行者{data["data_month"]}月札记', font=get_font_bd(30), fill='#27384C') bg_draw.text((300, 52), f'{data["nickname"]} {data["uid"]}', font=get_font(21), fill='#27384C') @@ -73,15 +74,15 @@ async def draw_monthinfo_card(data): bg_img.alpha_composite(await get_box('摩拉', data['day_data']['current_mora']), (40, 388)) # 表情 - emoticon1 = Image.open(os.path.join(res_path, 'emoticons', random.choice(os.listdir(os.path.join(res_path, 'emoticons'))))).convert('RGBA') + emoticon1 = load_image(os.path.join(res_path, 'emoticons', random.choice(os.listdir(os.path.join(res_path, 'emoticons')))), mode='RGBA') bg_img.alpha_composite(emoticon1, (360, 140)) - emoticon2 = Image.open(os.path.join(res_path, 'emoticons', random.choice(os.listdir(os.path.join(res_path, 'emoticons'))))).convert('RGBA') + emoticon2 = load_image(os.path.join(res_path, 'emoticons', random.choice(os.listdir(os.path.join(res_path, 'emoticons')))), mode='RGBA') bg_img.alpha_composite(emoticon2, (360, 317)) bg_img.alpha_composite(line, (64, 480)) # 圆环比例图 bg_draw.text((60, 495), '原石收入组成:', font=get_font_bd(25), fill='#27384C') - circle = Image.open(os.path.join(res_path, 'monthinfo', 'circle.png')).convert('RGBA') + circle = load_image(os.path.join(res_path, 'monthinfo', 'circle.png'), mode='RGBA') bg_img.alpha_composite(circle, (50, 550)) color = {'每日活跃': '#BD9A5A', '深境螺旋': '#739970', '活动奖励': '#5A7EA0', '邮件奖励': '#7A6CA7', '冒险奖励': '#D56564', diff --git a/Paimon_Info/draw_player_card.py b/Paimon_Info/draw_player_card.py index e72dd3e..37017e1 100644 --- a/Paimon_Info/draw_player_card.py +++ b/Paimon_Info/draw_player_card.py @@ -7,6 +7,7 @@ from PIL import Image, ImageDraw, ImageFont from utils import aiorequests from utils.message_util import MessageBuild +from utils.file_handler import load_image res_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'res') @@ -28,30 +29,23 @@ def get_expl_per(percentage): async def get_chara_card(data): chara_card = Image.new("RGBA", (226, 313), (255, 255, 255, 255)) - chara_img = Image.open(os.path.join(res_path, 'role_card', f'{data["id"]}.png')) + chara_img = load_image(os.path.join(res_path, 'role_card', f'{data["id"]}.png')) chara_card.alpha_composite(chara_img, (0, 0)) - b = Image.open(os.path.join(res_path, 'player_card', 'chara_botton.png')) + b = load_image(os.path.join(res_path, 'player_card', 'chara_botton.png')) chara_card.alpha_composite(b, (0, 236)) # 命座 if data['name'] != '埃洛伊': - actived_constellation_num = Image.open( + actived_constellation_num = load_image( os.path.join(res_path, 'player_card', f'命之座{data["actived_constellation_num"]}.png')) chara_card.alpha_composite(actived_constellation_num, (155, 0)) # 好感度 if data['name'] != '旅行者': - fetter = Image.open(os.path.join(res_path, 'player_card', f'好感度{data["fetter"]}.png')) + fetter = load_image(os.path.join(res_path, 'player_card', f'好感度{data["fetter"]}.png')) chara_card.alpha_composite(fetter, (155, 166)) # 武器背景 - weapon_bg = Image.open(os.path.join(res_path, 'player_card', f'{data["weapon"]["rarity"]}星武器.png')) + weapon_bg = load_image(os.path.join(res_path, 'player_card', f'{data["weapon"]["rarity"]}星武器.png')) chara_card.alpha_composite(weapon_bg, (0, 227)) # 武器图标 - # weapon_name = data['weapon']['icon'].split('/')[-1] - # if not os.path.exists(os.path.join(res_path, 'weapon', weapon_name)): - # async with ClientSession() as session: - # weapon_icon = await (await session.get(data['weapon']['icon'])).read() - # weapon_icon = Image.open(BytesIO(weapon_icon)).convert("RGBA") - # weapon_icon.save(os.path.join(res_path, 'weapon', weapon_name)) - # weapon_icon = Image.open(os.path.join(res_path, 'weapon', weapon_name)).resize((63, 63)) weapon_icon = await aiorequests.get_img(url=data['weapon']['icon'], size=(63, 63), mode='RGBA') chara_card.alpha_composite(weapon_icon, (0, 230)) # 等级信息 @@ -198,16 +192,15 @@ async def draw_player_card(data, chara_data, uid, nickname="旅行者"): elif data['retcode'] != 0: return f'派蒙获取数据失败了,获取状态:\n{data["message"]},{data["retcode"]}' data = data['data'] - bg_img = Image.open(os.path.join(res_path, 'player_card', '背景.png')).convert('RGBA') + bg_img = load_image(os.path.join(res_path, 'player_card', '背景.png'), mode='RGBA') bg_draw = ImageDraw.Draw(bg_img) # 头部名片 name_id = random.choice(data['avatars'][0:8])['id'] - name_card = Image.open(os.path.join(res_path, 'name_card', f'{name_id}.png')).crop((0, 40, 840, 360)).resize( - (846, 322)) - avatar = Image.open(os.path.join(res_path, 'role_profile', f'{name_id}.png')).resize((240, 240)) + name_card = load_image(os.path.join(res_path, 'name_card', f'{name_id}.png'), crop=(0, 40, 840, 360), size=(846, 322)) + avatar = load_image(os.path.join(res_path, 'role_profile', f'{name_id}.png'), size=(240, 240)) bg_img.alpha_composite(name_card, (57, 27)) bg_img.alpha_composite(avatar, (360, 25)) - uid_bg = Image.open(os.path.join(res_path, 'player_card', 'UID_bg.png')).resize((280, 100)) + uid_bg = load_image(os.path.join(res_path, 'player_card', 'UID_bg.png'), size=(280, 100)) bg_img.alpha_composite(uid_bg, (340, 247)) bg_draw.text((354, 259), f'昵称 {nickname}', font=get_font(30), fill='white') bg_draw.text((354, 291), f'UID {uid}', font=get_font(30), fill='white') @@ -215,14 +208,14 @@ async def draw_player_card(data, chara_data, uid, nickname="旅行者"): stats = data['stats'] await draw_stats_data(bg_draw, stats) # 尘歌壶 - h_lock = Image.open(os.path.join(res_path, 'player_card', '未解锁.png')) + h_lock = load_image(os.path.join(res_path, 'player_card', '未解锁.png')) homes_list = {'罗浮洞': {'unlock': False, 'posi': (79, 852)}, '清琼岛': {'unlock': False, 'posi': (79, 1000)}, '翠黛峰': {'unlock': False, 'posi': (489, 852)}, '绘绮庭': {'unlock': False, 'posi': (489, 1000)}} if data['homes']: for hl in homes_list.items(): for h in data['homes']: if hl[0] in h.values(): - h_img = Image.open(os.path.join(res_path, 'player_card', f'{hl[0]}.png')) + h_img = load_image(os.path.join(res_path, 'player_card', f'{hl[0]}.png')) bg_img.alpha_composite(h_img, hl[1]['posi']) homes_list[hl[0]]['unlock'] = True for hl in homes_list.items(): @@ -257,30 +250,22 @@ async def draw_player_card(data, chara_data, uid, nickname="旅行者"): # ysa async def get_chara_card_long(data): chara_card = Image.new("RGBA", (226, 382), (255, 255, 255, 255)) - chara_img = Image.open(os.path.join(res_path, 'role_card', f'{data["id"]}.png')) + chara_img = load_image(os.path.join(res_path, 'role_card', f'{data["id"]}.png')) chara_card.alpha_composite(chara_img, (0, 0)) - b = Image.open(os.path.join(res_path, 'player_card', '角色卡底部.png')) + b = load_image(os.path.join(res_path, 'player_card', '角色卡底部.png')) chara_card.alpha_composite(b, (0, 282)) # 命座 if data['name'] != '埃洛伊': - actived_constellation_num = Image.open( + actived_constellation_num = load_image( os.path.join(res_path, 'player_card', f'命之座{data["actived_constellation_num"]}.png')) chara_card.alpha_composite(actived_constellation_num, (155, 0)) # 好感度 if data['name'] != '旅行者': - fetter = Image.open(os.path.join(res_path, 'player_card', f'好感度{data["fetter"]}.png')) + fetter = load_image(os.path.join(res_path, 'player_card', f'好感度{data["fetter"]}.png')) chara_card.alpha_composite(fetter, (155, 166)) # 武器背景 - weapon_bg = Image.open(os.path.join(res_path, 'player_card', f'{data["weapon"]["rarity"]}星武器.png')) + weapon_bg = load_image(os.path.join(res_path, 'player_card', f'{data["weapon"]["rarity"]}星武器.png')) chara_card.alpha_composite(weapon_bg, (3, 288)) - # 武器图标 - # weapon_name = data['weapon']['icon'].split('/')[-1] - # if not os.path.exists(os.path.join(res_path, 'weapon', weapon_name)): - # async with ClientSession() as session: - # weapon_icon = await (await session.get(data['weapon']['icon'])).read() - # weapon_icon = Image.open(BytesIO(weapon_icon)).convert("RGBA") - # weapon_icon.save(os.path.join(res_path, 'weapon', weapon_name)) - # weapon_icon = Image.open(os.path.join(res_path, 'weapon', weapon_name)).resize((62, 62)) weapon_icon = await aiorequests.get_img(url=data['weapon']['icon'], size=(62, 62), mode='RGBA') chara_card.alpha_composite(weapon_icon, (3, 291)) # 等级信息 @@ -317,14 +302,14 @@ async def draw_all_chara_card(data, uid): chara['rarity'] = 4.5 chara_list = sorted(data, key=lambda i: ( i['rarity'], i['actived_constellation_num'], i['level'], i['fetter'], i['weapon']['rarity']), reverse=True) - bg_top = Image.open(os.path.join(res_path, 'player_card', '卡片顶部.png')) - avatar = Image.open(os.path.join(res_path, 'role_profile', f'{chara_list[0]["id"]}.png')).resize((220, 220)) + bg_top = load_image(os.path.join(res_path, 'player_card', '卡片顶部.png')) + avatar = load_image(os.path.join(res_path, 'role_profile', f'{chara_list[0]["id"]}.png'), size=(220, 220)) bg_top.alpha_composite(avatar, (542, 30)) draw = ImageDraw.Draw(bg_top) draw.text((538, 235), f'UID {uid}', font=get_font(30), fill='black') - bg_middle = Image.open(os.path.join(res_path, 'player_card', '卡片身体.png')).resize((1304, 474)) - bg_bottom = Image.open(os.path.join(res_path, 'player_card', '卡片底部.png')) + bg_middle = load_image(os.path.join(res_path, 'player_card', '卡片身体.png'), size=(1304, 474)) + bg_bottom = load_image(os.path.join(res_path, 'player_card', '卡片底部.png')) bg_img = Image.new('RGBA', (1304, 382 + col * 424 + (col - 1) * 50 + 87), (0, 0, 0, 0)) bg_img.paste(bg_top, (0, 0)) for i in range(0, col): @@ -340,18 +325,11 @@ async def draw_all_chara_card(data, uid): # ysc -shadow = Image.open(os.path.join(res_path, 'other', 'shadow.png')) +shadow = load_image(os.path.join(res_path, 'other', 'shadow.png')) async def draw_reli_icon(data): - base_icon = Image.open(os.path.join(res_path, 'other', f'star{data["rarity"]}.png')).resize((80, 80)) - # if not os.path.exists(os.path.join(res_path, 'reliquaries', f'{data["id"]}.png')): - # async with ClientSession() as session: - # icon = await (await session.get(data["icon"])).read() - # icon = Image.open(BytesIO(icon)).convert("RGBA").resize((80, 80)) - # icon.save(os.path.join(res_path, 'reliquaries', f'{data["id"]}.png')) - # else: - # icon = Image.open(os.path.join(res_path, 'reliquaries', f'{data["id"]}.png')).resize((80, 80)) + base_icon = load_image(os.path.join(res_path, 'other', f'star{data["rarity"]}.png'), size=(80, 80)) icon = await aiorequests.get_img(url=data["icon"], size=(80, 80), mode='RGBA') base_icon.alpha_composite(icon, (0, 0)) base_icon.alpha_composite(shadow, (40, 60)) @@ -361,18 +339,11 @@ async def draw_reli_icon(data): async def draw_const_skill_icon(data, name): - base_icon = Image.open(os.path.join(res_path, 'other', '命座.png')).resize((65, 65)) - # if not os.path.exists(os.path.join(res_path, 'skill_constellations', f'{name}{data["id"]}.png')): - # async with ClientSession() as session: - # icon = await (await session.get(data["icon"])).read() - # icon = Image.open(BytesIO(icon)).convert("RGBA").resize((65, 65)) - # icon.save(os.path.join(res_path, 'reliquaries', f'{name}{data["id"]}.png')) - # else: - # icon = Image.open(os.path.join(res_path, 'reliquaries', f'{name}{data["id"]}.png')).resize((65, 65)) + base_icon = load_image(os.path.join(res_path, 'other', '命座.png'), size=(65, 65)) icon = await aiorequests.get_img(url=data["icon"], size=(65, 65), mode='RGBA') base_icon.alpha_composite(icon, (0, 0)) if 'is_actived' in data and not data['is_actived']: - unlock_icon = Image.open(os.path.join(res_path, 'other', '命座未解锁.png')).resize((65, 65)) + unlock_icon = load_image(os.path.join(res_path, 'other', '命座未解锁.png'), size=(65, 65)) base_icon.alpha_composite(unlock_icon, (0, 0)) return base_icon @@ -414,14 +385,14 @@ async def draw_chara_card(data, skill_data, chara_name, uid): if not f: return f'{chara_name[1][0]}不在你公开的8个角色中或你没有这个角色哦' # 立绘 - bg_img = Image.open(os.path.join(res_path, 'name_card', f'{character["id"]}.png')) + bg_img = load_image(os.path.join(res_path, 'name_card', f'{character["id"]}.png')) bg_draw = ImageDraw.Draw(bg_img) if character['id'] == 10000007: - chara_img = Image.open(os.path.join(res_path, 'role_splash', '荧.png')).convert('RGBA') + chara_img = load_image(os.path.join(res_path, 'role_splash', '荧.png'), mode='RGBA') elif character['id'] == 10000005: - chara_img = Image.open(os.path.join(res_path, 'role_splash', '空.png')).convert('RGBA') + chara_img = load_image(os.path.join(res_path, 'role_splash', '空.png'), mode='RGBA') else: - chara_img = Image.open(os.path.join(res_path, 'role_splash', f'{character["name"]}.png')).convert('RGBA') + chara_img = load_image(os.path.join(res_path, 'role_splash', f'{character["name"]}.png'), mode='RGBA') W, H = chara_img.size chara_img = chara_img.resize((int(W * 400 / H), 400)) bg_img.alpha_composite(chara_img, (0, 0)) @@ -436,19 +407,18 @@ async def draw_chara_card(data, skill_data, chara_name, uid): bg_img.alpha_composite(await draw_line(character['level'] / 90), (330, 92)) bg_img.alpha_composite(await draw_line(character['fetter'] / 10), (330, 137)) bg_draw.text((560, 83), f'Lv.{character["level"]}', font=get_font(25), fill='white') - fetter = Image.open(os.path.join(res_path, 'player_card', f'好感度{character["fetter"]}.png')).resize((57, 49)) + fetter = load_image(os.path.join(res_path, 'player_card', f'好感度{character["fetter"]}.png'), size=(57, 49)) bg_img.alpha_composite(fetter, (560, 122)) # 武器 - weapon_bg = Image.open(os.path.join(res_path, 'other', f'star{character["weapon"]["rarity"]}.png')).resize( - (100, 100)) + weapon_bg = load_image(os.path.join(res_path, 'other', f'star{character["weapon"]["rarity"]}.png'), size=(100, 100)) weapon_icon = await aiorequests.get_img(url=character['weapon']['icon'], size=(100, 100), mode='RGBA') bg_img.alpha_composite(weapon_bg, (293, 175)) bg_img.alpha_composite(weapon_icon, (293, 175)) bg_img.alpha_composite(shadow.resize((50, 25)), (344, 250)) bg_draw.text((348, 250), f'Lv.{character["weapon"]["level"]}', font=get_font(18), fill='white') - weapon_star = Image.open( - os.path.join(res_path, 'player_card', f'命之座{character["weapon"]["affix_level"]}.png')).resize((40, 40)) + weapon_star = load_image( + os.path.join(res_path, 'player_card', f'命之座{character["weapon"]["affix_level"]}.png'), size=(40, 40)) bg_img.alpha_composite(weapon_star, (353, 175)) # 圣遗物 diff --git a/Paimon_Info/get_data.py b/Paimon_Info/get_data.py index 1363289..43cc147 100644 --- a/Paimon_Info/get_data.py +++ b/Paimon_Info/get_data.py @@ -1,12 +1,12 @@ from utils.auth_util import get_headers, get_sign_headers, get_use_cookie, get_own_cookie, check_retcode from utils.db_util import update_cookie_cache -from utils.decorator import cache +from utils.decorator import AsyncCache from utils import aiorequests import datetime import re -@cache(ttl=datetime.timedelta(hours=1)) +@AsyncCache(ttl=datetime.timedelta(hours=1)) async def get_abyss_data(user_id, uid, schedule_type="1", use_cache=True): server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" url = "https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/spiralAbyss" @@ -50,7 +50,7 @@ async def get_daily_note_data(uid): return f'你的uid{uid}的cookie已过期,需要重新绑定哦!' -@cache(ttl=datetime.timedelta(hours=1)) +@AsyncCache(ttl=datetime.timedelta(hours=1)) async def get_player_card_data(user_id, uid, use_cache=True): server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" url = "https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/index" @@ -72,7 +72,7 @@ async def get_player_card_data(user_id, uid, use_cache=True): return data -@cache(ttl=datetime.timedelta(hours=1)) +@AsyncCache(ttl=datetime.timedelta(hours=1)) async def get_chara_detail_data(user_id, uid, use_cache=True): server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" json_data = { @@ -95,7 +95,7 @@ async def get_chara_detail_data(user_id, uid, use_cache=True): return data -@cache(ttl=datetime.timedelta(hours=1)) +@AsyncCache(ttl=datetime.timedelta(hours=1)) async def get_chara_skill_data(uid, chara_id, use_cache=True): server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" url = 'https://api-takumi.mihoyo.com/event/e20200928calculate/v1/sync/avatar/detail' @@ -114,7 +114,7 @@ async def get_chara_skill_data(uid, chara_id, use_cache=True): return data -@cache(ttl=datetime.timedelta(hours=1)) +@AsyncCache(ttl=datetime.timedelta(hours=1)) async def get_monthinfo_data(uid, month, use_cache=True): server_id = "cn_qd01" if uid[0] == '5' else "cn_gf01" url = 'https://hk4e-api.mihoyo.com/event/ys_ledger/monthInfo' diff --git a/Paimon_Wiki/__init__.py b/Paimon_Wiki/__init__.py index c20053f..9a54d2c 100644 --- a/Paimon_Wiki/__init__.py +++ b/Paimon_Wiki/__init__.py @@ -2,7 +2,6 @@ import os import re import time -from ssl import SSLCertVerificationError from nonebot import on_endswith, on_command, on_regex from nonebot.adapters.onebot.v11 import MessageSegment, MessageEvent @@ -10,6 +9,7 @@ from nonebot.params import RegexDict from utils.character_alias import get_id_by_alias from utils.decorator import exception_handler +from utils.message_util import MessageBuild from .abyss_rate_draw import draw_rate_rank, draw_teams_rate from .blue import get_blue_pic @@ -45,12 +45,7 @@ async def genshin_guide(event: MessageEvent): realname = get_id_by_alias(name) if name in ['风主', '岩主', '雷主'] or realname: name = realname[1][0] if name not in ['风主', '岩主', '雷主'] else name - try: - await guide.finish( - MessageSegment.image(file=f'https://static.cherishmoon.fun/LittlePaimon/XFGuide/{name}.jpg')) - except SSLCertVerificationError: - await guide.finish( - MessageSegment.image(file=f'http://static.cherishmoon.fun/LittlePaimon/XFGuide/{name}.jpg')) + await guide.finish(await MessageBuild.StaticImage(url=f'LittlePaimon/XFGuide/{name}.jpg')) else: await guide.finish(f'没有找到{name}的攻略', at_sender=True) @@ -62,12 +57,7 @@ async def genshin_material(event: MessageEvent): realname = get_id_by_alias(name) if name in ['夜兰', '久岐忍'] or realname: name = realname[1][0] if realname else name - try: - await material.finish( - MessageSegment.image(file=f'https://static.cherishmoon.fun/LittlePaimon/RoleMaterials/{name}材料.jpg')) - except SSLCertVerificationError: - await material.finish( - MessageSegment.image(file=f'http://static.cherishmoon.fun/LittlePaimon/RoleMaterials/{name}材料.jpg')) + await material.finish(await MessageBuild.StaticImage(f'LittlePaimon/RoleMaterials/{name}材料.jpg')) else: await material.finish(f'没有找到{name}的材料', at_sender=True) @@ -92,13 +82,7 @@ async def genshinAttribute2(event: MessageEvent): realname = get_id_by_alias(name) if name in ['风主', '岩主', '雷主'] or realname: name = realname[1][0] if name not in ['风主', '岩主', '雷主'] else name - try: - await attribute2.finish( - MessageSegment.image(file=f'https://static.cherishmoon.fun/LittlePaimon/blue/{name}.jpg')) - except SSLCertVerificationError: - await attribute2.finish( - MessageSegment.image(file=f'http://static.cherishmoon.fun/LittlePaimon/blue/{name}.jpg')) - + await attribute2.finish(await MessageBuild.StaticImage(url=f'LittlePaimon/blue/{name}.jpg')) else: await attribute2.finish(f'没有找到{name}的收益曲线', at_sender=True) @@ -127,15 +111,12 @@ async def daily_material_handle(event: MessageEvent): if week == "0": await daily_material.finish('周日所有材料都可以刷哦!', at_sender=True) elif week in ['1', '4']: - url = 'https://static.cherishmoon.fun/LittlePaimon/DailyMaterials/周一周四.jpg' + url = 'LittlePaimon/DailyMaterials/周一周四.jpg' elif week in ['2', '5']: - url = 'https://static.cherishmoon.fun/LittlePaimon/DailyMaterials/周二周五.jpg' + url = 'LittlePaimon/DailyMaterials/周二周五.jpg' else: - url = 'https://static.cherishmoon.fun/LittlePaimon/DailyMaterials/周三周六.jpg' - try: - await daily_material.finish(MessageSegment.image(file=url)) - except SSLCertVerificationError: - await daily_material.finish(MessageSegment.image(file=url.replace('https', 'http'))) + url = 'LittlePaimon/DailyMaterials/周三周六.jpg' + await daily_material.finish(await MessageBuild.StaticImage(url=url)) @abyss_rate.handle() @@ -156,9 +137,4 @@ async def abyss_team_handler(event: MessageEvent, reGroup=RegexDict()): @exception_handler() async def weapon_guide_handler(event: MessageEvent): name: str = event.message.extract_plain_text().replace('武器攻略', '').strip() - try: - await weapon_guide.finish( - MessageSegment.image(file=f'https://static.cherishmoon.fun/LittlePaimon/WeaponGuild/{name}.png')) - except SSLCertVerificationError: - await weapon_guide.finish( - MessageSegment.image(file=f'http://static.cherishmoon.fun/LittlePaimon/WeaponGuild/{name}.png')) \ No newline at end of file + await weapon_guide.finish(await MessageBuild.StaticImage(url=f'LittlePaimon/WeaponGuild/{name}.png')) \ No newline at end of file diff --git a/Paimon_Wiki/blue.py b/Paimon_Wiki/blue.py index 75d4d3f..f3e094d 100644 --- a/Paimon_Wiki/blue.py +++ b/Paimon_Wiki/blue.py @@ -1,4 +1,3 @@ -from utils import aiorequests from utils.message_util import MessageBuild blue = { @@ -60,6 +59,5 @@ blue = { async def get_blue_pic(name): for c in blue.items(): if c[0] == name: - img = await aiorequests.get_img(url=f'https://static.cherishmoon.fun/LittlePaimon/blue/{c[1][0]}.jpg', crop=(0, int(c[1][1][0]), 1080, int(c[1][1][1]))) - return MessageBuild.Image(img) + return await MessageBuild.StaticImage(url=f'LittlePaimon/blue/{c[1][0]}.jpg', crop=(0, int(c[1][1][0]), 1080, int(c[1][1][1]))) return None diff --git a/utils/decorator.py b/utils/decorator.py index 77853ab..6857a3e 100644 --- a/utils/decorator.py +++ b/utils/decorator.py @@ -30,6 +30,36 @@ def auto_withdraw(seconds: int = -1): # 缓存装饰器 ttl为过期时间 参数use_cache决定是否使用缓存,默认为True def cache(ttl=datetime.timedelta(hours=1)): + def wrap(func): + cache_data = {} + + @functools.wraps(func) + def wrapped(*args, **kw): + nonlocal cache_data + bound = inspect.signature(func).bind(*args, **kw) + bound.apply_defaults() + ins_key = '|'.join(['%s_%s' % (k, v) for k, v in bound.arguments.items()]) + default_data = {"time": None, "value": None} + data = cache_data.get(ins_key, default_data) + now = datetime.datetime.now() + if 'use_cache' not in kw: + kw['use_cache'] = True + if not kw['use_cache'] or not data['time'] or now - data['time'] > ttl: + try: + data['value'] = func(*args, **kw) + data['time'] = now + cache_data[ins_key] = data + except Exception as e: + raise e + return data['value'] + + return wrapped + + return wrap + + +# 缓存装饰异步版 +def AsyncCache(ttl=datetime.timedelta(hours=1)): def wrap(func): cache_data = {} diff --git a/utils/file_handler.py b/utils/file_handler.py index 38ac89d..0580d6b 100644 --- a/utils/file_handler.py +++ b/utils/file_handler.py @@ -1,15 +1,21 @@ -from pathlib import Path -from PIL import Image -from typing import Union, Optional, Tuple import json +import requests +from pathlib import Path +from typing import Union, Optional, Tuple +from ssl import SSLCertVerificationError +from PIL import Image + +from .decorator import cache +@cache() def load_image( path: Union[Path, str], *, size: Optional[Union[Tuple[int, int], float]] = None, crop: Optional[Tuple[int, int, int, int]] = None, - mode: Optional[str] = 'RGB' + mode: Optional[str] = None, + use_cache: bool = True ): img = Image.open(path) if size: @@ -19,7 +25,8 @@ def load_image( img = img.resize(size, Image.ANTIALIAS) if crop: img = img.crop(crop) - img = img.convert(mode) + if mode: + img = img.convert(mode) return img @@ -34,6 +41,14 @@ def load_json(file: str = None, path: Union[Path, str] = None, encoding: str = ' return json.load(path.open('r', encoding=encoding)) +def load_json_from_url(url: str): + try: + resp = requests.get(url) + except SSLCertVerificationError: + resp = requests.get(url.replace('https', 'http')) + return resp.json() + + def save_json(data, file: str = None, path: Union[Path, str] = None, encoding: str = 'utf-8'): if file and not path: path = Path() / 'data' / 'LittlePaimon' / file diff --git a/utils/message_util.py b/utils/message_util.py index 5abccbf..a71b353 100644 --- a/utils/message_util.py +++ b/utils/message_util.py @@ -33,6 +33,30 @@ class MessageBuild: img_b64 = 'base64://' + base64.b64encode(bio.getvalue()).decode() return MessageSegment.image(img_b64) + @classmethod + async def StaticImage(cls, + url: str, + size: Optional[Tuple[int, int]] = None, + crop: Optional[Tuple[int, int, int, int]] = None, + quality: Optional[int] = 100, + mode: Optional[str] = 'RGB' + ): + path = Path() / 'data' / url + if not path.exists(): + path.parent.mkdir(parents=True, exist_ok=True) + img = await aiorequests.get_img(url='https://static.cherishmoon.fun/' + url, save_path=path) + else: + img = Image.open(path) + if size: + img = img.resize(size) + if crop: + img = img.crop(crop) + bio = BytesIO() + img = img.convert(mode) + img.save(bio, format='JPEG' if mode == 'RGB' else 'PNG', quality=quality) + img_b64 = 'base64://' + base64.b64encode(bio.getvalue()).decode() + return MessageSegment.image(img_b64) + @classmethod def Text(cls, text: str) -> MessageSegment: # TODO 过滤负面文本 @@ -43,6 +67,16 @@ class MessageBuild: # TODO 网络语音 return MessageSegment.record(path) + @classmethod + async def StaticRecord(cls, url: str) -> MessageSegment: + path = Path() / 'data' / url + if not path.exists(): + path.parent.mkdir(parents=True, exist_ok=True) + resp = await aiorequests.get(url='https://static.cherishmoon.fun/' + url) + content = resp.content + path.write_bytes(content) + return MessageSegment.record(file=path) + async def get_at_target(msg): for msg_seg in msg: