Skip to content

Commit

Permalink
✨ 新增幻想真境剧诗
Browse files Browse the repository at this point in the history
  • Loading branch information
KimigaiiWuyi committed Jul 1, 2024
1 parent 9d81565 commit 48bdf7b
Show file tree
Hide file tree
Showing 16 changed files with 371 additions and 6 deletions.
31 changes: 31 additions & 0 deletions GenshinUID/genshinuid_poetry_abyss/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import re

from gsuid_core.sv import SV
from gsuid_core.bot import Bot
from gsuid_core.models import Event
from gsuid_core.utils.error_reply import UID_HINT

from ..utils.convert import get_uid
from .draw_poetry_abyss import draw_poetry_abyss_img

sv_poetry_abyss = SV('查询幻想真境剧诗')


@sv_poetry_abyss.on_command(
('查询幻想真境剧诗', '幻想真境剧诗', '新深渊', '查询新深渊', '真剧诗'),
block=True,
)
async def send_poetry_abyss_info(bot: Bot, ev: Event):
name = ''.join(re.findall('[\u4e00-\u9fa5]', ev.text))
if name:
return

await bot.logger.info('开始执行[幻想真境剧诗]')
uid, user_id = await get_uid(bot, ev, True)
if uid is None:
return await bot.send(UID_HINT)
await bot.logger.info('[幻想真境剧诗]uid: {}'.format(uid))

im = await draw_poetry_abyss_img(uid, ev)

await bot.send(im)
212 changes: 212 additions & 0 deletions GenshinUID/genshinuid_poetry_abyss/draw_poetry_abyss.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
from pathlib import Path
from datetime import datetime
from typing import List, Union

from PIL import Image, ImageDraw
from gsuid_core.models import Event
from gsuid_core.utils.error_reply import get_error
from gsuid_core.utils.image.image_tools import get_avatar_with_ring

from ..utils.mys_api import mys_api
from ..utils.colors import first_color
from ..utils.image.convert import convert_img
from ..utils.image.image_tools import add_footer
from ..utils.resource.download_url import download_file
from ..utils.resource.RESOURCE_PATH import ICON_PATH, CHAR_CARD_PATH
from ..utils.resource.generate_char_card import create_single_char_card
from ..utils.fonts.genshin_fonts import (
gs_font_20,
gs_font_28,
gs_font_36,
gs_font_40,
gs_font_50,
)

TEXT_PATH = Path(__file__).parent / 'texture2d'
DIFFICULTY_MAP = {
1: '简单模式',
2: '普通模式',
3: '困难模式',
}


def timestamp_to_str(timestamp: float):
dt = datetime.fromtimestamp(timestamp)
return dt.strftime('%Y.%m.%d %H:%M:%S')


async def draw_buff(
data: List,
startb: int,
stage: Image.Image,
stage_draw: ImageDraw.ImageDraw,
_type: str,
):
if _type == '神秘收获':
y = 206
else:
y = 253

for index_choice, choice in enumerate(data):
name = f'{choice["name"]}.png'
path = ICON_PATH / name
if not path.exists():
await download_file(choice['icon'], 8, name)
icon = Image.open(path).resize((45, 45)).convert('RGBA')
buff = Image.open(TEXT_PATH / 'buff.png')
buff.paste(icon, (-3, -4), icon)
stage.paste(buff, (startb + index_choice * 44, y), buff)

if not data:
stage_draw.text(
(startb, y + 21), f'暂无{_type}', (68, 59, 45), gs_font_20, 'lm'
)


async def draw_poetry_abyss_img(uid: str, ev: Event) -> Union[str, bytes]:
data = await mys_api.get_poetry_abyss_data(uid)
if isinstance(data, int):
return get_error(data)

if not data['is_unlock'] or not data['data']:
return '[幻想真境剧诗] 你还没有解锁该模式!'

data = data['data'][0]
round_data = data['detail']['rounds_data']
stat_data = data['stat']

start_time = timestamp_to_str(float(data['schedule']['start_time']))
end_time = timestamp_to_str(float(data['schedule']['end_time']))

