from typing import Optional, Union from nonebot.adapters.onebot.v11 import MessageSegment from configs.config import Config from asyncio.exceptions import TimeoutError from services.log import logger from configs.path_config import IMAGE_PATH from utils.image_utils import BuildImage from utils.http_utils import AsyncHttpx from utils.utils import get_user_avatar from utils.message_builder import image from .._utils import get_ds from .._models import Genshin from io import BytesIO from nonebot import Driver import asyncio import nonebot driver: Driver = nonebot.get_driver() memo_path = IMAGE_PATH / "genshin" / "genshin_memo" memo_path.mkdir(exist_ok=True, parents=True) @driver.on_startup async def _(): for name, url in zip( ["resin.png", "task.png", "resin_discount.png"], [ "https://upload-bbs.mihoyo.com/upload/2021/09/29/8819732/54266243c7d15ba31690c8f5d63cc3c6_71491376413333325" "20.png?x-oss-process=image//resize,s_600/quality,q_80/auto-orient,0/interlace,1/format,png", "https://patchwiki.biligame.com/images/ys/thumb/c/cc/6k6kuj1kte6m1n7hexqfrn92z6h4yhh.png/60px-委托任务logo.png", "https://patchwiki.biligame.com/images/ys/d/d9/t1hv6wpucbwucgkhjntmzroh90nmcdv.png", ], ): file = memo_path / name if not file.exists(): await AsyncHttpx.download_file(url, file) logger.info(f"已下载原神便签资源 -> {file}...") async def get_user_memo(user_id: int, uid: int, uname: str) -> Optional[Union[str, MessageSegment]]: uid = str(uid) if uid[0] in ["1", "2"]: server_id = "cn_gf01" elif uid[0] == "5": server_id = "cn_qd01" else: return None return await parse_data_and_draw(user_id, uid, server_id, uname) async def get_memo(uid: str, server_id: str) -> "Union[str, dict], int": try: req = await AsyncHttpx.get( url=f"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote?server={server_id}&role_id={uid}", headers={ "DS": get_ds(f"role_id={uid}&server={server_id}"), "x-rpc-app_version": Config.get_config("genshin", "mhyVersion"), "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1", "x-rpc-client_type": Config.get_config("genshin", "client_type"), "Referer": "https://webstatic.mihoyo.com/", "Cookie": await Genshin.get_user_cookie(int(uid)) }, ) data = req.json() if data["message"] == "OK": return data["data"], 200 return data["message"], 999 except TimeoutError: return "访问超时,请稍后再试", 997 except Exception as e: logger.error(f"便签查询获取失败未知错误 {e}:{e}") return "发生了一些错误,请稍后再试", 998 def create_border( image_name: str, content: str, notice_text: str, value: str ) -> BuildImage: border = BuildImage(500, 100, color="#E0D9D1", font="HYWenHei-85W.ttf", font_size=20) text_bk = BuildImage(350, 96, color="#F5F1EB", font_size=23, font="HYWenHei-85W.ttf") _x = 70 if image_name == "resin.png" else 50 _px = 10 if image_name == "resin.png" else 20 text_bk.paste( BuildImage(_x, _x, background=memo_path / image_name), (_px, 0), True, center_type="by_height", ) text_bk.text((87, 20), content) text_bk.paste( BuildImage( 0, 0, plain_text=notice_text, font_color=(203, 189, 175), font="HYWenHei-85W.ttf", font_size=17, ), (87, 50), True, ) font_width, _ = border.getsize(value) border.text((350 + 76 - int(font_width / 2), 0), value, center_type="by_height") border.paste(text_bk, (2, 0), center_type="by_height") return border async def parse_data_and_draw( user_id: int, uid: str, server_id: str, uname: str ) -> Union[str, MessageSegment]: data, code = await get_memo(uid, server_id) if code != 200: return data user_avatar = BytesIO(await get_user_avatar(user_id)) for x in data["expeditions"]: file_name = x["avatar_side_icon"].split("_")[-1] role_avatar = memo_path / "role_avatar" / file_name if not role_avatar.exists(): await AsyncHttpx.download_file(x["avatar_side_icon"], role_avatar) return await asyncio.get_event_loop().run_in_executor( None, _parse_data_and_draw, data, user_avatar, uid, uname ) def _parse_data_and_draw( data: dict, user_avatar: BytesIO, uid: int, uname: str ) -> Union[str, MessageSegment]: current_resin = data["current_resin"] # 当前树脂 max_resin = data["max_resin"] # 最大树脂 resin_recovery_time = data["resin_recovery_time"] # 树脂全部回复时间 finished_task_num = data["finished_task_num"] # 完成的每日任务 total_task_num = data["total_task_num"] # 每日任务总数 remain_resin_discount_num = data["remain_resin_discount_num"] # 值得铭记的强敌总数 resin_discount_num_limit = data["resin_discount_num_limit"] # 剩余值得铭记的强敌 current_expedition_num = data["current_expedition_num"] # 当前挖矿人数 max_expedition_num = data["max_expedition_num"] # 每日挖矿最大人数 expeditions = data["expeditions"] # 挖矿详情 minute, second = divmod(int(resin_recovery_time), 60) hour, minute = divmod(minute, 60) A = BuildImage(1030, 520, color="#f1e9e1", font_size=15, font="HYWenHei-85W.ttf") A.text((10, 15), "原神便笺 | Create By ZhenXun", (198, 186, 177)) ava = BuildImage(100, 100, background=user_avatar) ava.circle() A.paste(ava, (40, 40), True) A.paste( BuildImage(0, 0, plain_text=uname, font_size=20, font="HYWenHei-85W.ttf"), (160, 62), True, ) A.paste( BuildImage( 0, 0, plain_text=f"UID:{uid}", font_size=15, font="HYWenHei-85W.ttf", font_color=(21, 167, 89), ), (160, 92), True, ) border = create_border( "resin.png", "原粹树脂", "将在{:0>2d}:{:0>2d}:{:0>2d}秒后全部恢复".format(hour, minute, second), f"{current_resin}/{max_resin}", ) A.paste(border, (10, 155)) border = create_border( "task.png", "每日委托", "今日委托已全部完成" if finished_task_num == total_task_num else "今日委托完成数量不足", f"{finished_task_num}/{total_task_num}", ) A.paste(border, (10, 265)) border = create_border( "resin_discount.png", "值得铭记的强敌", "本周剩余消耗减半次数", f"{remain_resin_discount_num}/{resin_discount_num_limit}", ) A.paste(border, (10, 375)) expeditions_border = BuildImage( 470, 430, color="#E0D9D1", font="HYWenHei-85W.ttf", font_size=20 ) expeditions_text = BuildImage( 466, 426, color="#F5F1EB", font_size=23, font="HYWenHei-85W.ttf" ) expeditions_text.text( (5, 5), f"探索派遣限制{current_expedition_num}/{max_expedition_num}", (100, 100, 98) ) h = 45 for x in expeditions: _bk = BuildImage(400, 66, color="#ECE3D8", font="HYWenHei-85W.ttf", font_size=21) file_name = x["avatar_side_icon"].split("_")[-1] role_avatar = memo_path / "role_avatar" / file_name _ava_img = BuildImage(75, 75, background=role_avatar) # _ava_img.circle() if x["status"] == "Finished": msg = "探索完成" font_color = (146, 188, 63) _circle_color = (146, 188, 63) else: minute, second = divmod(int(x["remained_time"]), 60) hour, minute = divmod(minute, 60) font_color = (193, 180, 167) msg = "还剩{:0>2d}小时{:0>2d}分钟{:0>2d}秒".format(hour, minute, second) _circle_color = "#DE9C58" _circle_bk = BuildImage(60, 60) _circle_bk.circle() a_circle = BuildImage(55, 55, color=_circle_color) a_circle.circle() b_circle = BuildImage(47, 47) b_circle.circle() a_circle.paste(b_circle, (4, 4), True) _circle_bk.paste(a_circle, (4, 4), True) _bk.paste(_circle_bk, (25, 0), True, "by_height") _bk.paste(_ava_img, (19, -13), True) _bk.text((100, 0), msg, font_color, "by_height") _bk.circle_corner(20) expeditions_text.paste(_bk, (25, h), True) h += 75 expeditions_border.paste(expeditions_text, center_type="center") A.paste(expeditions_border, (550, 45)) return image(b64=A.pic2bs4())