MibooGram/plugins/zzz/agent_detail.py
2024-08-02 17:34:30 +08:00

313 lines
12 KiB
Python

import math
from typing import TYPE_CHECKING, List, Tuple, Optional, Union
from simnet.models.zzz.calculator import ZZZCalculatorCharacterDetails, ZZZCalculatorAvatarProperty
from telegram import (
InlineKeyboardButton,
InlineKeyboardMarkup,
)
from telegram.constants import ChatAction
from telegram.ext import filters
from core.config import config
from core.dependence.redisdb import RedisDB
from core.plugin import Plugin, handler
from core.services.template.services import TemplateService
from metadata.shortname import roleToName, idToRole
from plugins.tools.genshin import GenshinHelper
from plugins.tools.player_detail import PlayerDetailHelper, NeedClient
from utils.log import logger
from utils.uid import mask_number
if TYPE_CHECKING:
from simnet import ZZZClient
from telegram import Update
from telegram.ext import ContextTypes
from core.services.template.models import RenderResult
__all__ = ("AgentDetailPlugin",)
class ZZZCalculatorAvatarPropertyModify(ZZZCalculatorAvatarProperty):
@property
def icon(self) -> str:
return {
"生命值": "hp",
"攻击力": "atk",
"防御力": "def",
"冲击力": "impact",
"暴击率": "crit-rate",
"暴击伤害": "crit-dmg",
"异常掌控": "anomaly-proficiency",
"异常精通": "anomaly-mastery",
"穿透率": "pen-ratio",
"能量自动回复": "energy-regen",
}.get(self.property_name, "")
color_map = {
"1011": "#c8e16c",
"1021": "#a0351c",
"1031": "#e6adaa",
"1041": "#febb2e",
"1061": "#c8d7bd",
"1081": "#af3e3a",
"1101": "#de643d",
"1111": "#ddc374",
"1121": "#a68d73",
"1131": "#28bdcc",
"1141": "#d0d3e0",
"1151": "#e8cda2",
"1181": "#b75339",
"1191": "#c9becc",
"1211": "#c4c1b1",
"1241": "#4e7ebd",
"1281": "#e9d892",
}
class AgentDetailPlugin(Plugin):
"""角色详细信息查询"""
def __init__(
self,
helper: GenshinHelper,
template_service: TemplateService,
redis: RedisDB,
):
self.template_service = template_service
self.helper = helper
self.qname = "plugins:agent_detail"
self.redis = redis.client
self.expire = 15 * 60 # 15分钟
self.kitsune: Optional[str] = None
self.player_detail_helper = PlayerDetailHelper(helper, redis)
async def get_characters(
self, uid: int, client: "ZZZClient" = None
) -> Tuple[Optional[str], Optional["ZZZCalculatorCharacterDetails"]]:
return await self.player_detail_helper.get_characters(uid, client)
def parse_render_data(self, data: "ZZZCalculatorCharacterDetails", nickname: str, ch_id: int, uid: int):
char = None
for i in data.characters:
if i.id == ch_id:
char = i
break
props = [ZZZCalculatorAvatarPropertyModify(**i.dict()) for i in char.properties]
return {
"uid": mask_number(uid),
"char": char,
"props": props,
"color": color_map.get(str(char.id), "#010101"),
}
@staticmethod
def gen_button(
data: "ZZZCalculatorCharacterDetails",
user_id: Union[str, int],
uid: int,
page: int = 1,
) -> List[List[InlineKeyboardButton]]:
"""生成按钮"""
buttons = []
if data.characters:
buttons = [
InlineKeyboardButton(
idToRole(value.id),
callback_data=f"get_role_detail|{user_id}|{uid}|{idToRole(value.id)}",
)
for value in data.characters
if value.id
]
all_buttons = [buttons[i : i + 4] for i in range(0, len(buttons), 4)]
send_buttons = all_buttons[(page - 1) * 3 : page * 3]
last_page = page - 1 if page > 1 else 0
all_page = math.ceil(len(all_buttons) / 3)
next_page = page + 1 if page < all_page and all_page > 1 else 0
last_button = []
if last_page:
last_button.append(
InlineKeyboardButton(
"<< 上一页",
callback_data=f"get_role_detail|{user_id}|{uid}|{last_page}",
)
)
if last_page or next_page:
last_button.append(
InlineKeyboardButton(
f"{page}/{all_page}",
callback_data=f"get_role_detail|{user_id}|{uid}|empty_data",
)
)
if next_page:
last_button.append(
InlineKeyboardButton(
"下一页 >>",
callback_data=f"get_role_detail|{user_id}|{uid}|{next_page}",
)
)
if last_button:
send_buttons.append(last_button)
return send_buttons
async def get_render_result(
self, data: "ZZZCalculatorCharacterDetails", nickname: str, ch_id: int, uid: int
) -> "RenderResult":
final = self.parse_render_data(data, nickname, ch_id, uid)
return await self.template_service.render(
"zzz/agent_detail/agent_detail.html",
final,
{"width": 1024, "height": 1200},
query_selector=".shareContainer_2ZR-xeyd",
full_page=True,
)
@staticmethod
def get_caption(data: "ZZZCalculatorCharacterDetails", character_id: int) -> str:
tags = []
for character in data.characters:
if character.id == character_id:
tags.append(character.name)
tags.append(f"等级{character.level}")
tags.append(f"命座{character.rank}")
if weapon := character.weapon:
tags.append(weapon.name)
tags.append(f"武器等级{weapon.level}")
tags.append(f"{weapon.star}")
break
return "#" + " #".join(tags)
@handler.command(command="agent_detail", block=False)
@handler.message(filters=filters.Regex("^角色详细信息查询(.*)"), block=False)
async def command_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> None:
user_id = await self.get_real_user_id(update)
message = update.effective_message
uid, offset = self.get_real_uid_or_offset(update)
args = self.get_args(context)
ch_name = None
for i in args:
if i.startswith("@"):
continue
ch_name = roleToName(i)
if ch_name:
break
self.log_user(
update,
logger.info,
"角色详细信息查询命令请求 || character_name[%s]",
ch_name,
)
await message.reply_chat_action(ChatAction.TYPING)
async with self.helper.genshin(user_id, player_id=uid, offset=offset) as client:
nickname, data = await self.get_characters(client.player_id, client)
uid = client.player_id
if ch_name is None:
buttons = self.gen_button(data, user_id, uid)
if isinstance(self.kitsune, str):
photo = self.kitsune
else:
photo = open("resources/img/aaa.jpg", "rb")
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
reply_message = await message.reply_photo(
photo=photo,
caption=f"请选择你要查询的角色 - UID {uid}",
reply_markup=InlineKeyboardMarkup(buttons),
)
if reply_message.photo:
self.kitsune = reply_message.photo[-1].file_id
return
for characters in data.characters:
if idToRole(characters.id) == ch_name:
break
else:
await message.reply_text(f"未在游戏中找到 {ch_name} ,请检查角色是否存在,或者等待角色数据更新后重试")
return
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
render_result = await self.get_render_result(data, nickname, characters.id, client.player_id)
await render_result.reply_photo(
message,
filename=f"{client.player_id}.png",
reply_markup=self.get_custom_button(user_id, uid, characters.id),
caption=self.get_caption(data, characters.id),
)
@handler.callback_query(pattern=r"^get_role_detail\|", block=False)
async def get_role_detail(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> None:
callback_query = update.callback_query
user = callback_query.from_user
message = callback_query.message
async def get_role_detail_callback(
callback_query_data: str,
) -> Tuple[str, int, int]:
_data = callback_query_data.split("|")
_user_id = int(_data[1])
_uid = int(_data[2])
_result = _data[3]
logger.debug(
"callback_query_data函数返回 result[%s] user_id[%s] uid[%s]",
_result,
_user_id,
_uid,
)
return _result, _user_id, _uid
result, user_id, uid = await get_role_detail_callback(callback_query.data)
if user.id != user_id:
await callback_query.answer(text="这不是你的按钮!\n" + config.notice.user_mismatch, show_alert=True)
return
if result == "empty_data":
await callback_query.answer(text="此按钮不可用", show_alert=True)
return
page = 0
if result.isdigit():
page = int(result)
logger.info(
"用户 %s[%s] 角色详细信息查询命令请求 || page[%s] uid[%s]",
user.full_name,
user.id,
page,
uid,
)
else:
logger.info(
"用户 %s[%s] 角色详细信息查询命令请求 || character_name[%s] uid[%s]",
user.full_name,
user.id,
result,
uid,
)
try:
nickname, data = await self.get_characters(uid)
except NeedClient:
async with self.helper.genshin(user.id, player_id=uid) as client:
nickname, data = await self.get_characters(client.player_id, client)
if page:
buttons = self.gen_button(data, user.id, uid, page)
await message.edit_reply_markup(reply_markup=InlineKeyboardMarkup(buttons))
await callback_query.answer(f"已切换到第 {page}", show_alert=False)
return
for characters in data.characters:
if idToRole(characters.id) == result:
break
else:
await message.delete()
await callback_query.answer(
f"未在游戏中找到 {result} ,请检查角色是否存在,或者等待角色数据更新后重试",
show_alert=True,
)
return
await callback_query.answer(text="正在渲染图片中 请稍等 请不要重复点击按钮", show_alert=False)
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
render_result = await self.get_render_result(data, nickname, characters.id, uid)
render_result.filename = f"role_detail_{uid}_{result}.png"
render_result.caption = self.get_caption(data, characters.id)
await render_result.edit_media(message, reply_markup=self.get_custom_button(user.id, uid, characters.id))
@staticmethod
def get_custom_button(user_id: int, uid: int, char_id: int) -> Optional[InlineKeyboardMarkup]:
return None