From 993af24b758faede44fcabecf7390fd96b69aaad Mon Sep 17 00:00:00 2001 From: KimigaiiWuyi <444835641@qq.com> Date: Thu, 11 Apr 2024 05:24:58 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=96=B0=E5=A2=9E`ba=E5=AD=A6?= =?UTF-8?q?=E7=94=9F=E6=8E=92=E8=A1=8C`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bauid_info/draw_user_info_pic.py | 231 +++++++++--------- BlueArchiveUID/bauid_ranklist/__init__.py | 12 + .../bauid_ranklist/draw_rank_pic.py | 100 ++++++++ .../bauid_ranklist/texture2d/card.png | Bin 0 -> 11855 bytes BlueArchiveUID/utils/api/api.py | 3 +- BlueArchiveUID/utils/api/models.py | 53 +++- BlueArchiveUID/utils/api/request.py | 40 ++- BlueArchiveUID/utils/ba_map.py | 10 + 8 files changed, 322 insertions(+), 127 deletions(-) create mode 100644 BlueArchiveUID/bauid_ranklist/__init__.py create mode 100644 BlueArchiveUID/bauid_ranklist/draw_rank_pic.py create mode 100644 BlueArchiveUID/bauid_ranklist/texture2d/card.png diff --git a/BlueArchiveUID/bauid_info/draw_user_info_pic.py b/BlueArchiveUID/bauid_info/draw_user_info_pic.py index 2a08487..50a738c 100644 --- a/BlueArchiveUID/bauid_info/draw_user_info_pic.py +++ b/BlueArchiveUID/bauid_info/draw_user_info_pic.py @@ -13,6 +13,7 @@ from ..utils.ba_api import xtzx_api from ..utils.error_reply import get_error +from ..utils.api.models import AssistInfo, RankAssistInfo from ..utils.resource_path import ( SKILL_ICON_PATH, WEAPON_ICON_PATH, @@ -44,6 +45,118 @@ } +async def draw_assist_card(assist: Union[AssistInfo, RankAssistInfo]): + assist_card = Image.open(TEXT_PATH / 'assist_bg.png') + if assist: + assist_draw = ImageDraw.Draw(assist_card) + + student_id = assist['uniqueId'] + student_star = assist['starGrade'] + student_level = assist['level'] + student_star_pic = Image.open(TEXT_PATH / f'star{student_star}.png') + student_star_pic = student_star_pic.convert('RGBA') + student_name = studentId2Name[str(student_id)] + student_type = studentId2Type[str(student_id)] + + student_pic = Image.open( + STUDENT_COLLECTION_PATH / f'{student_id}.webp' + ) + student_color = COLOR_MAP[student_type] + student_color_pic = Image.new('RGBA', student_pic.size, student_color) + + favor_rank = assist['favorRank'] + ex = assist['exSkillLevel'] + nm = assist['publicSkillLevel'] + ps = assist['passiveSkillLevel'] + sub = assist['extraPassiveSkillLevel'] + + skill_data = { + 'ex': ex, + 'nm': nm, + 'ps': ps, + 'sub': sub, + } + for sindex, s in enumerate(skill_data): + skill_bg = Image.open(TEXT_PATH / f'{student_type}_skill_bg.png') + skill_draw = ImageDraw.Draw(skill_bg) + + skill_icon = studentSkill2Icon[str(student_id)][s] + skill_path = SKILL_ICON_PATH / f'{skill_icon}.webp' + skill_pic = Image.open(skill_path).resize((36, 38)) + + skill = skill_data[s] if skill_data[s] != 10 else 'M' + skill_color = GREY if skill != 'M' else student_color + + skill_bg.paste(skill_pic, (25, 21), skill_pic) + skill_draw.text( + (70, 40), f'等级{skill}', skill_color, cf(30), 'lm' + ) + assist_card.paste(skill_bg, (312 + 172 * sindex, 96), skill_bg) + + assist_card.paste(student_color_pic, (66, 108), student_pic) + assist_card.paste(student_pic, (68, 96), student_pic) + assist_card.paste(student_star_pic, (228, 390), student_star_pic) + assist_draw.text((146, 364), student_name, GREY, cf(32), 'mm') + assist_draw.text((251, 361), str(favor_rank), 'white', cf(25), 'mm') + assist_draw.text( + (151, 414), f'等级{student_level}', BLACK, cf(25), 'mm' + ) + + weapon_bg = Image.open(TEXT_PATH / 'weapon_bar.png') + if assist['weapon']: + weapon_draw = ImageDraw.Draw(weapon_bg) + + weapon_star = assist['weaponStartGrade'] + weapon_name = weaponId2Nmae[str(student_id)] + weapon_level = assist['weaponLevel'] + weapon_icon_id = studentId2weaponIcon[str(student_id)] + weapon_path = WEAPON_ICON_PATH / f'{weapon_icon_id}.webp' + weapon_icon = Image.open(weapon_path).resize((400, 102)) + + weapon_bg.paste(weapon_icon, (11, 42), weapon_icon) + weapon_draw.text( + (470, 70), f'等级{weapon_level}', BLACK, cf(25), 'mm' + ) + weapon_draw.text((486, 115), weapon_name, BLACK, cf(38), 'lm') + + for i in range(5): + if i < weapon_star: + star_pic = weapon_star_full + else: + star_pic = weapon_star_empty + weapon_bg.paste(star_pic, (227 + 29 * i, 110), star_pic) + + assist_card.paste(weapon_bg, (275, 147), weapon_bg) + + equip_fg = Image.open(TEXT_PATH / 'equip_fg.png') + for eindex, equip in enumerate(assist['equipment']): + equip_bg = Image.open(TEXT_PATH / 'equip_bg.png') + equip_id = equip['UniqueId'] + equip_icon = equipId2Icon[str(equip_id)] + equip_pic = Image.open(EQUIPMENT_ICON_PATH / f'{equip_icon}.webp') + equip_level = equip['Level'] + # equip_tier = equip['Tier'] + + equip_bg.paste(equip_pic, (2, 17), equip_pic) + equip_bg.paste(equip_fg, (0, 0), equip_fg) + equip_bg_draw = ImageDraw.Draw(equip_bg) + equip_bg_draw.text( + (75, 124), + f'等级{equip_level}', + 'white', + cf(24), + 'mm', + ) + assist_card.paste(equip_bg, (304 + eindex * 131, 306), equip_bg) + return assist_card + + +def get_bg(w: int, h: int): + img = crop_center_img(Image.open(TEXT_PATH / 'bg.jpg'), w, h) + img = img.convert('RGBA') + return img + + async def draw_user_info_img( _fcode: str, ev: Event, user_id: str ) -> Union[str, bytes]: @@ -54,8 +167,7 @@ async def draw_user_info_img( return get_error(data) w, h = 1100, 2880 - img = crop_center_img(Image.open(TEXT_PATH / 'bg.jpg'), w, h) - img = img.convert('RGBA') + img = get_bg(w, h) title = Image.open(TEXT_PATH / 'title.png') title_draw = ImageDraw.Draw(title) @@ -129,120 +241,7 @@ async def draw_user_info_img( _assist_list.append({}) for index, assist in enumerate(_assist_list): - assist_card = Image.open(TEXT_PATH / 'assist_bg.png') - if assist: - assist_draw = ImageDraw.Draw(assist_card) - - student_id = assist['uniqueId'] - student_star = assist['starGrade'] - student_level = assist['level'] - student_star_pic = Image.open( - TEXT_PATH / f'star{student_star}.png' - ) - student_star_pic = student_star_pic.convert('RGBA') - student_name = studentId2Name[str(student_id)] - student_type = studentId2Type[str(student_id)] - - student_pic = Image.open( - STUDENT_COLLECTION_PATH / f'{student_id}.webp' - ) - student_color = COLOR_MAP[student_type] - student_color_pic = Image.new( - 'RGBA', student_pic.size, student_color - ) - - favor_rank = assist['favorRank'] - ex = assist['exSkillLevel'] - nm = assist['publicSkillLevel'] - ps = assist['passiveSkillLevel'] - sub = assist['extraPassiveSkillLevel'] - - skill_data = { - 'ex': ex, - 'nm': nm, - 'ps': ps, - 'sub': sub, - } - for sindex, s in enumerate(skill_data): - skill_bg = Image.open( - TEXT_PATH / f'{student_type}_skill_bg.png' - ) - skill_draw = ImageDraw.Draw(skill_bg) - - skill_icon = studentSkill2Icon[str(student_id)][s] - skill_path = SKILL_ICON_PATH / f'{skill_icon}.webp' - skill_pic = Image.open(skill_path).resize((36, 38)) - - skill = skill_data[s] if skill_data[s] != 10 else 'M' - skill_color = GREY if skill != 'M' else student_color - - skill_bg.paste(skill_pic, (25, 21), skill_pic) - skill_draw.text( - (70, 40), f'等级{skill}', skill_color, cf(30), 'lm' - ) - assist_card.paste(skill_bg, (312 + 172 * sindex, 96), skill_bg) - - assist_card.paste(student_color_pic, (66, 108), student_pic) - assist_card.paste(student_pic, (68, 96), student_pic) - assist_card.paste(student_star_pic, (228, 390), student_star_pic) - assist_draw.text((146, 364), student_name, GREY, cf(32), 'mm') - assist_draw.text( - (251, 361), str(favor_rank), 'white', cf(25), 'mm' - ) - assist_draw.text( - (151, 414), f'等级{student_level}', BLACK, cf(25), 'mm' - ) - - weapon_bg = Image.open(TEXT_PATH / 'weapon_bar.png') - if assist['weapon']: - weapon_draw = ImageDraw.Draw(weapon_bg) - - weapon_star = assist['weaponStartGrade'] - weapon_name = weaponId2Nmae[str(student_id)] - weapon_level = assist['weaponLevel'] - weapon_icon_id = studentId2weaponIcon[str(student_id)] - weapon_path = WEAPON_ICON_PATH / f'{weapon_icon_id}.webp' - weapon_icon = Image.open(weapon_path).resize((400, 102)) - - weapon_bg.paste(weapon_icon, (11, 42), weapon_icon) - weapon_draw.text( - (470, 70), f'等级{weapon_level}', BLACK, cf(25), 'mm' - ) - weapon_draw.text((486, 115), weapon_name, BLACK, cf(38), 'lm') - - for i in range(5): - if i < weapon_star: - star_pic = weapon_star_full - else: - star_pic = weapon_star_empty - weapon_bg.paste(star_pic, (227 + 29 * i, 110), star_pic) - - assist_card.paste(weapon_bg, (275, 147), weapon_bg) - - equip_fg = Image.open(TEXT_PATH / 'equip_fg.png') - for eindex, equip in enumerate(assist['equipment']): - equip_bg = Image.open(TEXT_PATH / 'equip_bg.png') - equip_id = equip['UniqueId'] - equip_icon = equipId2Icon[str(equip_id)] - equip_pic = Image.open( - EQUIPMENT_ICON_PATH / f'{equip_icon}.webp' - ) - equip_level = equip['Level'] - # equip_tier = equip['Tier'] - - equip_bg.paste(equip_pic, (2, 17), equip_pic) - equip_bg.paste(equip_fg, (0, 0), equip_fg) - equip_bg_draw = ImageDraw.Draw(equip_bg) - equip_bg_draw.text( - (75, 124), - f'等级{equip_level}', - 'white', - cf(24), - 'mm', - ) - assist_card.paste( - equip_bg, (304 + eindex * 131, 306), equip_bg - ) + assist_card = await draw_assist_card(assist) x = 80 if index >= 2 else 0 img.paste(assist_card, (0, 884 + 450 * index + x), assist_card) diff --git a/BlueArchiveUID/bauid_ranklist/__init__.py b/BlueArchiveUID/bauid_ranklist/__init__.py new file mode 100644 index 0000000..c598630 --- /dev/null +++ b/BlueArchiveUID/bauid_ranklist/__init__.py @@ -0,0 +1,12 @@ +from gsuid_core.sv import SV +from gsuid_core.bot import Bot +from gsuid_core.models import Event + +from .draw_rank_pic import draw_rank_pic + +sv_ba_xtzx_rank = SV('BA什亭之匣学生排行榜') + + +@sv_ba_xtzx_rank.on_command(('ba学生排行')) +async def send_rank_msg(bot: Bot, ev: Event): + await bot.send(await draw_rank_pic(ev.text.strip())) diff --git a/BlueArchiveUID/bauid_ranklist/draw_rank_pic.py b/BlueArchiveUID/bauid_ranklist/draw_rank_pic.py new file mode 100644 index 0000000..e98417f --- /dev/null +++ b/BlueArchiveUID/bauid_ranklist/draw_rank_pic.py @@ -0,0 +1,100 @@ +from pathlib import Path +from typing import Union + +from PIL import Image, ImageDraw +from gsuid_core.utils.image.convert import convert_img +from gsuid_core.utils.fonts.fonts import core_font as cf + +from ..utils.ba_api import xtzx_api +from ..utils.error_reply import get_error +from ..utils.ba_map import student_name_to_id +from ..bauid_info.draw_user_info_pic import BLACK, get_bg, draw_assist_card + +TEXT_PATH = Path(__file__).parent / 'texture2d' + + +def get_color(rank_key: int): + if rank_key < 10: + rank_color = (235, 126, 163) + elif rank_key < 80: + rank_color = (235, 177, 129) + elif rank_key < 200: + rank_color = (151, 129, 235) + else: + rank_color = (202, 235, 129) + return rank_color + + +async def draw_rank_pic(student: str) -> Union[bytes, str]: + student_id = student_name_to_id(student) + if student_id == '9999': + return '要查询的角色不存在或别名未收录, 请尝试使用完整名字。' + data = await xtzx_api.get_xtzx_friend_ranking(1, student_id) + if isinstance(data, int): + return get_error(data) + teacher_data = data['records'] + + img = get_bg(1100, 2800) + for index, teacher in enumerate(teacher_data): + info = teacher['assistInfoList'][0] + assist_card = await draw_assist_card(info) + rank_key = info['baRank']['key'] + rank_value = info['baRank']['value'] + rank_str = f'{rank_key} / {rank_value}' + + global_rank_key = info['baGlobalRank']['key'] + global_rank_value = info['baGlobalRank']['value'] + global_rank_str = f'{global_rank_key} / {global_rank_value}' + + rank_color = get_color(rank_key) + global_rank_color = get_color(global_rank_key) + + card = Image.open(TEXT_PATH / 'card.png') + card_draw = ImageDraw.Draw(card) + + card_draw.text( + (230, 52), + f'{teacher["nickname"]}', + BLACK, + cf(40), + 'lm', + ) + + if teacher['server'] == 1: + s_f = (136, 205, 242) + s_t = '官服' + else: + s_f = (243, 143, 225) + s_t = 'B服' + + card_draw.rounded_rectangle((50, 36, 124, 68), 30, s_f) + card_draw.text( + (87, 52), + s_t, + BLACK, + cf(24), + 'mm', + ) + + card_draw.rounded_rectangle((632, 36, 780, 68), 30, rank_color) + card_draw.rounded_rectangle((904, 36, 1052, 68), 30, global_rank_color) + card_draw.text( + (706, 52), + rank_str, + BLACK, + cf(24), + 'mm', + ) + card_draw.text( + (978, 52), + global_rank_str, + BLACK, + cf(24), + 'mm', + ) + card.paste(assist_card, (0, 40), assist_card) + + img.paste(card, (0, 28 + index * 550), card) + + img = await convert_img(img) + return img diff --git a/BlueArchiveUID/bauid_ranklist/texture2d/card.png b/BlueArchiveUID/bauid_ranklist/texture2d/card.png new file mode 100644 index 0000000000000000000000000000000000000000..5820e93c3f88a01eb19eedbc20c01b1c51713dc7 GIT binary patch literal 11855 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 AU;qFB literal 0 HcmV?d00001 diff --git a/BlueArchiveUID/utils/api/api.py b/BlueArchiveUID/utils/api/api.py index 882b301..f552c25 100644 --- a/BlueArchiveUID/utils/api/api.py +++ b/BlueArchiveUID/utils/api/api.py @@ -16,4 +16,5 @@ XTZX_RAID_CHART = XTZX_API + '/raid/new/charts/{}?s={}' XTZX_FRIEND_DATA = XTZX_API + '/api/friends/find' XTZX_FRIEND_REFRESH = XTZX_API + '/api/friends/refresh' -XTZX_ASSIST = XTZX_API + 'api/friends/assist_query' +XTZX_ASSIST = XTZX_API + '/api/friends/assist_query' +XTZX_FRIEND_RANK = XTZX_API + '/api/friends/rank' diff --git a/BlueArchiveUID/utils/api/models.py b/BlueArchiveUID/utils/api/models.py index affc7b2..342953d 100644 --- a/BlueArchiveUID/utils/api/models.py +++ b/BlueArchiveUID/utils/api/models.py @@ -1,4 +1,4 @@ -from typing import List, TypedDict +from typing import Dict, List, TypedDict class FriendData(TypedDict): @@ -53,3 +53,54 @@ class Equipment(TypedDict): BoundCharacterServerId: int isNew: bool IsLocked: bool + + +class RankAssistInfo(TypedDict): + baRank: Dict[str, int] + baGlobalRank: Dict[str, int] + type: int + uniqueId: int + bulletType: str + tacticRole: str + echelonType: int + level: int + slotIndex: int + starGrade: int + favorRank: int + publicSkillLevel: int + exSkillLevel: int + passiveSkillLevel: int + extraPassiveSkillLevel: int + equipment: List[Equipment] + weapon: bool + weaponUniqueId: int + weaponType: int + weaponLevel: int + weaponStartGrade: int + + +class Record(TypedDict): + server: int + friendCode: str + friendCount: int + nickname: str + representCharacterUniqueId: int + clanName: str + comment: str + level: int + db: bool + lastHardCampaignClearStageId: int + lastNormalCampaignClearStageId: int + updateTime: int + maxFavorRank: int + echelonType: int + assistInfoList: List[RankAssistInfo] + + +class RankResp(TypedDict): + page: int + size: int + totalPages: int + totalData: int + records: List[Record] + lastPage: bool diff --git a/BlueArchiveUID/utils/api/request.py b/BlueArchiveUID/utils/api/request.py index 72025d9..40ec83e 100644 --- a/BlueArchiveUID/utils/api/request.py +++ b/BlueArchiveUID/utils/api/request.py @@ -3,8 +3,8 @@ from gsuid_core.logger import logger from aiohttp import FormData, TCPConnector, ClientSession, ContentTypeError -from .models import FriendData from ..ba_config import ba_config +from .models import RankResp, FriendData from .api import ( ARONA_URL, BATTLE_URL, @@ -13,6 +13,7 @@ XTZX_RAID_RANK, XTZX_RAID_CHART, XTZX_FRIEND_DATA, + XTZX_FRIEND_RANK, XTZX_RAID_CHART_PERSON, ) @@ -41,7 +42,7 @@ async def get_raid_ranking( async def _ba_request( self, url: str, - method: Literal["GET", "POST"] = "GET", + method: Literal['GET', 'POST'] = 'GET', header: Dict[str, str] = _HEADER, params: Optional[Dict[str, Any]] = None, json: Optional[Dict[str, Any]] = None, @@ -63,7 +64,7 @@ async def _ba_request( raw_data = await resp.json() except ContentTypeError: _raw_data = await resp.text() - raw_data = {"retcode": -999, "data": _raw_data} + raw_data = {'retcode': -999, 'data': _raw_data} logger.debug(raw_data) return raw_data @@ -97,8 +98,8 @@ async def get_xtzx_raid_chart_person( XTZX_RAID_CHART_PERSON, 'POST', json={ - "server": int(server_id), - "season": int(season), + 'server': int(server_id), + 'season': int(season), }, ) if ( @@ -186,8 +187,8 @@ async def get_xtzx_friend_data( XTZX_FRIEND_DATA, 'POST', json={ - "server": int(server_id), - "friend": friend_code, + 'server': int(server_id), + 'friend': friend_code, }, ) if isinstance(data, Dict) and 'code' in data: @@ -198,10 +199,31 @@ async def get_xtzx_friend_data( else: return -500 + async def get_xtzx_friend_ranking( + self, + page: int, + student_id: Union[str, int] = 10000, + ) -> Union[int, RankResp]: + data = await self._ba_request( + XTZX_FRIEND_RANK, + 'POST', + json={ + 'page': page, + 'studentId': student_id, + }, + ) + if isinstance(data, Dict) and 'code' in data: + if data['code'] == 200: + return cast(RankResp, data['data']) + else: + return data['code'] + else: + return -500 + async def _ba_request( self, url: str, - method: Literal["GET", "POST"] = "GET", + method: Literal['GET', 'POST'] = 'GET', header: Dict[str, str] = _HEADER, params: Optional[Dict[str, Any]] = None, json: Optional[Dict[str, Any]] = None, @@ -226,6 +248,6 @@ async def _ba_request( raw_data = await resp.json() except ContentTypeError: _raw_data = await resp.text() - raw_data = {"retcode": -999, "data": _raw_data} + raw_data = {'retcode': -999, 'data': _raw_data} logger.debug(raw_data) return raw_data diff --git a/BlueArchiveUID/utils/ba_map.py b/BlueArchiveUID/utils/ba_map.py index 18bd1f6..a160e78 100644 --- a/BlueArchiveUID/utils/ba_map.py +++ b/BlueArchiveUID/utils/ba_map.py @@ -2,6 +2,7 @@ from msgspec import json as msgjson +from .alias.name_convert import alias_to_char_name from ..tools.make_map import ( equipId2Icon_path, weaponId2Nmae_path, @@ -53,3 +54,12 @@ f.read(), type=Dict[str, Dict[str, str]], ) + + +def student_name_to_id(name: str) -> str: + name = alias_to_char_name(name) + for _id in studentId2Name: + if name in studentId2Name[_id]: + return _id + else: + return '9999'