w, h = 1000, 1040 + 50 + (len(round_data) // 2) * 330

img = Image.new('RGBA', (w, h), (22, 18, 20))

title = Image.open(TEXT_PATH / 'title.png').convert('RGBA')
status = Image.open(TEXT_PATH / 'status.png').convert('RGBA')
title_draw = ImageDraw.Draw(title)
status_draw = ImageDraw.Draw(status)

avatar = await get_avatar_with_ring(ev, 278)
title.paste(avatar, (361, 115), avatar)
title_draw.text((500, 473), f'UID {uid}', 'white', gs_font_36, 'mm')

difficulty = DIFFICULTY_MAP.get(stat_data['difficulty_id'], '困难模式')
max_round_id = stat_data['max_round_id']
is_gold = (
True
if stat_data['difficulty_id'] == 3 and max_round_id == 8
else False
)
time = f'{start_time} ~ {end_time}'
avatar_bonus = stat_data['avatar_bonus_num']
rent_cnt = stat_data['rent_cnt']
coin_num = stat_data['coin_num']

if is_gold:
status_icon = Image.open(TEXT_PATH / 'yes.png')
else:
status_icon = Image.open(TEXT_PATH / 'no.png')

status_icon = status_icon.convert('RGBA')
status.paste(status_icon, (560, 111), status_icon)
status_draw.text((462, 132), difficulty, (236, 233, 229), gs_font_36, 'mm')
status_draw.text((500, 189), time, (139, 137, 133), gs_font_20, 'mm')

status_draw.text(
(220, 398), f'第{max_round_id}幕', (68, 59, 45), gs_font_50, 'mm'
)
status_draw.text(
(416, 398), f'{avatar_bonus}', (68, 59, 45), gs_font_50, 'mm'
)
status_draw.text((604, 398), f'{rent_cnt}', (68, 59, 45), gs_font_50, 'mm')
status_draw.text((793, 398), f'{coin_num}', (68, 59, 45), gs_font_50, 'mm')

flower_yes = Image.open(TEXT_PATH / 'flower_yes.png').convert('RGBA')
flower_no = Image.open(TEXT_PATH / 'flower_no.png').convert('RGBA')
medals = stat_data['get_medal_round_list']

while len(medals) < 8:
medals.append(0)

for index, medal in enumerate(medals):
flower = flower_yes if medal else flower_no
status.paste(flower, (449 + 42 * index, 270), flower)

img.paste(title, (0, 0), title)
img.paste(status, (0, 474), status)

for i, r in enumerate(round_data):
is_get_medal = r['is_get_medal']
round_id = r['round_id']
_medal = flower_yes if is_get_medal else flower_no

if i % 2 == 0:
stage = Image.open(TEXT_PATH / 'stage.png')
stage_draw = ImageDraw.Draw(stage)
stage_draw.text(
(160, 54), f'第{round_id}幕', (68, 59, 45), gs_font_28, 'lm'
)
stage.paste(_medal, (97, 32), _medal)
startx = 92
startb = 116
else:
stage_draw.text(
(830, 54), f'第{round_id}幕', (68, 59, 45), gs_font_28, 'rm'
)
stage.paste(_medal, (866, 32), _medal)
startx = 520
startb = 552

for index_char, char in enumerate(r['avatars']):
char_id = char['avatar_id']
char_type = char['avatar_type']
char_pic_path = CHAR_CARD_PATH / f'{char_id}.png'
# 不存在自动下载
if not char_pic_path.exists():
await create_single_char_card(char_id)
char_card = Image.open(char_pic_path).convert('RGBA')
char_card_draw = ImageDraw.Draw(char_card)
char_card_draw.text(
(128, 280),
f'Lv.{char["level"]}',
font=gs_font_40,
fill=first_color,
anchor='mm',
)
char_card = char_card.resize((89, 108), Image.Resampling.LANCZOS)
stage.paste(
char_card,
(startx + 97 * index_char, 88),
char_card,
)
if char_type != 1:
char_tag = Image.open(TEXT_PATH / f'{char_type}.png')
stage.paste(
char_tag,
(startx + 16 + 97 * index_char, 75),
char_tag,
)

await draw_buff(
r['choice_cards'],
startb,
stage,
stage_draw,
'神秘收获',
)
await draw_buff(
r['buffs'],
startb,
stage,
stage_draw,
'奇妙助益',
)

if i % 2 == 1:
img.paste(stage, (0, 1040 + 330 * (i // 2)), stage)

img = add_footer(img, 1000)

res = await convert_img(img)
return res
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions GenshinUID/utils/api/mys/api.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
base_url = 'https://api-takumi-record.mihoyo.com/'
widget_url = f'{base_url}game_record/genshin/aapi/widget/v2'
base_url = 'https://api-takumi-record.mihoyo.com'
widget_url = f'{base_url}/game_record/genshin/aapi/widget/v2'
new_abyss_url = f'{base_url}/game_record/app/genshin/api/role_combat'
94 changes: 94 additions & 0 deletions GenshinUID/utils/api/mys/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,97 @@ class FakeResin(WidgetResin):
transformer: Transformer
daily_task: DayilyTask
archon_quest_progress: ArchonProgress


class PoetryAbyssLinks(TypedDict):
lineup_link: str
lineup_link_pc: str
strategy_link: str
lineup_publish_link: str
lineup_publish_link_pc: str


class PoetryAbyssAvatar(TypedDict):
avatar_id: int
avatar_type: int
name: str
element: str
image: str
level: int
rarity: int


class PoetryAbyssChoiceCard(TypedDict):
icon: str
name: str
desc: str
is_enhanced: bool
id: int


class PoetryAbyssBuff(TypedDict):
icon: str
name: str
desc: str
is_enhanced: bool
id: int


class PoetryAbyssDateTime(TypedDict):
year: int
month: int
day: int
hour: int
minute: int
second: int


class PoetryAbyssSchedule(TypedDict):
start_time: int
end_time: int
schedule_type: int
schedule_id: int
start_date_time: PoetryAbyssDateTime
end_date_time: PoetryAbyssDateTime


class PoetryAbyssDetailStat(TypedDict):
difficulty_id: int
max_round_id: int
heraldry: int
get_medal_round_list: List[int]
medal_num: int
coin_num: int
avatar_bonus_num: int
rent_cnt: int


class RoundData(TypedDict):
avatars: List[PoetryAbyssAvatar]
choice_cards: List[PoetryAbyssChoiceCard]
buffs: List[PoetryAbyssBuff]
is_get_medal: bool
round_id: int
finish_time: int
finish_date_time: PoetryAbyssDateTime
detail_stat: PoetryAbyssDetailStat


class PoetryAbyssDetail(TypedDict):
rounds_data: List[RoundData]
detail_stat: PoetryAbyssDetailStat
backup_avatars: List[PoetryAbyssAvatar]


class PoetryAbyssData(TypedDict):
detail: PoetryAbyssDetail
stat: PoetryAbyssDetailStat
schedule: PoetryAbyssSchedule
has_data: bool
has_detail_data: bool


class PoetryAbyssDatas(TypedDict):
data: List[PoetryAbyssData]
is_unlock: bool
links: PoetryAbyssLinks
33 changes: 30 additions & 3 deletions GenshinUID/utils/api/mys/mys_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from typing import Dict, Union, cast

from gsuid_core.utils.api.mys_api import _MysApi
from gsuid_core.utils.api.mys.tools import get_web_ds_token
from gsuid_core.utils.api.mys.tools import get_ds_token, get_web_ds_token

from .api import widget_url
from .models import WidgetResin
from .api import widget_url, new_abyss_url
from .models import WidgetResin, PoetryAbyssDatas


class GsMysAPI(_MysApi):
Expand All @@ -28,3 +28,30 @@ async def get_widget_resin_data(self, uid: str) -> Union[WidgetResin, int]:
if isinstance(data, Dict):
data = cast(WidgetResin, data['data'])
return data

async def get_poetry_abyss_data(
self, uid: str
) -> Union[PoetryAbyssDatas, int]:
server_id = self.RECOGNIZE_SERVER.get(uid[0])
HEADER = deepcopy(self._HEADER)
ck = await self.get_ck(uid)
if ck is None:
return -51
HEADER['Cookie'] = ck
params = {
'server': server_id,
'role_id': uid,
'need_detail': True,
}
HEADER['DS'] = get_ds_token(
'&'.join([f'{k}={v}' for k, v in params.items()])
)
data = await self._mys_request(
new_abyss_url,
'GET',
HEADER,
params,
)
if isinstance(data, Dict):
data = cast(PoetryAbyssDatas, data['data'])
return data
2 changes: 1 addition & 1 deletion GenshinUID/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
GenshinUID_version = '4.7.1'
GenshinUID_version = '4.7.2'
Genshin_version = '4.7.0'

0 comments on commit 48bdf7b

Please sign in to comment.