♻️ Abyss overview show all data

This commit is contained in:
xtaodada 2024-12-15 16:00:29 +08:00
parent db7aec6f84
commit 9b385cc995
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
4 changed files with 87 additions and 230 deletions

View File

@ -1,12 +1,9 @@
"""防卫战数据查询""" """防卫战数据查询"""
import asyncio
import math import math
import re
from functools import lru_cache, partial from functools import lru_cache, partial
from typing import Any, List, Optional, Tuple, Union, TYPE_CHECKING from typing import List, Optional, Tuple, TYPE_CHECKING
from arkowrapper import ArkoWrapper
from simnet.models.zzz.chronicle.challenge import ZZZChallenge from simnet.models.zzz.chronicle.challenge import ZZZChallenge
from telegram import Message, Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram import Message, Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.constants import ChatAction, ParseMode from telegram.constants import ChatAction, ParseMode
@ -17,7 +14,7 @@ from core.plugin import Plugin, handler
from core.services.cookies.error import TooManyRequestPublicCookies from core.services.cookies.error import TooManyRequestPublicCookies
from core.services.history_data.models import HistoryDataAbyss from core.services.history_data.models import HistoryDataAbyss
from core.services.history_data.services import HistoryDataAbyssServices from core.services.history_data.services import HistoryDataAbyssServices
from core.services.template.models import RenderGroupResult, RenderResult from core.services.template.models import RenderResult
from core.services.template.services import TemplateService from core.services.template.services import TemplateService
from gram_core.config import config from gram_core.config import config
from gram_core.dependence.redisdb import RedisDB from gram_core.dependence.redisdb import RedisDB
@ -44,18 +41,9 @@ MAX_STARS = MAX_FLOOR * 3
@lru_cache @lru_cache
def get_args(text: str) -> Tuple[int, bool, bool]: def get_args(text: str) -> bool:
if text.startswith("/"): prev = "pre" in text or "上期" in text
result = re.match(cmd_pattern, text).groups() return prev
try:
floor = int(result[0] or 0)
if floor > 100:
floor = 0
except ValueError:
floor = 0
return floor, result[0] == "all", bool(result[1])
result = re.match(msg_pattern, text).groups()
return int(result[2] or 0), result[0] == "总览", result[1] == "上期"
class AbyssUnlocked(Exception): class AbyssUnlocked(Exception):
@ -103,10 +91,11 @@ class ChallengePlugin(Plugin):
@handler.command("challenge", block=False) @handler.command("challenge", block=False)
@handler.message(filters.Regex(msg_pattern), block=False) @handler.message(filters.Regex(msg_pattern), block=False)
async def command_start(self, update: Update, _: CallbackContext) -> None: async def command_start(self, update: Update, context: CallbackContext) -> None:
user_id = await self.get_real_user_id(update) user_id = await self.get_real_user_id(update)
message = update.effective_message
uid, offset = self.get_real_uid_or_offset(update) uid, offset = self.get_real_uid_or_offset(update)
args = self.get_args(context)
message = update.effective_message
uid: int = await self.get_uid(user_id, message.reply_to_message, uid, offset) uid: int = await self.get_uid(user_id, message.reply_to_message, uid, offset)
# 若查询帮助 # 若查询帮助
@ -124,22 +113,13 @@ class ChallengePlugin(Plugin):
return return
# 解析参数 # 解析参数
floor, total, previous = get_args(message.text) previous = get_args(" ".join([i for i in args if not i.startswith("@")]))
if floor > MAX_FLOOR or floor < 0:
reply_msg = await message.reply_text(f"防卫战层数输入错误,请重新输入。支持的参数为: 1-{MAX_FLOOR} 或 all")
if filters.ChatType.GROUPS.filter(message):
self.add_delete_message_job(reply_msg)
self.add_delete_message_job(message)
return
self.log_user( self.log_user(
update, update,
logger.info, logger.info,
"[bold]防卫战挑战数据[/bold]请求: uid=%s floor=%s total=%s previous=%s", "[bold]防卫战挑战数据[/bold]请求: uid=%s previous=%s",
uid, uid,
floor,
total,
previous, previous,
extra={"markup": True}, extra={"markup": True},
) )
@ -151,13 +131,12 @@ class ChallengePlugin(Plugin):
try: try:
async with self.helper.genshin_or_public(user_id, uid=uid) as client: async with self.helper.genshin_or_public(user_id, uid=uid) as client:
if total: reply_text = await message.reply_text(
reply_text = await message.reply_text( f"{config.notice.bot_name} 需要时间整理防卫战数据,还请耐心等待哦~"
f"{config.notice.bot_name} 需要时间整理防卫战数据,还请耐心等待哦~" )
)
await message.reply_chat_action(ChatAction.TYPING) await message.reply_chat_action(ChatAction.TYPING)
abyss_data = await self.get_rendered_pic_data(client, uid, previous) abyss_data = await self.get_rendered_pic_data(client, uid, previous)
images = await self.get_rendered_pic(abyss_data, uid, floor, total) images = await self.get_rendered_pic(abyss_data, uid)
except TooManyRequestPublicCookies: except TooManyRequestPublicCookies:
reply_message = await message.reply_text("查询次数太多,请您稍后重试") reply_message = await message.reply_text("查询次数太多,请您稍后重试")
if filters.ChatType.GROUPS.filter(message): if filters.ChatType.GROUPS.filter(message):
@ -178,18 +157,12 @@ class ChallengePlugin(Plugin):
await reply_message_func("UID 输入错误,请重新输入") await reply_message_func("UID 输入错误,请重新输入")
return return
raise e raise e
if images is None:
await reply_message_func(f"还没有第 {floor} 层的挑战数据")
return
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
await images.reply_photo(message)
for group in ArkoWrapper(images).group(10): # 每 10 张图片分一个组
await RenderGroupResult(results=group).reply_media_group(message, write_timeout=60)
if reply_text is not None: if reply_text is not None:
await reply_text.delete() await reply_text.delete()
self.log_user(update, logger.info, "[bold]防卫战挑战数据[/bold]: 成功发送图片", extra={"markup": True}) self.log_user(update, logger.info, "[bold]防卫战挑战数据[/bold]: 成功发送图片", extra={"markup": True})
def get_floor_data(self, abyss_data: "ZZZChallenge", floor: int): def get_floor_data(self, abyss_data: "ZZZChallenge", floor: int):
@ -234,26 +207,16 @@ class ChallengePlugin(Plugin):
return f"{int(hours)}{int(minutes)}{int(sec)}" return f"{int(hours)}{int(minutes)}{int(sec)}"
async def get_rendered_pic( # skipcq: PY-R1000 # async def get_rendered_pic( # skipcq: PY-R1000 #
self, abyss_data: "ZZZChallenge", uid: int, floor: int, total: bool self,
) -> Union[ abyss_data: "ZZZChallenge",
Tuple[ uid: int,
Union[BaseException, Any], ) -> RenderResult:
Union[BaseException, Any],
Union[BaseException, Any],
Union[BaseException, Any],
Union[BaseException, Any],
],
List[RenderResult],
None,
]:
""" """
获取渲染后的图片 获取渲染后的图片
Args: Args:
abyss_data (ZZZChallenge): 防卫战数据 abyss_data (ZZZChallenge): 防卫战数据
uid (int): 需要查询的 uid uid (int): 需要查询的 uid
floor (int): 层数
total (bool): 是否为总览
Returns: Returns:
bytes格式的图片 bytes格式的图片
@ -291,57 +254,22 @@ class ChallengePlugin(Plugin):
}, },
} }
overview = await self.template_service.render( floors_data = []
"zzz/abyss/overview.html", render_data, viewport={"width": 750, "height": 250} floors = abyss_data.floors[::-1]
for i in range(len(floors)):
try:
floors_data.append(self.get_floor_data(abyss_data, i + 1))
except AbyssFastPassed:
pass
render_data["floors"] = floors_data
return await self.template_service.render(
"zzz/abyss/overview.html",
render_data,
viewport={"width": 2745, "height": 4000},
query_selector=".container",
) )
if total:
def floor_task(floor_index: int):
_abyss_data = self.get_floor_data(abyss_data, floor_index)
return (
floor_index,
self.template_service.render(
"zzz/abyss/floor.html",
{
**render_data,
**_abyss_data,
},
viewport={"width": 690, "height": 500},
full_page=True,
ttl=15 * 24 * 60 * 60,
),
)
render_inputs = []
floors = abyss_data.floors[::-1]
for i, f in enumerate(floors):
try:
render_inputs.append(floor_task(i + 1))
except AbyssFastPassed:
pass
render_group_inputs = list(map(lambda x: x[1], sorted(render_inputs, key=lambda x: x[0])))
render_group_outputs = await asyncio.gather(*render_group_inputs)
render_group_outputs.insert(0, overview)
return render_group_outputs
if floor < 1:
return [overview]
try:
floor_data = abyss_data.floors[-floor]
except IndexError:
return None
if not floor_data:
return None
render_data.update(self.get_floor_data(abyss_data, floor))
return [
await self.template_service.render(
"zzz/abyss/floor.html", render_data, viewport={"width": 690, "height": 500}
)
]
@staticmethod @staticmethod
async def save_abyss_data( async def save_abyss_data(
history_data_abyss: "HistoryDataAbyssServices", uid: int, abyss_data: "ZZZChallenge" history_data_abyss: "HistoryDataAbyssServices", uid: int, abyss_data: "ZZZChallenge"
@ -444,39 +372,6 @@ class ChallengePlugin(Plugin):
send_buttons.append(last_button) send_buttons.append(last_button)
return send_buttons return send_buttons
@staticmethod
async def gen_floor_button(
data_id: int,
abyss_data: "HistoryDataAbyss",
user_id: int,
uid: int,
) -> List[List[InlineKeyboardButton]]:
max_floors = len(abyss_data.abyss_data.floors)
buttons = [
InlineKeyboardButton(
f"{i + 1}",
callback_data=f"get_abyss_history|{user_id}|{uid}|{data_id}|{i + 1}",
)
for i in range(max_floors)
]
send_buttons = [buttons[i : i + 4] for i in range(0, len(buttons), 4)]
all_buttons = [
InlineKeyboardButton(
"<< 返回",
callback_data=f"get_abyss_history|{user_id}|{uid}|p_1",
),
InlineKeyboardButton(
"总览",
callback_data=f"get_abyss_history|{user_id}|{uid}|{data_id}|total",
),
InlineKeyboardButton(
"所有",
callback_data=f"get_abyss_history|{user_id}|{uid}|{data_id}|all",
),
]
send_buttons.append(all_buttons)
return send_buttons
@handler.command("challenge_history", block=False) @handler.command("challenge_history", block=False)
@handler.message(filters.Regex(r"^防卫战历史数据"), block=False) @handler.message(filters.Regex(r"^防卫战历史数据"), block=False)
async def abyss_history_command_start(self, update: Update, _: CallbackContext) -> None: async def abyss_history_command_start(self, update: Update, _: CallbackContext) -> None:
@ -509,23 +404,7 @@ class ChallengePlugin(Plugin):
await callback_query.edit_message_reply_markup(reply_markup=InlineKeyboardMarkup(buttons)) await callback_query.edit_message_reply_markup(reply_markup=InlineKeyboardMarkup(buttons))
await callback_query.answer(f"已切换到第 {page}", show_alert=False) await callback_query.answer(f"已切换到第 {page}", show_alert=False)
async def get_abyss_history_season(self, update: "Update", data_id: int): async def get_abyss_history_floor(self, update: "Update", data_id: int):
"""进入选择层数"""
callback_query = update.callback_query
user = callback_query.from_user
self.log_user(update, logger.info, "切换防卫战历史数据到层数页 data_id[%s]", data_id)
data = await self.history_data_abyss.get_by_id(data_id)
if not data:
await callback_query.answer("数据不存在,请尝试重新发送命令~", show_alert=True)
await callback_query.edit_message_text("数据不存在,请尝试重新发送命令~")
return
abyss_data = HistoryDataAbyss.from_data(data)
buttons = await self.gen_floor_button(data_id, abyss_data, user.id, data.user_id)
await callback_query.edit_message_reply_markup(reply_markup=InlineKeyboardMarkup(buttons))
await callback_query.answer("已切换到层数页", show_alert=False)
async def get_abyss_history_floor(self, update: "Update", data_id: int, detail: str):
"""渲染层数数据""" """渲染层数数据"""
callback_query = update.callback_query callback_query = update.callback_query
message = callback_query.message message = callback_query.message
@ -533,14 +412,6 @@ class ChallengePlugin(Plugin):
if message.reply_to_message: if message.reply_to_message:
reply = message.reply_to_message reply = message.reply_to_message
floor = 0
total = False
if detail == "total":
floor = 0
elif detail == "all":
total = True
else:
floor = int(detail)
data = await self.history_data_abyss.get_by_id(data_id) data = await self.history_data_abyss.get_by_id(data_id)
if not data: if not data:
await callback_query.answer("数据不存在,请尝试重新发送命令", show_alert=True) await callback_query.answer("数据不存在,请尝试重新发送命令", show_alert=True)
@ -548,16 +419,13 @@ class ChallengePlugin(Plugin):
return return
abyss_data = HistoryDataAbyss.from_data(data) abyss_data = HistoryDataAbyss.from_data(data)
images = await self.get_rendered_pic(abyss_data.abyss_data, data.user_id, floor, total)
if images is None:
await callback_query.answer(f"还没有第 {floor} 层的挑战数据", show_alert=True)
return
await callback_query.answer("正在渲染图片中 请稍等 请不要重复点击按钮", show_alert=False) await callback_query.answer("正在渲染图片中 请稍等 请不要重复点击按钮", show_alert=False)
images = await self.get_rendered_pic(abyss_data.abyss_data, data.user_id)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
for group in ArkoWrapper(images).group(10): # 每 10 张图片分一个组 await images.reply_photo(reply or message)
await RenderGroupResult(results=group).reply_media_group(reply or message, write_timeout=60)
self.log_user(update, logger.info, "[bold]防卫战挑战数据[/bold]: 成功发送图片", extra={"markup": True}) self.log_user(update, logger.info, "[bold]防卫战挑战数据[/bold]: 成功发送图片", extra={"markup": True})
self.add_delete_message_job(message, delay=1) self.add_delete_message_job(message, delay=1)
@ -568,22 +436,20 @@ class ChallengePlugin(Plugin):
async def get_abyss_history_callback( async def get_abyss_history_callback(
callback_query_data: str, callback_query_data: str,
) -> Tuple[str, str, int, int]: ) -> Tuple[str, int, int]:
_data = callback_query_data.split("|") _data = callback_query_data.split("|")
_user_id = int(_data[1]) _user_id = int(_data[1])
_uid = int(_data[2]) _uid = int(_data[2])
_result = _data[3] _result = _data[3]
_detail = _data[4] if len(_data) > 4 else None
logger.debug( logger.debug(
"callback_query_data函数返回 detail[%s] result[%s] user_id[%s] uid[%s]", "callback_query_data函数返回 result[%s] user_id[%s] uid[%s]",
_detail,
_result, _result,
_user_id, _user_id,
_uid, _uid,
) )
return _detail, _result, _user_id, _uid return _result, _user_id, _uid
detail, result, user_id, uid = await get_abyss_history_callback(callback_query.data) result, user_id, uid = await get_abyss_history_callback(callback_query.data)
if user.id != user_id: if user.id != user_id:
await callback_query.answer(text="这不是你的按钮!\n" + config.notice.user_mismatch, show_alert=True) await callback_query.answer(text="这不是你的按钮!\n" + config.notice.user_mismatch, show_alert=True)
return return
@ -594,10 +460,7 @@ class ChallengePlugin(Plugin):
await self.get_abyss_history_page(update, user_id, uid, result) await self.get_abyss_history_page(update, user_id, uid, result)
return return
data_id = int(result) data_id = int(result)
if detail: await self.get_abyss_history_floor(update, data_id)
await self.get_abyss_history_floor(update, data_id, detail)
return
await self.get_abyss_history_season(update, data_id)
async def abyss_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE", previous: bool): async def abyss_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE", previous: bool):
callback_query = update.callback_query callback_query = update.callback_query
@ -612,8 +475,7 @@ class ChallengePlugin(Plugin):
if not client.public: if not client.public:
await client.get_record_cards() await client.get_record_cards()
abyss_data = await self.get_rendered_pic_data(client, uid, previous) abyss_data = await self.get_rendered_pic_data(client, uid, previous)
images = await self.get_rendered_pic(abyss_data, uid, 0, False) image = await self.get_rendered_pic(abyss_data, uid)
image = images[0]
except AbyssUnlocked: # 若深渊未解锁 except AbyssUnlocked: # 若深渊未解锁
notice = "还未解锁防卫战哦~" notice = "还未解锁防卫战哦~"
except TooManyRequestPublicCookies: except TooManyRequestPublicCookies:

View File

@ -1,59 +1,31 @@
<!DOCTYPE html> <div class="floors">
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>floor</title>
<link type="text/css" href="./style.css" rel="stylesheet"/>
<link type="text/css" href="../../styles/public.css" rel="stylesheet"/>
<style>
body {
margin: 0;
padding: 0;
}
.floors, .floor {
border-radius: unset;
margin: 0;
}
.floor-num > div:last-child {
display: flex;
flex-flow: column;
justify-content: center;
align-content: center;
}
</style>
</head>
<body>
<div class="container">
<div class="floors">
<div class="floor floor-abyss"> <div class="floor floor-abyss">
<div class="head"> <div class="head">
<div class="floor-name"> <div class="floor-name">
<div class="floor-num"></div> <div class="floor_num"></div>
<div> <div>
<div>UID: {{ uid }}</div> <div>UID: {{ uid }}</div>
<div>{{ title }}•{{ floor.zone_name }}</div> <div>{{ title }}•{{ data.floor.zone_name }}</div>
</div> </div>
</div> </div>
<div class="star star{{ floor.rating }}"></div> <div class="star star{{ data.floor.rating }}"></div>
</div> </div>
<div class="hr"></div> <div class="hr"></div>
<div class="chamber"> <div class="chamber">
<div class="chamber-info"> <div class="chamber-info">
<div> <div>
<span style="color: white;">{{ floor_time }}</span> <span style="color: white;">{{ data.floor_time }}</span>
</div> </div>
</div> </div>
<div class="battles"> <div class="battles">
{% for node in floor_nodes %} {% for node in data.floor_nodes %}
<div class="battle"> <div class="battle">
{% for character in node.avatars %} {% for character in node.avatars %}
<div class="character"> <div class="character">
<div class="element" style="background-image: url('../../img/attribute/{{ character.element_type }}.png')"></div> <div class="element" style="background-image: url('../../img/attribute/{{ character.element_type }}.png')"></div>
<div class="icon" <div class="icon"
style="background-image: url('../../background/rarity/half/{{ character.rarity }}.png')"> style="background-image: url('../../background/rarity/half/{{ character.rarity }}.png')">
<img src="{{ character_icons[character.id] }}" alt=""/> <img src="{{ data.character_icons[character.id] }}" alt=""/>
</div> </div>
<div class="caption">Lv.{{ character.level }}</div> <div class="caption">Lv.{{ character.level }}</div>
</div> </div>
@ -63,7 +35,7 @@
<div class="character"> <div class="character">
<div class="icon" <div class="icon"
style="background-image: url('../../background/rarity/half/{{ character.rarity }}.png')"> style="background-image: url('../../background/rarity/half/{{ character.rarity }}.png')">
<img src="{{ buddy_icons[character.id] }}" alt=""/> <img src="{{ data.buddy_icons[character.id] }}" alt=""/>
</div> </div>
<div class="caption">Lv.{{ character.level }}</div> <div class="caption">Lv.{{ character.level }}</div>
</div> </div>
@ -76,7 +48,4 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
</body>
</html>

View File

@ -42,6 +42,15 @@
</div> </div>
</div> </div>
</div> </div>
{% for data in floors %}
{% if loop.index % 4 == 1 %}
<div class="floors_row">
{% endif %}
{% include "zzz/abyss/floor.jinja2" %}
{% if loop.index % 4 == 0 or loop.index == loop.length %}
</div>
{% endif %}
{% endfor %}
</div> </div>
</body> </body>
</html> </html>

View File

@ -19,20 +19,18 @@ body {
} }
.container { .container {
width: 750px;
position: relative; position: relative;
filter: drop-shadow(2px 2px 5px rgb(0 0 0 /70%)); filter: drop-shadow(2px 2px 5px rgb(0 0 0 /70%));
} }
.container2 { .container2 {
width: 850px;
position: relative; position: relative;
filter: drop-shadow(2px 2px 5px rgb(0 0 0 /70%)); filter: drop-shadow(2px 2px 5px rgb(0 0 0 /70%));
} }
.title { .title {
text-align: center; text-align: center;
font-size: 27px; font-size: 30px;
font-weight: bold; font-weight: bold;
color: var(--h-color); color: var(--h-color);
} }
@ -46,8 +44,7 @@ body {
/* 概览 */ /* 概览 */
.overview { .overview {
/*height: 250px;*/ padding: 20px 540px;
padding: 20px 40px;
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
@ -61,9 +58,8 @@ body {
} }
.summarize { .summarize {
font-size: 20px; font-size: 30px;
width: calc(100% - 90px); width: calc(100% - 90px);
height: calc(150px - 20px);
margin: 20px 0; margin: 20px 0;
padding: 10px 0 10px 80px; padding: 10px 0 10px 80px;
border-radius: 5px; border-radius: 5px;
@ -241,6 +237,7 @@ body {
} }
.rank { .rank {
font-size: 30px;
flex: 1; flex: 1;
position: relative; position: relative;
display: flex; display: flex;
@ -295,12 +292,12 @@ body {
color: var(--white); color: var(--white);
} }
.floor-name > div:not(.floor-num) { .floor-name > div:not(.floor_num) {
margin-left: 20px; margin-left: 20px;
text-shadow: 1px 1px 5px rgb(0 0 0/50%); text-shadow: 1px 1px 5px rgb(0 0 0/50%);
} }
.floor-num { .floor_num {
color: black; color: black;
width: 100px; width: 100px;
height: 100px; height: 100px;
@ -434,3 +431,23 @@ body {
.buffs > .buff-item > p > .buff-item-name { .buffs > .buff-item > p > .buff-item-name {
color: #f9c87e; color: #f9c87e;
} }
.floors_row {
display: flex;
}
.floors {
flex: 1;
}
.floors, .floor {
border-radius: unset;
margin: 0;
}
.floor_num > div:last-child {
display: flex;
flex-flow: column;
justify-content: center;
align-content: center;
}