mirror of
https://github.com/PaiGramTeam/MibooGram.git
synced 2024-12-04 20:47:27 +00:00
360 lines
15 KiB
Python
360 lines
15 KiB
Python
|
import math
|
||
|
import os
|
||
|
import re
|
||
|
from datetime import datetime, timedelta
|
||
|
from typing import TYPE_CHECKING, List, Tuple, Optional
|
||
|
|
||
|
from simnet.errors import BadRequest as SimnetBadRequest, DataNotPublic
|
||
|
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
|
||
|
from telegram.constants import ChatAction
|
||
|
from telegram.ext import filters, CallbackContext, ContextTypes
|
||
|
|
||
|
from core.plugin import Plugin, handler
|
||
|
from core.services.cookies import CookiesService
|
||
|
from core.services.history_data.models import HistoryDataLedger
|
||
|
from core.services.history_data.services import HistoryDataLedgerServices
|
||
|
from core.services.template.models import RenderResult
|
||
|
from core.services.template.services import TemplateService
|
||
|
from gram_core.config import config
|
||
|
from gram_core.dependence.redisdb import RedisDB
|
||
|
from gram_core.plugin.methods.inline_use_data import IInlineUseData
|
||
|
from plugins.tools.genshin import GenshinHelper
|
||
|
from utils.enkanetwork import RedisCache
|
||
|
from utils.log import logger
|
||
|
from utils.uid import mask_number
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from simnet import ZZZClient
|
||
|
from simnet.models.zzz.diary import ZZZDiary
|
||
|
|
||
|
|
||
|
__all__ = ("LedgerPlugin",)
|
||
|
|
||
|
|
||
|
class LedgerPlugin(Plugin):
|
||
|
"""绳网月报查询"""
|
||
|
|
||
|
def __init__(
|
||
|
self,
|
||
|
helper: GenshinHelper,
|
||
|
cookies_service: CookiesService,
|
||
|
template_service: TemplateService,
|
||
|
history_data_ledger: HistoryDataLedgerServices,
|
||
|
redis: RedisDB,
|
||
|
):
|
||
|
self.template_service = template_service
|
||
|
self.cookies_service = cookies_service
|
||
|
self.current_dir = os.getcwd()
|
||
|
self.helper = helper
|
||
|
self.history_data_ledger = history_data_ledger
|
||
|
self.cache = RedisCache(redis.client, key="plugin:ledger:history")
|
||
|
self.kitsune = None
|
||
|
|
||
|
async def _start_get_ledger(self, client: "ZZZClient", year, month) -> RenderResult:
|
||
|
req_month = f"{year}0{month}" if month < 10 else f"{year}{month}"
|
||
|
diary_info = await client.get_zzz_diary(client.player_id, month=req_month)
|
||
|
await self.save_ledger_data(self.history_data_ledger, client.player_id, diary_info)
|
||
|
return await self._start_get_ledger_render(client.player_id, diary_info)
|
||
|
|
||
|
async def _start_get_ledger_render(self, uid: int, diary_info: "ZZZDiary") -> RenderResult:
|
||
|
color = ["#73a9c6", "#d56565", "#70b2b4", "#bd9a5a", "#739970", "#7a6da7", "#597ea0"]
|
||
|
categories = [
|
||
|
{
|
||
|
"name": i.name,
|
||
|
"color": color[idx % len(color)],
|
||
|
"amount": i.amount,
|
||
|
"percentage": i.percentage,
|
||
|
}
|
||
|
for idx, i in enumerate(diary_info.month_data.categories)
|
||
|
]
|
||
|
color = [i["color"] for i in categories]
|
||
|
|
||
|
def format_amount(amount: int) -> str:
|
||
|
return f"{round(amount / 10000, 2)}w" if amount >= 10000 else amount
|
||
|
|
||
|
_amount_map = {i.id: i.amount for i in diary_info.month_data.list}
|
||
|
current_hcoin = _amount_map.get("PolychromesData", 0)
|
||
|
current_rails_pass = _amount_map.get("MatserTapeData", 0)
|
||
|
current_boo_pass = _amount_map.get("BooponsData", 0)
|
||
|
|
||
|
ledger_data = {
|
||
|
"uid": mask_number(uid),
|
||
|
"day": diary_info.month,
|
||
|
"current_hcoin": format_amount(current_hcoin),
|
||
|
"gacha": int(current_hcoin / 160),
|
||
|
"current_rails_pass": format_amount(current_rails_pass),
|
||
|
"current_boo_pass": format_amount(current_boo_pass),
|
||
|
"categories": categories,
|
||
|
"color": color,
|
||
|
"nickname": diary_info.role_info.nickname,
|
||
|
"avatar": diary_info.role_info.avatar,
|
||
|
}
|
||
|
render_result = await self.template_service.render(
|
||
|
"zzz/ledger/ledger.html", ledger_data, {"width": 640, "height": 610}
|
||
|
)
|
||
|
return render_result
|
||
|
|
||
|
@handler.command(command="ledger", cookie=True, block=False)
|
||
|
@handler.message(filters=filters.Regex("^绳网月报查询(.*)"), block=False)
|
||
|
async def command_start(self, update: Update, context: CallbackContext) -> None:
|
||
|
user_id = await self.get_real_user_id(update)
|
||
|
message = update.effective_message
|
||
|
uid, offset = self.get_real_uid_or_offset(update)
|
||
|
|
||
|
now = datetime.now()
|
||
|
now_time = (now - timedelta(days=1)) if now.day == 1 and now.hour <= 4 else now
|
||
|
month = now_time.month
|
||
|
try:
|
||
|
args = self.get_args(context)
|
||
|
if len(args) >= 1:
|
||
|
month = args[0].replace("月", "")
|
||
|
if re_data := re.findall(r"\d+", str(month)):
|
||
|
month = int(re_data[0])
|
||
|
else:
|
||
|
num_dict = {"一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "十": 10}
|
||
|
month = sum(num_dict.get(i, 0) for i in str(month))
|
||
|
# check right
|
||
|
allow_month_year = {now_time.month: now_time.year}
|
||
|
|
||
|
last_month = now_time.replace(day=1) - timedelta(days=1)
|
||
|
allow_month_year[last_month.month] = last_month.year
|
||
|
|
||
|
last_month = last_month.replace(day=1) - timedelta(days=1)
|
||
|
allow_month_year[last_month.month] = last_month.year
|
||
|
|
||
|
if (month not in allow_month_year) or (not isinstance(month, int)):
|
||
|
raise IndexError
|
||
|
year = allow_month_year[month]
|
||
|
except IndexError:
|
||
|
reply_message = await message.reply_text("仅可查询最新三月的数据,请重新输入")
|
||
|
if filters.ChatType.GROUPS.filter(message):
|
||
|
self.add_delete_message_job(reply_message, delay=30)
|
||
|
self.add_delete_message_job(message, delay=30)
|
||
|
return
|
||
|
self.log_user(update, logger.info, "查询绳网月报")
|
||
|
await message.reply_chat_action(ChatAction.TYPING)
|
||
|
try:
|
||
|
async with self.helper.genshin(user_id, player_id=uid, offset=offset) as client:
|
||
|
render_result = await self._start_get_ledger(client, year, month)
|
||
|
except DataNotPublic:
|
||
|
reply_message = await message.reply_text(
|
||
|
"查询失败惹,可能是绳网月报功能被禁用了?请先通过米游社或者 hoyolab 获取一次绳网月报后重试。"
|
||
|
)
|
||
|
if filters.ChatType.GROUPS.filter(message):
|
||
|
self.add_delete_message_job(reply_message, delay=30)
|
||
|
self.add_delete_message_job(message, delay=30)
|
||
|
return
|
||
|
except SimnetBadRequest as exc:
|
||
|
if exc.retcode == -120:
|
||
|
await message.reply_text("当前角色等级不足,暂时无法获取信息")
|
||
|
return
|
||
|
raise exc
|
||
|
await message.reply_chat_action(ChatAction.UPLOAD_PHOTO)
|
||
|
await render_result.reply_photo(message, filename=f"{client.player_id}.png")
|
||
|
|
||
|
@staticmethod
|
||
|
async def save_ledger_data(
|
||
|
history_data_ledger: "HistoryDataLedgerServices", uid: int, ledger_data: "ZZZDiary"
|
||
|
) -> bool:
|
||
|
if int(ledger_data.current_month) == ledger_data.month:
|
||
|
return False
|
||
|
model = history_data_ledger.create(uid, ledger_data)
|
||
|
old_data = await history_data_ledger.get_by_user_id_data_id(uid, model.data_id)
|
||
|
if not old_data:
|
||
|
await history_data_ledger.add(model)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
async def get_ledger_data(self, uid: int):
|
||
|
return await self.history_data_ledger.get_by_user_id(uid)
|
||
|
|
||
|
@staticmethod
|
||
|
def get_season_data_name(data: "HistoryDataLedger") -> str:
|
||
|
return f"{data.diary_data.data_id}"
|
||
|
|
||
|
async def get_session_button_data(self, user_id: int, uid: int, force: bool = False):
|
||
|
redis = await self.cache.get(str(uid))
|
||
|
if redis and not force:
|
||
|
return redis["buttons"]
|
||
|
data = await self.get_ledger_data(uid)
|
||
|
data.sort(key=lambda x: x.data_id, reverse=True)
|
||
|
abyss_data = [HistoryDataLedger.from_data(i) for i in data]
|
||
|
buttons = [
|
||
|
{
|
||
|
"name": LedgerPlugin.get_season_data_name(abyss_data[idx]),
|
||
|
"value": f"get_ledger_history|{user_id}|{uid}|{value.id}",
|
||
|
}
|
||
|
for idx, value in enumerate(data)
|
||
|
]
|
||
|
await self.cache.set(str(uid), {"buttons": buttons})
|
||
|
return buttons
|
||
|
|
||
|
async def gen_season_button(
|
||
|
self,
|
||
|
user_id: int,
|
||
|
uid: int,
|
||
|
page: int = 1,
|
||
|
) -> List[List[InlineKeyboardButton]]:
|
||
|
"""生成按钮"""
|
||
|
data = await self.get_session_button_data(user_id, uid)
|
||
|
if not data:
|
||
|
return []
|
||
|
buttons = [
|
||
|
InlineKeyboardButton(
|
||
|
value["name"],
|
||
|
callback_data=value["value"],
|
||
|
)
|
||
|
for value in data
|
||
|
]
|
||
|
all_buttons = [buttons[i : i + 3] for i in range(0, len(buttons), 3)]
|
||
|
send_buttons = all_buttons[(page - 1) * 5 : page * 5]
|
||
|
last_page = page - 1 if page > 1 else 0
|
||
|
all_page = math.ceil(len(all_buttons) / 5)
|
||
|
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_ledger_history|{user_id}|{uid}|p_{last_page}",
|
||
|
)
|
||
|
)
|
||
|
if last_page or next_page:
|
||
|
last_button.append(
|
||
|
InlineKeyboardButton(
|
||
|
f"{page}/{all_page}",
|
||
|
callback_data=f"get_ledger_history|{user_id}|{uid}|empty_data",
|
||
|
)
|
||
|
)
|
||
|
if next_page:
|
||
|
last_button.append(
|
||
|
InlineKeyboardButton(
|
||
|
"下一页 >>",
|
||
|
callback_data=f"get_ledger_history|{user_id}|{uid}|p_{next_page}",
|
||
|
)
|
||
|
)
|
||
|
if last_button:
|
||
|
send_buttons.append(last_button)
|
||
|
return send_buttons
|
||
|
|
||
|
@handler.command("ledger_history", cookie=True, block=False)
|
||
|
@handler.message(filters.Regex(r"^绳网月报历史数据"), block=False)
|
||
|
async def ledger_history_command_start(self, update: "Update", _: "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)
|
||
|
self.log_user(update, logger.info, "查询绳网月报历史数据")
|
||
|
|
||
|
async with self.helper.genshin(user_id, player_id=uid, offset=offset) as client:
|
||
|
await self.get_session_button_data(user_id, client.player_id, force=True)
|
||
|
buttons = await self.gen_season_button(user_id, client.player_id)
|
||
|
if not buttons:
|
||
|
await message.reply_text("还没有绳网月报历史数据哦~")
|
||
|
return
|
||
|
if isinstance(self.kitsune, str):
|
||
|
photo = self.kitsune
|
||
|
else:
|
||
|
photo = open("resources/img/aaa.jpg", "rb")
|
||
|
reply_message = await message.reply_photo(
|
||
|
photo, "请选择要查询的绳网月报历史数据", reply_markup=InlineKeyboardMarkup(buttons)
|
||
|
)
|
||
|
if reply_message.photo:
|
||
|
self.kitsune = reply_message.photo[-1].file_id
|
||
|
|
||
|
async def get_ledger_history_page(self, update: "Update", user_id: int, uid: int, result: str):
|
||
|
"""翻页处理"""
|
||
|
callback_query = update.callback_query
|
||
|
|
||
|
self.log_user(update, logger.info, "切换绳网月报历史数据页 page[%s]", result)
|
||
|
page = int(result.split("_")[1])
|
||
|
async with self.helper.genshin(user_id, player_id=uid) as client:
|
||
|
buttons = await self.gen_season_button(user_id, client.player_id, page)
|
||
|
if not buttons:
|
||
|
await callback_query.answer("还没有绳网月报历史数据哦~", show_alert=True)
|
||
|
await callback_query.edit_message_text("还没有绳网月报历史数据哦~")
|
||
|
return
|
||
|
await callback_query.edit_message_reply_markup(reply_markup=InlineKeyboardMarkup(buttons))
|
||
|
await callback_query.answer(f"已切换到第 {page} 页", show_alert=False)
|
||
|
|
||
|
@handler.callback_query(pattern=r"^get_ledger_history\|", block=False)
|
||
|
async def get_ledger_history(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> None:
|
||
|
callback_query = update.callback_query
|
||
|
message = callback_query.message
|
||
|
user = callback_query.from_user
|
||
|
|
||
|
async def get_ledger_history_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_ledger_history_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
|
||
|
if result.startswith("p_"):
|
||
|
await self.get_ledger_history_page(update, user_id, uid, result)
|
||
|
return
|
||
|
data_id = int(result)
|
||
|
data = await self.history_data_ledger.get_by_id(data_id)
|
||
|
if not data:
|
||
|
await callback_query.answer("数据不存在,请尝试重新发送命令", show_alert=True)
|
||
|
await callback_query.edit_message_text("数据不存在,请尝试重新发送命令~")
|
||
|
return
|
||
|
await callback_query.answer("正在渲染图片中 请稍等 请不要重复点击按钮")
|
||
|
render = await self._start_get_ledger_render(user_id, HistoryDataLedger.from_data(data).diary_data)
|
||
|
await render.edit_media(message)
|
||
|
|
||
|
async def ledger_use_by_inline(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"):
|
||
|
callback_query = update.callback_query
|
||
|
user = update.effective_user
|
||
|
user_id = user.id
|
||
|
uid = IInlineUseData.get_uid_from_context(context)
|
||
|
|
||
|
self.log_user(update, logger.info, "查询绳网月报")
|
||
|
now = datetime.now()
|
||
|
now_time = (now - timedelta(days=1)) if now.day == 1 and now.hour <= 4 else now
|
||
|
year, month = now_time.year, now_time.month
|
||
|
try:
|
||
|
async with self.helper.genshin(user_id, player_id=uid) as client:
|
||
|
render_result = await self._start_get_ledger(client, year, month)
|
||
|
except DataNotPublic:
|
||
|
await callback_query.answer(
|
||
|
"查询失败惹,可能是绳网月报功能被禁用了?请先通过米游社或者 hoyolab 获取一次绳网月报后重试。",
|
||
|
show_alert=True,
|
||
|
)
|
||
|
return
|
||
|
except SimnetBadRequest as exc:
|
||
|
if exc.ret_code == -120:
|
||
|
await callback_query.answer(
|
||
|
"当前角色等级不足,暂时无法获取信息",
|
||
|
show_alert=True,
|
||
|
)
|
||
|
return
|
||
|
raise exc
|
||
|
|
||
|
await render_result.edit_inline_media(callback_query)
|
||
|
|
||
|
async def get_inline_use_data(self) -> List[Optional[IInlineUseData]]:
|
||
|
return [
|
||
|
IInlineUseData(
|
||
|
text="当月绳网月报",
|
||
|
hash="ledger",
|
||
|
callback=self.ledger_use_by_inline,
|
||
|
cookie=True,
|
||
|
player=True,
|
||
|
)
|
||
|
]
|