diff --git a/assets/Genshin_All_Char.xlsx b/assets/Genshin_All_Char.xlsx new file mode 100644 index 0000000..8f67b55 Binary files /dev/null and b/assets/Genshin_All_Char.xlsx differ diff --git a/assets/data/id2name.json b/assets/data/id2name.json new file mode 100644 index 0000000..e21d369 --- /dev/null +++ b/assets/data/id2name.json @@ -0,0 +1,52 @@ +{ + "10000002": "神里绫华", + "10000003": "琴", + "10000006": "丽莎", + "10000005": "空", + "10000007": "荧", + "10000014": "芭芭拉", + "10000015": "凯亚", + "10000016": "迪卢克", + "10000020": "雷泽", + "10000021": "安柏", + "10000022": "温迪", + "10000023": "香菱", + "10000024": "北斗", + "10000025": "行秋", + "10000026": "魈", + "10000027": "凝光", + "10000029": "可莉", + "10000030": "钟离", + "10000031": "菲谢尔", + "10000032": "班尼特", + "10000033": "达达利亚", + "10000034": "诺艾尔", + "10000035": "七七", + "10000036": "重云", + "10000037": "甘雨", + "10000038": "阿贝多", + "10000039": "迪奥娜", + "10000041": "莫娜", + "10000042": "刻晴", + "10000043": "砂糖", + "10000044": "辛焱", + "10000045": "罗莎莉亚", + "10000046": "胡桃", + "10000047": "枫原万叶", + "10000048": "烟绯", + "10000049": "宵宫", + "10000050": "托马", + "10000051": "优菈", + "10000052": "雷电将军", + "10000053": "早柚", + "10000054": "珊瑚宫心海", + "10000055": "五郎", + "10000056": "九条裟罗", + "10000057": "荒泷一斗", + "10000058": "八重神子", + "10000062": "埃洛伊", + "10000063": "申鹤", + "10000064": "云堇", + "10000066": "神里绫人", + "注释1": "10000005/7分别对应哥哥和妹妹" +} diff --git a/assets/images/zb.png b/assets/images/zb.png index 1644d7f..ddf20b0 100644 Binary files a/assets/images/zb.png and b/assets/images/zb.png differ diff --git a/defs/char_adv.py b/defs/char_adv.py new file mode 100644 index 0000000..3c6451c --- /dev/null +++ b/defs/char_adv.py @@ -0,0 +1,91 @@ +import os + +from openpyxl import load_workbook + +FILE_PATH = "assets" +char_adv_im = '''【{}】 +【五星武器】:{} +【四星武器】:{} +【三星武器】:{} +【圣遗物】: +{}''' + + +async def weapon_adv(name): + char_adv_path = os.path.join(FILE_PATH, "Genshin_All_Char.xlsx") + wb = load_workbook(char_adv_path) + ws = wb.active + + weapon_name = "" + char_list = [] + for c in range(2, 5): + for r in range(2, 300): + if ws.cell(r, c).value: + # if all(i in ws.cell(r,c).value for i in name): + if name in ws.cell(r, c).value: + weapon_name = ws.cell(r, c).value + char_list.append(ws.cell(2 + ((r - 2) // 5) * 5, 1).value) + + if char_list: + im = ','.join(char_list) + im = im + " 可能会用到【{}】".format(weapon_name) + else: + im = " 没有角色能使用【{}】".format(weapon_name) + return im + + +async def char_adv(name): + char_adv_path = os.path.join(FILE_PATH, "Genshin_All_Char.xlsx") + wb = load_workbook(char_adv_path) + ws = wb.active + char_list = ws["A"] + index = None + for i in char_list: + if i.value: + if all(g in i.value for g in name): + # if name in i.value: + index = i.row + char_name = i.value + if index: + weapon_5star = "" + for i in range(index, index + 5): + if ws.cell(i, 2).value: + weapon_5star += ws.cell(i, 2).value + ">" + if weapon_5star != "": + weapon_5star = weapon_5star[:-1] + else: + weapon_5star = "无推荐" + + weapon_4star = "" + for i in range(index, index + 5): + if ws.cell(i, 3).value: + weapon_4star += ws.cell(i, 3).value + ">" + if weapon_4star != "": + weapon_4star = weapon_4star[:-1] + else: + weapon_4star = "无推荐" + + weapon_3star = "" + for i in range(index, index + 5): + if ws.cell(i, 4).value: + weapon_3star += ws.cell(i, 4).value + ">" + if weapon_3star != "": + weapon_3star = weapon_3star[:-1] + else: + weapon_3star = "无推荐" + + artifacts = "" + for i in range(index, index + 5): + if ws.cell(i, 5).value: + if ws.cell(i, 6).value: + artifacts += ws.cell(i, 5).value + "*2" + ws.cell(i, 6).value + "*2" + "\n" + else: + artifacts += ws.cell(i, 5).value + "*4" + "\n" + + if artifacts != "": + artifacts = artifacts[:-1] + else: + artifacts = "无推荐" + + im = char_adv_im.format(char_name, weapon_5star, weapon_4star, weapon_3star, artifacts) # noqa + return im diff --git a/defs/db.py b/defs/db.py index 05eeb74..b5ad1af 100644 --- a/defs/db.py +++ b/defs/db.py @@ -9,12 +9,19 @@ import traceback from shutil import copyfile import requests +from httpx import AsyncClient + from ci import client from defs.mysbbs import MihoyoBbs mhyVersion = "2.11.1" +def regex_func(value, patter): + c_pattern = re.compile(r"account_id={}".format(patter)) + return c_pattern.search(value) is not None + + async def cookiesDB(uid, Cookies, qid): conn = sqlite3.connect('ID_DATA.db') c = conn.cursor() @@ -406,29 +413,28 @@ def functionRegex(value, patter): def cacheDB(uid, mode=1, mys=None): - use = '' conn = sqlite3.connect('ID_DATA.db') c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS CookiesCache - (UID TEXT PRIMARY KEY, - MYSID TEXT, - Cookies TEXT);''') - if mode == 2: - cursor = c.execute("SELECT * FROM CookiesCache WHERE MYSID = ?", (uid,)) - c_data = cursor.fetchall() - else: + (UID TEXT PRIMARY KEY, + MYSID TEXT, + Cookies TEXT);''') + + if mode == 1: if mys: cursor = c.execute("SELECT * FROM CookiesCache WHERE MYSID = ?", (mys,)) - c_data = cursor.fetchall() else: cursor = c.execute("SELECT * FROM CookiesCache WHERE UID = ?", (uid,)) - c_data = cursor.fetchall() + else: + cursor = c.execute("SELECT * FROM CookiesCache WHERE MYSID = ?", (uid,)) + c_data = cursor.fetchall() if len(c_data) == 0: if mode == 2: - conn.create_function("REGEXP", 2, functionRegex) + conn.create_function("REGEXP", 2, regex_func) cursor = c.execute("SELECT * FROM NewCookiesTable WHERE REGEXP(Cookies, ?)", (uid,)) d_data = cursor.fetchall() + else: cursor = c.execute("SELECT * FROM NewCookiesTable WHERE UID = ?", (uid,)) d_data = cursor.fetchall() @@ -438,33 +444,33 @@ def cacheDB(uid, mode=1, mys=None): use = d_data[0][1] if mode == 1: c.execute("INSERT OR IGNORE INTO CookiesCache (Cookies,UID) \ - VALUES (?, ?)", (use, uid)) + VALUES (?, ?)", (use, uid)) elif mode == 2: c.execute("INSERT OR IGNORE INTO CookiesCache (Cookies,MYSID) \ - VALUES (?, ?)", (use, uid)) + VALUES (?, ?)", (use, uid)) else: - cookiesrow = c.execute("SELECT * FROM NewCookiesTable WHERE Extra IS NULL ORDER BY RANDOM() LIMIT 1") - e_data = cookiesrow.fetchall() + cookies_row = c.execute("SELECT * FROM NewCookiesTable WHERE Extra IS NULL ORDER BY RANDOM() LIMIT 1") + e_data = cookies_row.fetchall() if len(e_data) != 0: if mode == 1: c.execute("INSERT OR IGNORE INTO CookiesCache (Cookies,UID) \ - VALUES (?, ?)", (e_data[0][1], uid)) + VALUES (?, ?)", (e_data[0][1], uid)) elif mode == 2: c.execute("INSERT OR IGNORE INTO CookiesCache (Cookies,MYSID) \ - VALUES (?, ?)", (e_data[0][1], uid)) + VALUES (?, ?)", (e_data[0][1], uid)) use = e_data[0][1] else: return "没有可以使用的Cookies!" else: - cookiesrow = c.execute("SELECT * FROM NewCookiesTable WHERE Extra IS NULL ORDER BY RANDOM() LIMIT 1") - e_data = cookiesrow.fetchall() + cookies_row = c.execute("SELECT * FROM NewCookiesTable WHERE Extra IS NULL ORDER BY RANDOM() LIMIT 1") + e_data = cookies_row.fetchall() if len(e_data) != 0: if mode == 1: c.execute("INSERT OR IGNORE INTO CookiesCache (Cookies,UID) \ - VALUES (?, ?)", (e_data[0][1], uid)) + VALUES (?, ?)", (e_data[0][1], uid)) elif mode == 2: c.execute("INSERT OR IGNORE INTO CookiesCache (Cookies,MYSID) \ - VALUES (?, ?)", (e_data[0][1], uid)) + VALUES (?, ?)", (e_data[0][1], uid)) use = e_data[0][1] else: return "没有可以使用的Cookies!" @@ -608,3 +614,63 @@ async def GetInfo(Uid, ck, ServerID="cn_gf01"): except Exception as e: print("米游社基础信息读取老Api失败!") print(e.with_traceback) + + +async def get_spiral_abyss_info(uid, ck, schedule_type="1", server_id="cn_gf01"): + if uid[0] == '5': + server_id = "cn_qd01" + try: + async with AsyncClient() as c: + req = await c.get( + url="https://api-takumi.mihoyo.com/game_record/app/genshin/api/spiralAbyss", + headers={ + 'DS': DSGet("role_id=" + uid + "&schedule_type=" + schedule_type + "&server=" + server_id), + 'Origin': 'https://webstatic.mihoyo.com', + 'Cookie': ck, + 'x-rpc-app_version': 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': '5', + 'Referer': 'https://webstatic.mihoyo.com/' + }, + params={ + "schedule_type": schedule_type, + "role_id": uid, + "server": server_id + } + ) + data = json.loads(req.text) + return data + except requests.exceptions.SSLError: + try: + async with AsyncClient() as c: + req = await c.get( + url="https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/spiralAbyss", + headers={ + 'DS': DSGet( + "role_id=" + uid + "&schedule_type=" + schedule_type + "&server=" + server_id), + 'Origin': 'https://webstatic.mihoyo.com', + 'Cookie': ck, + 'x-rpc-app_version': 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': '5', + 'Referer': 'https://webstatic.mihoyo.com/' + }, + params={ + "role_id": uid, + "server": server_id, + "bbs_presentation_style": "fullscreen", + "bbs_auth_required": "true", + "utm_source": "bbs", + "utm_medium": "mys", + "utm_campaign": "icon" + } + ) + data = json.loads(req.text) + return data + except json.decoder.JSONDecodeError: + print("深渊信息读取新Api失败!") + except Exception as e: + print("深渊信息读取老Api失败!") + print(e.with_traceback) diff --git a/defs/spiral_abyss.py b/defs/spiral_abyss.py new file mode 100644 index 0000000..8a67f14 --- /dev/null +++ b/defs/spiral_abyss.py @@ -0,0 +1,587 @@ +import math +import os +import random +import time +from io import BytesIO +from httpx import get +from PIL import Image, ImageDraw, ImageFont + +from defs.db import cacheDB, GetMysInfo, GetInfo, errorDB, get_spiral_abyss_info + +FILE2_PATH = os.path.join("assets") +CHAR_DONE_PATH = os.path.join(FILE2_PATH, "char_done") +TEXT_PATH = os.path.join(FILE2_PATH, "bg2") +BG_PATH = os.path.join(FILE2_PATH, "bg") + + +def get_char_done_pic(_id, url, star): + char_data = get(url).content + if star == 4: + star1_path = os.path.join(TEXT_PATH, '4star_1.png') + star2_path = os.path.join(TEXT_PATH, '4star_2.png') + else: + star1_path = os.path.join(TEXT_PATH, '5star_1.png') + star2_path = os.path.join(TEXT_PATH, '5star_2.png') + star_1 = Image.open(star1_path) + star_2 = Image.open(star2_path) + char_img = Image.open(BytesIO(char_data)).resize((104, 104), Image.ANTIALIAS) + star_1.paste(char_img, (12, 15), char_img) + star_1.paste(star_2, (0, 0), star_2) + star_1.save(os.path.join(CHAR_DONE_PATH, str(_id) + '.png')) + + +def genshin_font(size): + return ImageFont.truetype(f"assets{os.sep}fonts{os.sep}yuan_shen.ttf", size=size, encoding="utf-8") + + +async def draw_abyss0_pic(uid, nickname, image=None, mode=2, date="1"): + # 获取Cookies + while True: + use_cookies = cacheDB(uid, mode - 1) + if use_cookies == '': + return "绑定记录不存在。" + elif use_cookies == "没有可以使用的Cookies!": + return "没有可以使用的Cookies!" + + if mode == 3: + mys_data = await GetMysInfo(uid, use_cookies) + for i in mys_data['data']['list']: + if i['game_id'] != 2: + mys_data['data']['list'].remove(i) + uid = mys_data['data']['list'][0]['game_role_id'] + nickname = mys_data['data']['list'][0]['nickname'] + + raw_data = await get_spiral_abyss_info(uid, use_cookies, date) + raw_char_data = await GetInfo(uid, use_cookies) + + if raw_data["retcode"] != 0: + if raw_data["retcode"] == 10001: + # return ("Cookie错误/过期,请重置Cookie") + errorDB(use_cookies, "error") + elif raw_data["retcode"] == 10101: + # return ("当前cookies已达到30人上限!") + errorDB(use_cookies, "limit30") + elif raw_data["retcode"] == 10102: + return "当前查询id已经设置了隐私,无法查询!" + else: + return ( + "Api报错,返回内容为:\r\n" + + str(raw_data) + "\r\n出现这种情况可能的UID输入错误 or 不存在" + ) + else: + break + + # 获取数据 + raw_data = raw_data["data"] + raw_char_data = raw_char_data['data']["avatars"] + + # 获取查询者数据 + if not raw_data['floors']: + return "" + floors_data = raw_data['floors'][-1] + levels_num = len(floors_data['levels']) + + # 获取背景图片 + bg2_path = os.path.join(BG_PATH, random.choice([x for x in os.listdir(BG_PATH) + if os.path.isfile(os.path.join(BG_PATH, x))])) + + if image: + image_data = image.group(2) + edit_bg = Image.open(BytesIO(get(image_data).content)) + else: + edit_bg = Image.open(bg2_path) + + # 确定图片的长宽 + based_w = 900 + based_h = 660 + levels_num * 315 + based_scale = '%.3f' % (based_w / based_h) + + w, h = edit_bg.size + scale_f = '%.3f' % (w / h) + new_w = math.ceil(based_h * float(scale_f)) + new_h = math.ceil(based_w / float(scale_f)) + if scale_f > based_scale: + bg_img2 = edit_bg.resize((new_w, based_h), Image.ANTIALIAS) + else: + bg_img2 = edit_bg.resize((based_w, new_h), Image.ANTIALIAS) + + bg_img = bg_img2.crop((0, 0, based_w, based_h)) + + # 获取背景主色 + q = edit_bg.quantize(colors=3, method=2) + bg_num_temp = 0 + for i in range(0, 3): + bg = tuple(q.getpalette()[i * 3:(i * 3) + 3]) + bg_num = bg[0] + bg[1] + bg[2] + if bg_num >= bg_num_temp: + bg_num_temp = bg_num + bg_color = (bg[0], bg[1], bg[2]) + + # 通过背景主色(bg_color)确定文字主色 + r = 140 + if max(*bg_color) > 255 - r: + r *= -1 + new_color = (math.floor(bg_color[0] + r if bg_color[0] + r <= 255 else 255), + math.floor(bg_color[1] + r if bg_color[1] + r <= 255 else 255), + math.floor(bg_color[2] + r if bg_color[2] + r <= 255 else 255)) + + # 确定贴图路径 + abyss0_path = os.path.join(TEXT_PATH, "abyss_0.png") + abyss3_path = os.path.join(TEXT_PATH, "abyss_3.png") + abyss_star0_path = os.path.join(TEXT_PATH, "abyss_star0.png") + abyss_star1_path = os.path.join(TEXT_PATH, "abyss_star1.png") + avatar_bg_path = os.path.join(TEXT_PATH, "avatar_bg.png") + avatar_fg_path = os.path.join(TEXT_PATH, "avatar_fg.png") + + all_mask_path = os.path.join(TEXT_PATH, "All_Mask.png") + + # 转换遮罩的颜色、大小匹配,并paste上去 + all_mask = Image.open(all_mask_path).resize(bg_img.size, Image.ANTIALIAS) + all_mask_img = Image.new("RGBA", (based_w, based_h), bg_color) + bg_img.paste(all_mask_img, (0, 0), all_mask) + + # 开启图片 + avatar_bg = Image.open(avatar_bg_path) + avatar_fg = Image.open(avatar_fg_path) + + # 确定主体框架 + avatar_bg_color = Image.new("RGBA", (316, 100), bg_color) + bg_img.paste(avatar_bg_color, (113, 98), avatar_bg) + bg_img.paste(avatar_fg, (114, 95), avatar_fg) + + """ + x1, y1 = 65, 276 + radius = 15 + cropped_img1 = bg_img.crop((x1, y1, 836, 607)) + blurred_img1 = cropped_img1.filter(ImageFilter.GaussianBlur(5),).convert("RGBA") + bg_img.paste(blurred_img1, (x1, y1), create_rounded_rectangle_mask(cropped_img1,radius)) + for i in range(0,len(floors_data['levels'])): + x2, y2 = 65, 630 + 315*i + radius = 15 + cropped_img2 = bg_img.crop((x2, y2, 836, 925+315*i)) + blurred_img2 = cropped_img2.filter(ImageFilter.GaussianBlur(5),).convert("RGBA") + bg_img.paste(blurred_img2, (x2, y2), create_rounded_rectangle_mask(cropped_img2,radius)) + """ + + abyss0_bg_color = Image.new("RGBA", (900, 620), new_color) + abyss0 = Image.new("RGBA", (900, 620), (0, 0, 0, 0)) + + abyss0_pic = Image.open(abyss0_path) + abyss0.paste(abyss0_bg_color, (0, 0), abyss0_pic) + abyss3 = Image.open(abyss3_path) + abyss_star0 = Image.open(abyss_star0_path) + abyss_star1 = Image.open(abyss_star1_path) + + for i in range(0, 4): + if not os.path.exists(os.path.join(CHAR_DONE_PATH, str(raw_data["reveal_rank"][i]["avatar_id"]) + ".png")): + get_char_done_pic(raw_data["reveal_rank"][i]["avatar_id"], raw_data["reveal_rank"][i]["avatar_icon"], + raw_data["reveal_rank"][i]["rarity"]) + char = os.path.join(CHAR_DONE_PATH, str(raw_data["reveal_rank"][i]["avatar_id"]) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == raw_data["reveal_rank"][i]["avatar_id"]: + char_draw.text((63.5, 117), f'{str(raw_data["reveal_rank"][i]["value"])}次', (21, 21, 21), + genshin_font(18), anchor="mm") + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (82 + 130 * i, 300) + abyss0.paste(char_img, char_crop, char_img) + + for i in range(0, 1): + if not os.path.exists(os.path.join(CHAR_DONE_PATH, str(raw_data["damage_rank"][i]["avatar_id"]) + ".png")): + get_char_done_pic(raw_data["damage_rank"][i]["avatar_id"], raw_data["damage_rank"][i]["avatar_icon"], + raw_data["reveal_rank"][i]["rarity"]) + char = os.path.join(CHAR_DONE_PATH, str(raw_data["damage_rank"][i]["avatar_id"]) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == raw_data["damage_rank"][i]["avatar_id"]: + char_draw.text((63.5, 117), f'{str(raw_data["damage_rank"][i]["value"])}', (21, 21, 21), + genshin_font(18), anchor="mm") + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (685, 470) + abyss0.paste(char_img, char_crop, char_img) + + for i in range(0, 1): + if not os.path.exists(os.path.join(CHAR_DONE_PATH, str(raw_data["defeat_rank"][i]["avatar_id"]) + ".png")): + get_char_done_pic(raw_data["defeat_rank"][i]["avatar_id"], raw_data["defeat_rank"][i]["avatar_icon"], + raw_data["reveal_rank"][i]["rarity"]) + char = os.path.join(CHAR_DONE_PATH, str(raw_data["defeat_rank"][i]["avatar_id"]) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == raw_data["defeat_rank"][i]["avatar_id"]: + char_draw.text((63.5, 117), f'{str(raw_data["defeat_rank"][i]["value"])}', (21, 21, 21), + genshin_font(18), anchor="mm") + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (82 + 123 * i, 470) + abyss0.paste(char_img, char_crop, char_img) + + for i in range(0, 1): + if not os.path.exists(os.path.join(CHAR_DONE_PATH, str(raw_data["take_damage_rank"][i]["avatar_id"]) + ".png")): + get_char_done_pic(raw_data["take_damage_rank"][i]["avatar_id"], + raw_data["take_damage_rank"][i]["avatar_icon"], raw_data["reveal_rank"][i]["rarity"]) + char = os.path.join(CHAR_DONE_PATH, str(raw_data["take_damage_rank"][i]["avatar_id"]) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == raw_data["take_damage_rank"][i]["avatar_id"]: + char_draw.text((63.5, 117), f'{str(raw_data["take_damage_rank"][i]["value"])}', (21, 21, 21), + genshin_font(18), anchor="mm") + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (232 + 123 * i, 470) + abyss0.paste(char_img, char_crop, char_img) + + for i in range(0, 1): + if not os.path.exists( + os.path.join(CHAR_DONE_PATH, str(raw_data["normal_skill_rank"][i]["avatar_id"]) + ".png")): + get_char_done_pic(raw_data["normal_skill_rank"][i]["avatar_id"], + raw_data["normal_skill_rank"][i]["avatar_icon"], raw_data["reveal_rank"][i]["rarity"]) + char = os.path.join(CHAR_DONE_PATH, str(raw_data["normal_skill_rank"][i]["avatar_id"]) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == raw_data["normal_skill_rank"][i]["avatar_id"]: + char_draw.text((63.5, 117), f'{str(raw_data["normal_skill_rank"][i]["value"])}', (21, 21, 21), + genshin_font(18), anchor="mm") + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (382 + 123 * i, 470) + abyss0.paste(char_img, char_crop, char_img) + + for i in range(0, 1): + if not os.path.exists( + os.path.join(CHAR_DONE_PATH, str(raw_data["energy_skill_rank"][i]["avatar_id"]) + ".png")): + get_char_done_pic(raw_data["energy_skill_rank"][i]["avatar_id"], + raw_data["energy_skill_rank"][i]["avatar_icon"], raw_data["reveal_rank"][i]["rarity"]) + char = os.path.join(CHAR_DONE_PATH, str(raw_data["energy_skill_rank"][i]["avatar_id"]) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == raw_data["energy_skill_rank"][i]["avatar_id"]: + char_draw.text((63.5, 118), f'{str(raw_data["energy_skill_rank"][i]["value"])}', (21, 21, 21), + genshin_font(18), anchor="mm") + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (532 + 123 * i, 470) + abyss0.paste(char_img, char_crop, char_img) + + bg_img.paste(abyss0, (0, 0), abyss0) + + for j in range(0, len(floors_data["levels"])): + abyss2 = Image.new("RGBA", (900, 340), (0, 0, 0, 0)) + # abyss2 = Image.open(abyss2_path) + num_1 = 0 + for i in floors_data['levels'][j]['battles'][0]['avatars']: + if not os.path.exists(os.path.join(CHAR_DONE_PATH, str(i['id']) + ".png")): + get_char_done_pic(i['id'], i['icon'], i['rarity']) + char = os.path.join(CHAR_DONE_PATH, str(i['id']) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == i['id']: + char_draw.text((40, 108), f'Lv.{str(k["level"])}', (21, 21, 21), genshin_font(18)) + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (70 + 125 * (num_1 % 4), 46) + abyss2.paste(char_img, char_crop, char_img) + num_1 = num_1 + 1 + num_2 = 0 + for i in floors_data['levels'][j]['battles'][1]['avatars']: + if not os.path.exists(os.path.join(CHAR_DONE_PATH, str(i['id']) + ".png")): + get_char_done_pic(i['id'], i['icon'], i['rarity']) + char = os.path.join(CHAR_DONE_PATH, str(i['id']) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == i['id']: + char_draw.text((40, 108), f'Lv.{str(k["level"])}', (21, 21, 21), genshin_font(18)) + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (70 + 125 * (num_2 % 4), 180) + abyss2.paste(char_img, char_crop, char_img) + num_2 = num_2 + 1 + star_num = floors_data['levels'][j]['star'] + if star_num == 1: + abyss2.paste(abyss_star1, (640, 155), abyss_star1) + abyss2.paste(abyss_star0, (685, 155), abyss_star0) + abyss2.paste(abyss_star0, (730, 155), abyss_star0) + elif star_num == 0: + abyss2.paste(abyss_star0, (640, 155), abyss_star0) + abyss2.paste(abyss_star0, (685, 155), abyss_star0) + abyss2.paste(abyss_star0, (730, 155), abyss_star0) + elif star_num == 2: + abyss2.paste(abyss_star1, (640, 155), abyss_star1) + abyss2.paste(abyss_star1, (685, 155), abyss_star1) + abyss2.paste(abyss_star0, (730, 155), abyss_star0) + else: + abyss2.paste(abyss_star1, (640, 155), abyss_star1) + abyss2.paste(abyss_star1, (685, 155), abyss_star1) + abyss2.paste(abyss_star1, (730, 155), abyss_star1) + abyss2_text_draw = ImageDraw.Draw(abyss2) + abyss2_text_draw.text((87, 30), f"第{j + 1}间", new_color, genshin_font(21)) + timeStamp1 = int(floors_data['levels'][j]['battles'][0]['timestamp']) + timeStamp2 = int(floors_data['levels'][j]['battles'][1]['timestamp']) + timeArray1 = time.localtime(timeStamp1) + timeArray2 = time.localtime(timeStamp2) + otherStyleTime1 = time.strftime("%Y--%m--%d %H:%M:%S", timeArray1) + otherStyleTime2 = time.strftime("%Y--%m--%d %H:%M:%S", timeArray2) + abyss2_text_draw.text((167, 33), f"{otherStyleTime1}/{otherStyleTime2}", new_color, genshin_font(19)) + bg_img.paste(abyss2, (0, 605 + j * 315), abyss2) + + bg_img.paste(abyss3, (0, len(floors_data["levels"]) * 315 + 610), abyss3) + + text_draw = ImageDraw.Draw(bg_img) + text_draw.text((220, 123), f"{nickname}", new_color, genshin_font(32)) + text_draw.text((235, 163), 'UID ' + f"{uid}", new_color, genshin_font(14)) + + text_draw.text((690, 82), raw_data['max_floor'], new_color, genshin_font(26)) + text_draw.text((690, 127), str(raw_data['total_battle_times']), new_color, genshin_font(26)) + text_draw.text((690, 172), str(raw_data['total_star']), new_color, genshin_font(26)) + + bg_img = bg_img.convert('RGB') + bg_img.save(f"temp{os.sep}abyss.jpg", format='JPEG', subsampling=0, quality=90) + # bg_img.save(result_buffer, format='PNG') + return f"temp{os.sep}abyss.jpg" + + +async def draw_abyss_pic(uid, nickname, floor_num, image=None, mode=2, date="1"): + while True: + use_cookies = cacheDB(uid, mode - 1) + if use_cookies == '': + return "绑定记录不存在。" + elif use_cookies == "没有可以使用的Cookies!": + return "没有可以使用的Cookies!" + + if mode == 3: + mys_data = await GetMysInfo(uid, use_cookies) + for i in mys_data['data']['list']: + if i['game_id'] != 2: + mys_data['data']['list'].remove(i) + uid = mys_data['data']['list'][0]['game_role_id'] + nickname = mys_data['data']['list'][0]['nickname'] + + raw_data = await get_spiral_abyss_info(uid, use_cookies, date) + raw_char_data = await GetInfo(uid, use_cookies) + + if raw_data["retcode"] != 0: + if raw_data["retcode"] == 10001: + # return ("Cookie错误/过期,请重置Cookie") + errorDB(use_cookies, "error") + elif raw_data["retcode"] == 10101: + # return ("当前cookies已达到30人上限!") + errorDB(use_cookies, "limit30") + elif raw_data["retcode"] == 10102: + return "当前查询id已经设置了隐私,无法查询!" + else: + return ( + "Api报错,返回内容为:\r\n" + + str(raw_data) + "\r\n出现这种情况可能的UID输入错误 or 不存在" + ) + else: + break + + # 获取数据 + raw_data = raw_data["data"] + raw_char_data = raw_char_data['data']["avatars"] + floors_data = raw_data['floors'] + if not floors_data: + return "" + based_data = [] + for i in floors_data: + if str(i['index']) == floor_num: + based_data = i + levels_num = len(based_data['levels']) + + # 获取背景图片 + bg2_path = os.path.join(BG_PATH, random.choice([x for x in os.listdir(BG_PATH) + if os.path.isfile(os.path.join(BG_PATH, x))])) + + if image: + image_data = image.group(2) + edit_bg = Image.open(BytesIO(get(image_data).content)) + else: + edit_bg = Image.open(bg2_path) + + # 确定图片的长宽 + based_w = 900 + based_h = 440 + levels_num * 340 + based_scale = '%.3f' % (based_w / based_h) + + w, h = edit_bg.size + scale_f = '%.3f' % (w / h) + new_w = math.ceil(based_h * float(scale_f)) + new_h = math.ceil(based_w / float(scale_f)) + if scale_f > based_scale: + bg_img2 = edit_bg.resize((new_w, based_h), Image.ANTIALIAS) + else: + bg_img2 = edit_bg.resize((based_w, new_h), Image.ANTIALIAS) + + bg_img = bg_img2.crop((0, 0, based_w, based_h)) + + # 获取背景主色 + q = edit_bg.quantize(colors=3, method=2) + bg_num_temp = 0 + for i in range(0, 3): + bg = tuple(q.getpalette()[i * 3:(i * 3) + 3]) + bg_num = bg[0] + bg[1] + bg[2] + if bg_num >= bg_num_temp: + bg_num_temp = bg_num + bg_color = (bg[0], bg[1], bg[2]) + + # 通过背景主色(bg_color)确定文字主色 + r = 140 + if max(*bg_color) > 255 - r: + r *= -1 + new_color = (math.floor(bg_color[0] + r if bg_color[0] + r <= 255 else 255), + math.floor(bg_color[1] + r if bg_color[1] + r <= 255 else 255), + math.floor(bg_color[2] + r if bg_color[2] + r <= 255 else 255)) + + # 打开图片 + abyss1_path = os.path.join(TEXT_PATH, "abyss_1.png") + abyss3_path = os.path.join(TEXT_PATH, "abyss_3.png") + abyss_star0_path = os.path.join(TEXT_PATH, "abyss_star0.png") + abyss_star1_path = os.path.join(TEXT_PATH, "abyss_star1.png") + abyss1 = Image.open(abyss1_path) + abyss3 = Image.open(abyss3_path) + abyss_star0 = Image.open(abyss_star0_path) + abyss_star1 = Image.open(abyss_star1_path) + avatar_bg_path = os.path.join(TEXT_PATH, "avatar_bg.png") + avatar_fg_path = os.path.join(TEXT_PATH, "avatar_fg.png") + + all_mask_path = os.path.join(TEXT_PATH, "All_Mask.png") + + # 转换遮罩的颜色、大小匹配,并paste上去 + all_mask = Image.open(all_mask_path).resize(bg_img.size, Image.ANTIALIAS) + all_mask_img = Image.new("RGBA", (based_w, based_h), bg_color) + bg_img.paste(all_mask_img, (0, 0), all_mask) + + # 开启图片 + avatar_bg = Image.open(avatar_bg_path) + avatar_fg = Image.open(avatar_fg_path) + + # 确定主体框架 + avatar_bg_color = Image.new("RGBA", (316, 100), bg_color) + bg_img.paste(avatar_bg_color, (113, 145), avatar_bg) + bg_img.paste(avatar_fg, (114, 142), avatar_fg) + + """ + for i in range(0,len(based_data['levels'])): + x, y = 65, 220 + 340*i + radius = 10 + cropped_img = bg_img.crop((x, y, 836, 517+340*i)) + blurred_img = cropped_img.filter(ImageFilter.GaussianBlur(5),).convert("RGBA") + bg_img.paste(blurred_img, (x, y), create_rounded_rectangle_mask(cropped_img,radius)) + """ + + abyss1_bg_color = Image.new("RGBA", (900, 400), bg_color) + bg_img.paste(abyss1_bg_color, (0, 0), abyss1) + + for j in range(0, len(based_data['levels'])): + abyss2 = Image.new("RGBA", (900, 340), (0, 0, 0, 0)) + num_1 = 0 + avatars = based_data['levels'][j]['battles'][0]['avatars'] + based_data['levels'][j]['battles'][1]['avatars'] + for i in based_data['levels'][j]['battles'][0]['avatars']: + if not os.path.exists(os.path.join(CHAR_DONE_PATH, str(i['id']) + ".png")): + get_char_done_pic(i['id'], i['icon'], i['rarity']) + char = os.path.join(CHAR_DONE_PATH, str(i['id']) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == i['id']: + char_draw.text((40, 108), f'Lv.{str(k["level"])}', (21, 21, 21), genshin_font(18)) + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (70 + 125 * (num_1 % 4), 46) + abyss2.paste(char_img, char_crop, char_img) + num_1 = num_1 + 1 + num_2 = 0 + for i in based_data['levels'][j]['battles'][1]['avatars']: + if not os.path.exists(os.path.join(CHAR_DONE_PATH, str(i['id']) + ".png")): + get_char_done_pic(i['id'], i['icon'], i['rarity']) + char = os.path.join(CHAR_DONE_PATH, str(i['id']) + ".png") + char_img = Image.open(char) + char_draw = ImageDraw.Draw(char_img) + for k in raw_char_data: + if k['id'] == i['id']: + char_draw.text((40, 108), f'Lv.{str(k["level"])}', (21, 21, 21), genshin_font(18)) + char_draw.text((95.3, 19), f'{str(k["actived_constellation_num"])}', 'white', genshin_font(18)) + if str(k["fetter"]) == "10" or str(k["name"]) == "旅行者": + char_draw.text((93, 41.5), "♥", (21, 21, 21), genshin_font(15)) + else: + char_draw.text((95.3, 40.5), f'{str(k["fetter"])}', (21, 21, 21), genshin_font(18)) + char_crop = (70 + 125 * (num_2 % 4), 180) + abyss2.paste(char_img, char_crop, char_img) + num_2 = num_2 + 1 + star_num = based_data['levels'][j]['star'] + if star_num == 1: + abyss2.paste(abyss_star1, (640, 155), abyss_star1) + abyss2.paste(abyss_star0, (685, 155), abyss_star0) + abyss2.paste(abyss_star0, (730, 155), abyss_star0) + elif star_num == 0: + abyss2.paste(abyss_star0, (640, 155), abyss_star0) + abyss2.paste(abyss_star0, (685, 155), abyss_star0) + abyss2.paste(abyss_star0, (730, 155), abyss_star0) + elif star_num == 2: + abyss2.paste(abyss_star1, (640, 155), abyss_star1) + abyss2.paste(abyss_star1, (685, 155), abyss_star1) + abyss2.paste(abyss_star0, (730, 155), abyss_star0) + else: + abyss2.paste(abyss_star1, (640, 155), abyss_star1) + abyss2.paste(abyss_star1, (685, 155), abyss_star1) + abyss2.paste(abyss_star1, (730, 155), abyss_star1) + abyss2_text_draw = ImageDraw.Draw(abyss2) + abyss2_text_draw.text((87, 30), f"第{j + 1}间", new_color, genshin_font(21)) + timeStamp1 = int(based_data['levels'][j]['battles'][0]['timestamp']) + timeStamp2 = int(based_data['levels'][j]['battles'][1]['timestamp']) + timeArray1 = time.localtime(timeStamp1) + timeArray2 = time.localtime(timeStamp2) + otherStyleTime1 = time.strftime("%Y--%m--%d %H:%M:%S", timeArray1) + otherStyleTime2 = time.strftime("%Y--%m--%d %H:%M:%S", timeArray2) + abyss2_text_draw.text((167, 33), f"{otherStyleTime1}/{otherStyleTime2}", new_color, genshin_font(19)) + bg_img.paste(abyss2, (0, 350 + j * 340), abyss2) + + bg_img.paste(abyss3, (0, len(based_data['levels']) * 340 + 400), abyss3) + + text_draw = ImageDraw.Draw(bg_img) + + text_draw.text((220, 163), f"{nickname}", new_color, genshin_font(32)) + text_draw.text((235, 203), 'UID ' + f"{uid}", new_color, genshin_font(14)) + text_draw.text((710, 190), f"{floor_num}", new_color, genshin_font(50), anchor="mm") + + bg_img = bg_img.convert('RGB') + result_buffer = BytesIO() + bg_img.save(f"temp{os.sep}abyss.jpg", format='JPEG', subsampling=0, quality=90) + # bg_img.save(result_buffer, format='PNG') + return f"temp{os.sep}abyss.jpg" diff --git a/defs/spiral_abyss_text.py b/defs/spiral_abyss_text.py new file mode 100644 index 0000000..545afc9 --- /dev/null +++ b/defs/spiral_abyss_text.py @@ -0,0 +1,233 @@ +import json +import os +import time + +from defs.db import GetInfo, cacheDB, GetMysInfo, get_spiral_abyss_info, errorDB +from pydantic import BaseModel +from typing import List + + +class GenshinUserCharacher(BaseModel): + id: int + image: str # 角色头图url + name: str # 角色名 + element: str # 属性 + fetter: int # 好感等级 + level: int + rarity: int # 稀有度 + actived_constellation_num: int # 命之座 + + +class GenshinUserStats(BaseModel): + active_day_number: int # 活跃天数 + achievement_number: int # 成就数 + win_rate: int + anemoculus_number: int # 风神瞳数量 + geoculus_number: int # 岩神瞳数 + electroculus_number: int # 雷神瞳数量 + avatar_number: int # 角色数量 + way_point_number: int # 传送点解锁数 + domain_number: int # 秘境解锁数 + spiral_abyss: str # 深渊进度 + common_chest_number: int # 普通宝箱数量 + exquisite_chest_number: int # 精致宝箱数量 + precious_chest_number: int # 珍贵宝箱数量 + luxurious_chest_number: int # 华丽宝箱数量 + magic_chest_number: int # 奇馈宝箱数量 + + +class GenshinWorldOfferings(BaseModel): + name: str + level: int + + +class GenshinWorldInfo(BaseModel): + level: int # 声望等级 + exploration_percentage: int # 探索度 + icon: str # 区域图标url + name: str + type: str + id: int + offerings: List[GenshinWorldOfferings] # 供奉信息 + + +class GenshinHomeInfo(BaseModel): + level: int # 信任等级 + visit_num: int # 访客数 + comfort_num: int # 洞天仙力 + item_num: int # 摆件数量 + name: str + icon: str # 背景图 + comfort_level_name: str # 洞天仙力对应名称 + comfort_level_icon: str # 等级图标 + + +class GenshinUserData(BaseModel): + avatars: List[GenshinUserCharacher] # 角色列表 + stats: GenshinUserStats + city_explorations: List # 不知道是啥玩意, 都是空的 + world_explorations: List[GenshinWorldInfo] # 区域探索信息 + homes: List[GenshinHomeInfo] # 家园信息 + + +class GenshinAbyssRankInfo(BaseModel): + avatar_id: int + avatar_icon: str + value: int + rarity: int + + +class GenshinAbyssFloorInfoBattlesAvatars(BaseModel): + id: int + icon: str + level: int + rarity: int + + +class GenshinAbyssFloorInfoBattles(BaseModel): + index: int # 战斗场次 + timestamp: str + avatars: List[GenshinAbyssFloorInfoBattlesAvatars] + + +class GenshinAbyssFloorInfo(BaseModel): + index: int # 间号 + star: int + max_star: int + battles: List[GenshinAbyssFloorInfoBattles] + + +class GenshinAbyssFloors(BaseModel): + index: int # 层数 + icon: str # 空的 + is_unlock: bool + settle_time: str + star: int + max_star: int + levels: List[GenshinAbyssFloorInfo] + + +class GenshinAbyss(BaseModel): + schedule_id: int + start_time: int # 10位 + end_time: int # 10位 + total_battle_times: int + total_win_times: int + max_floor: str + reveal_rank: List[GenshinAbyssRankInfo] # 出战次数Rank + defeat_rank: List[GenshinAbyssRankInfo] # 击破数Rank + damage_rank: List[GenshinAbyssRankInfo] # 最强一击 + take_damage_rank: List[GenshinAbyssRankInfo] # 承伤Rank + normal_skill_rank: List[GenshinAbyssRankInfo] # 元素战技释放数 + energy_skill_rank: List[GenshinAbyssRankInfo] # 元素爆发次数 + floors: List[GenshinAbyssFloors] + total_star: int + is_unlock: bool + + +def char_id_to_name(udata: GenshinUserData, charid: int): # id2name.json数据不全, 我也懒得去搜集了, 故采用此邪道方法( + chars = udata.avatars + for char in chars: + if charid == char.id: + return char.name + with open(f"assets{os.sep}data{os.sep}id2name.json", "r", encoding="utf-8") as f: + id2name = json.load(f) + if str(charid) in id2name: + return id2name[str(charid)] + return f"{charid}" + + +def timestamp_to_text(timestamp: int, _format="%Y-%m-%d %H:%M:%S"): + """ + :param timestamp: 时间戳,若输入13位时间戳则自动转为10位 + :param _format: 格式,默认"%Y-%m-%d %H:%M:%S" + :return: %Y-%m-%d %H:%M:%S -> str + """ + if timestamp > 9999999999: # 13位时间戳转10位 + timestamp = timestamp / 1000 + ret = time.strftime(_format, time.localtime(timestamp)) + return ret + + +async def get_user_abyss(uid, mode=2, date="1"): # 深境螺旋 + # 获取Cookies + while True: + use_cookies = cacheDB(uid, mode - 1) + if use_cookies == '': + return "绑定记录不存在。" + elif use_cookies == "没有可以使用的Cookies!": + return "没有可以使用的Cookies!" + + if mode == 3: + mys_data = await GetMysInfo(uid, use_cookies) + for i in mys_data['data']['list']: + if i['game_id'] != 2: + mys_data['data']['list'].remove(i) + uid = mys_data['data']['list'][0]['game_role_id'] + nickname = mys_data['data']['list'][0]['nickname'] + + raw_data = await get_spiral_abyss_info(uid, use_cookies, date) + raw_char_data = await GetInfo(uid, use_cookies) + + if raw_data["retcode"] != 0: + if raw_data["retcode"] == 10001: + # return ("Cookie错误/过期,请重置Cookie") + errorDB(use_cookies, "error") + elif raw_data["retcode"] == 10101: + # return ("当前cookies已达到30人上限!") + errorDB(use_cookies, "limit30") + elif raw_data["retcode"] == 10102: + return "当前查询id已经设置了隐私,无法查询!" + else: + return ( + "Api报错,返回内容为:\r\n" + + str(raw_data) + "\r\n出现这种情况可能的UID输入错误 or 不存在" + ) + else: + break + + # 获取数据 + udata = GenshinUserData(**raw_char_data["data"]) + aby = GenshinAbyss(**raw_data["data"]) + breakpoint() + if not aby.floors: # 没打 + return "" + rettext = f"第{aby.schedule_id}期深境螺旋信息\n\n" \ + f"\t开始时间: {timestamp_to_text(aby.start_time)}\n" \ + f"\t结束时间: {timestamp_to_text(aby.end_time)}\n" \ + f"\t最深抵达:{aby.max_floor}\n" \ + f"\t胜利场次/总场次: {aby.total_win_times}/{aby.total_battle_times}\n" + if aby.reveal_rank: + rettext += f"\t出战最多: {char_id_to_name(udata, aby.reveal_rank[0].avatar_id)} - {aby.reveal_rank[0].value}\n" + if aby.defeat_rank: + rettext += f"\t击破最多: {char_id_to_name(udata, aby.defeat_rank[0].avatar_id)} - {aby.defeat_rank[0].value}\n" + if aby.damage_rank: + rettext += f"\t最强一击: {char_id_to_name(udata, aby.damage_rank[0].avatar_id)} - {aby.damage_rank[0].value}\n" + if aby.take_damage_rank: + rettext += f"\t最高承伤: {char_id_to_name(udata, aby.take_damage_rank[0].avatar_id)} - {aby.take_damage_rank[0].value}\n" + if aby.normal_skill_rank: + rettext += f"\t元素战技: {char_id_to_name(udata, aby.normal_skill_rank[0].avatar_id)} - {aby.normal_skill_rank[0].value}\n" + if aby.energy_skill_rank: + rettext += f"\t元素爆发: {char_id_to_name(udata, aby.energy_skill_rank[0].avatar_id)} - {aby.energy_skill_rank[0].value}\n" + rettext += f"\t总星数: ★ {aby.total_star}\n\t" + + floor_text = "" # 层 + has_details = False + if len(aby.floors) >= 0: + if len(aby.floors[0].levels) > 0: + has_details = True + for floor in aby.floors: # 层 + room_text = "" # 间 + for room in floor.levels: # 间 + battle_text = "" # 场 + for battle in room.battles: # 场次 + character_text = "" # 角色 + for char in battle.avatars: # 角色列表 + character_text += f"/{char_id_to_name(udata, char.id)}" + battle_text += f"\n\t\t\t\t第 {battle.index} 场: {character_text[1:]}" + + room_text += f"\n\t\t\t第 {room.index} 间 (★ {room.star}/{room.max_star}):{battle_text}" + + floor_text += f"\n\n\t\t第 {floor.index} 层:\t{room_text}" + rettext = f"{rettext}楼层信息:{floor_text}" if has_details else f"{rettext}未获取到详细楼层信息" + return rettext diff --git a/plugins/challenge.py b/plugins/challenge.py index ddbccc9..f146b46 100644 --- a/plugins/challenge.py +++ b/plugins/challenge.py @@ -2,6 +2,7 @@ from pyrogram import Client from pyrogram.types import Message from os import getcwd, sep from defs.challenge import get_day +from defs.redis_load import redis_status, redis async def tf_msg(client: Client, message: Message): @@ -23,4 +24,18 @@ async def wq_msg(client: Client, message: Message): async def zb_msg(client: Client, message: Message): - await message.reply_photo(photo=f'{getcwd()}{sep}assets{sep}images{sep}zb.png', quote=True) + if redis_status(): + try: + data = redis.get("zb").decode() + except AttributeError: + data = None + if data != "2.5": + # 开始上传 + msg = await message.reply_photo(photo=f"{getcwd()}{sep}assets{sep}images{sep}zb.png", quote=True) + # 缓存 file_id + redis.set("zb", "2.5") + redis.set("zb_file_id", msg.document.file_id) + else: + await message.reply_photo(photo=redis.get('zb_file_id').decode(), quote=True) + else: + await message.reply_photo(photo=f'{getcwd()}{sep}assets{sep}images{sep}zb.png', quote=True) diff --git a/plugins/char_adv.py b/plugins/char_adv.py new file mode 100644 index 0000000..5acfd68 --- /dev/null +++ b/plugins/char_adv.py @@ -0,0 +1,28 @@ +from pyrogram import Client +from pyrogram.types import Message +from traceback import print_exc +from defs.char_adv import char_adv, weapon_adv + + +async def send_char_adv(client: Client, message: Message): + try: + name = message.text + for i in ["用什么", "能用啥", "怎么养"]: + name = name.replace(i, "").strip() + im = await char_adv(name) + await message.reply(im, quote=True) + except Exception as e: + print("获取建议失败。") + print_exc() + + +async def send_weapon_adv(client: Client, message: Message): + try: + name = message.text + for i in ["能给谁", "给谁用", "要给谁", "谁能用"]: + name = name.replace(i, "").strip() + im = await weapon_adv(name) + await message.reply(im, quote=True) + except Exception as e: + print("获取建议失败。") + print_exc() diff --git a/plugins/mys2.py b/plugins/mys2.py index cabfed3..c5844c4 100644 --- a/plugins/mys2.py +++ b/plugins/mys2.py @@ -15,6 +15,8 @@ from defs.mihoyo import draw_pic as draw_pic_2 from ci import scheduler, app, admin_id from defs.redis_load import redis +from defs.spiral_abyss import draw_abyss_pic, draw_abyss0_pic +from defs.spiral_abyss_text import get_user_abyss SUPERUSERS = [admin_id] @@ -195,26 +197,89 @@ async def mys2_qun_msg(client: Client, message: Message): elif "uid" in text: try: uid = re.findall(r"\d+", text)[0] # str + m = ''.join(re.findall('[\u4e00-\u9fa5]', text)) except IndexError: return await message.reply("uid格式错误!") - try: + nickname = message.from_user.first_name + nickname = nickname if len(nickname) < 10 else (nickname[:10] + "...") + if m == "深渊": try: - nickname = message.from_user.first_name - nickname = nickname if len(nickname) < 10 else (nickname[:10] + "...") - if is_chinese(uid): - im = await draw_pic(uid, message, nickname=nickname, mode=2) + if len(re.findall(r"\d+", text)) == 2: + floor_num = re.findall(r"\d+", text)[1] + im = await draw_abyss_pic(uid, nickname, floor_num) + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply_photo(im) else: - im = await draw_pic_2(uid, message, nickname=nickname, mode=2) - if im.find(".") != -1: - await message.reply_photo(im) - else: - await message.reply(im) + im = await draw_abyss0_pic(uid, nickname) + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply_photo(im) + except TypeError: + await message.reply("获取失败,可能是Cookies失效或者未打开米游社角色详情开关。") + print("上期深渊数据获取失败(Cookie失效/不公开信息)") + traceback.print_exc() except Exception as e: await message.reply("获取失败,有可能是数据状态有问题,\n{}\n请检查后台输出。".format(e)) + print("上期深渊数据获取失败(数据状态问题)") traceback.print_exc() - except Exception as e: - traceback.print_exc() - await message.reply("发生错误 {},请检查后台输出。".format(e)) + elif m == "上期深渊": + try: + if len(re.findall(r"\d+", text)) == 2: + floor_num = re.findall(r"\d+", text)[1] + im = await draw_abyss_pic(uid, nickname, floor_num, None, 2, "2") + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply_photo(im) + else: + im = await draw_abyss0_pic(uid, nickname, None, 2, "2") + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply_photo(im) + except TypeError: + await message.reply("获取失败,可能是Cookies失效或者未打开米游社角色详情开关。") + print("上期深渊数据获取失败(Cookie失效/不公开信息)") + traceback.print_exc() + except Exception as e: + await message.reply("获取失败,有可能是数据状态有问题,\n{}\n请检查后台输出。".format(e)) + print("上期深渊数据获取失败(数据状态问题)") + traceback.print_exc() + elif m == "文本深渊": + try: + im = await get_user_abyss(uid, 2, "2") + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply(im) + except TypeError: + await message.reply("获取失败,可能是Cookies失效或者未打开米游社角色详情开关。") + print("上期深渊数据获取失败(Cookie失效/不公开信息)") + traceback.print_exc() + except Exception as e: + await message.reply("获取失败,有可能是数据状态有问题,\n{}\n请检查后台输出。".format(e)) + print("上期深渊数据获取失败(数据状态问题)") + traceback.print_exc() + else: + try: + try: + if is_chinese(uid): + im = await draw_pic(uid, message, nickname=nickname, mode=2) + else: + im = await draw_pic_2(uid, message, nickname=nickname, mode=2) + if im.find(".") != -1: + await message.reply_photo(im) + else: + await message.reply(im) + except Exception as e: + await message.reply("获取失败,有可能是数据状态有问题,\n{}\n请检查后台输出。".format(e)) + traceback.print_exc() + except Exception as e: + traceback.print_exc() + await message.reply("发生错误 {},请检查后台输出。".format(e)) elif "查询" in text: try: at = message.reply_to_message @@ -227,7 +292,68 @@ async def mys2_qun_msg(client: Client, message: Message): uid = await selectDB(message.from_user.id) nickname = nickname if len(nickname) < 10 else (nickname[:10] + "...") if uid: - if "词云" in text: + if "深渊" in text and "上期" not in text and "文本" not in text: + try: + if len(re.findall(r"\d+", text)) == 1: + floor_num = re.findall(r"\d+", text)[0] + im = await draw_abyss_pic(uid[0], nickname, floor_num, None, uid[1]) + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply_photo(im) + else: + im = await draw_abyss0_pic(uid[0], nickname, None, uid[1]) + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply_photo(im) + except TypeError: + await message.reply("获取失败,可能是Cookies失效或者未打开米游社角色详情开关。") + print("上期深渊数据获取失败(Cookie失效/不公开信息)") + traceback.print_exc() + except Exception as e: + await message.reply("获取失败,有可能是数据状态有问题,\n{}\n请检查后台输出。".format(e)) + print("上期深渊数据获取失败(数据状态问题)") + traceback.print_exc() + elif "深渊" in text and "文本" not in text: + try: + if len(re.findall(r"\d+", text)) == 1: + floor_num = re.findall(r"\d+", text)[0] + im = await draw_abyss_pic(uid[0], nickname, floor_num, None, uid[1], "2") + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply_photo(im) + else: + im = await draw_abyss0_pic(uid[0], nickname, None, uid[1], "2") + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply_photo(im) + except TypeError: + await message.reply("获取失败,可能是Cookies失效或者未打开米游社角色详情开关。") + print("上期深渊数据获取失败(Cookie失效/不公开信息)") + traceback.print_exc() + except Exception as e: + await message.reply("获取失败,有可能是数据状态有问题,\n{}\n请检查后台输出。".format(e)) + print("上期深渊数据获取失败(数据状态问题)") + traceback.print_exc() + elif "深渊" in text: + try: + im = await get_user_abyss(uid[0], uid[1], "2") + if not im: + await message.reply("未查找到该用户的深渊信息。") + else: + await message.reply(im) + except TypeError: + await message.reply("获取失败,可能是Cookies失效或者未打开米游社角色详情开关。") + print("上期深渊数据获取失败(Cookie失效/不公开信息)") + traceback.print_exc() + except Exception as e: + await message.reply("获取失败,有可能是数据状态有问题,\n{}\n请检查后台输出。".format(e)) + print("上期深渊数据获取失败(数据状态问题)") + traceback.print_exc() + elif "词云" in text: try: im = await draw_wordcloud(uid[0], message, uid[1]) if im.find(".jpg") != -1: @@ -300,7 +426,8 @@ async def push(): if i['gid'] == "on": await app.send_message(int(i['qid']), i['message']) else: - await app.send_message(int(i['gid']), f"[NOTICE {i['qid']}](tg://user?id={i['qid']})" + "\n" + i['message']) + await app.send_message(int(i['gid']), + f"[NOTICE {i['qid']}](tg://user?id={i['qid']})" + "\n" + i['message']) else: pass diff --git a/plugins/process.py b/plugins/process.py index 081a7eb..1150eaf 100644 --- a/plugins/process.py +++ b/plugins/process.py @@ -24,6 +24,7 @@ from plugins.foods import foods_msg from plugins.artifacts import artifacts_msg from plugins.artifact_rate import artifact_rate_msg from plugins.query_resource_points import inquire_resource_points, inquire_resource_list +from plugins.char_adv import send_char_adv, send_weapon_adv from plugins.mys import mys_msg, promote_command from defs.inline_query_result_cached_media import InlineQueryResultCachedDocument @@ -115,6 +116,14 @@ async def process_private_msg(client: Client, message: Message): await mys2_msg(client, message) if 'hoyolab' in message.text: await mihoyo_msg(client, message) + for i in ["用什么", "能用啥", "怎么养"]: + if i in message.text: + await send_char_adv(client, message) + break + for i in ["能给谁", "给谁用", "要给谁", "谁能用"]: + if i in message.text: + await send_weapon_adv(client, message) + break # 账号信息(cookie 过期过快 不推荐启用) # if '账号信息' in message.text or '用户信息' in message.text: # await mys_msg(client, message) @@ -199,6 +208,15 @@ async def process_group_msg(client: Client, message: Message): await guess_voice(client, message) if text.startswith("抽卡"): await gacha_msg(client, message) + # 建议 + for i in ["用什么", "能用啥", "怎么养"]: + if i in message.text: + await send_char_adv(client, message) + break + for i in ["能给谁", "给谁用", "要给谁", "谁能用"]: + if i in message.text: + await send_weapon_adv(client, message) + break # 处理猜语音游戏 await process_guess(client, message) diff --git a/plugins/start.py b/plugins/start.py index 4035712..0d5cb6e 100644 --- a/plugins/start.py +++ b/plugins/start.py @@ -2,7 +2,8 @@ from ci import admin_id from pyrogram import Client from pyrogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton -HELP_MSG_PRE = 'PaimonBot 0.3.8beta By Xtao-Labs\n\n' \ +HELP_MSG_PRE = 'PaimonBot ' \ + '0.4.0beta By Xtao-Labs\n\n' \ '🔅 以下是小派蒙我学会了的功能(部分):\n' HELP_MSG = """① [武器/今日武器] 查看今日武器材料和武器 ② [天赋/今日天赋] 查看今日天赋材料和角色 @@ -36,7 +37,9 @@ HELP_MSG = """① [武器/今日武器] 查看今日武器材料和武器 (17) [猜语音] 和群友一起玩猜语音小游戏吧!(群聊) 💠 猜语音 💠 猜语音 无尽模式 -(18) [米游社/hoyolab] 米游社/hoyolab相关功能 +(18) [怎么养 (角色名)] 输出角色适用武器&圣遗物 +(19) [给谁用 (武器名)] 输出武器适用角色 +(20) [米游社/hoyolab] 米游社/hoyolab相关功能 💠 点击查看""" diff --git a/requirements.txt b/requirements.txt index 370ee6e..7185fef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,4 @@ apscheduler>=3.8.1 wordcloud>=1.8.1 numpy sqlitedict +openpyxl>=3.0.9