diff --git a/core/services/history_data/services.py b/core/services/history_data/services.py index daee952b..94057c50 100644 --- a/core/services/history_data/services.py +++ b/core/services/history_data/services.py @@ -18,6 +18,7 @@ except ImportError: __all__ = ( "HistoryDataBaseServices", "HistoryDataAbyssServices", + "HistoryDataLedgerServices", ) TZ = timezone("Asia/Shanghai") diff --git a/plugins/genshin/abyss.py b/plugins/genshin/abyss.py index 4131af64..31096d22 100644 --- a/plugins/genshin/abyss.py +++ b/plugins/genshin/abyss.py @@ -185,7 +185,7 @@ class AbyssPlugin(Plugin): avatars = await client.get_genshin_characters(uid, lang="zh-cn") avatar_data = {i.id: i.constellation for i in avatars} if abyss_data.unlocked and abyss_data.ranks.most_kills: - await self.save_abyss_data(uid, abyss_data, avatar_data) + await self.save_abyss_data(self.history_data_abyss, uid, abyss_data, avatar_data) return abyss_data, avatar_data async def get_rendered_pic( # skipcq: PY-R1000 # @@ -319,12 +319,20 @@ class AbyssPlugin(Plugin): ) ] - async def save_abyss_data(self, uid: int, abyss_data: "SpiralAbyss", character_data: Dict[int, int]): - model = self.history_data_abyss.create(uid, abyss_data, character_data) - old_data = await self.history_data_abyss.get_by_user_id_data_id(uid, model.data_id) - exists = self.history_data_abyss.exists_data(model, old_data) + @staticmethod + async def save_abyss_data( + history_data_abyss: "HistoryDataAbyssServices", + uid: int, + abyss_data: "SpiralAbyss", + character_data: Dict[int, int], + ) -> bool: + model = history_data_abyss.create(uid, abyss_data, character_data) + old_data = await history_data_abyss.get_by_user_id_data_id(uid, model.data_id) + exists = history_data_abyss.exists_data(model, old_data) if not exists: - await self.history_data_abyss.add(model) + await history_data_abyss.add(model) + return True + return False async def get_abyss_data(self, uid: int): return await self.history_data_abyss.get_by_user_id(uid) diff --git a/plugins/genshin/ledger.py b/plugins/genshin/ledger.py index 6b3cd3f1..14abe736 100644 --- a/plugins/genshin/ledger.py +++ b/plugins/genshin/ledger.py @@ -53,7 +53,7 @@ class LedgerPlugin(Plugin): async def _start_get_ledger(self, client: "GenshinClient", month=None) -> RenderResult: diary_info = await client.get_genshin_diary(client.player_id, month=month) if month: - await self.save_ledger_data(client.player_id, diary_info) + 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: "Diary") -> RenderResult: @@ -146,14 +146,19 @@ class LedgerPlugin(Plugin): await message.reply_chat_action(ChatAction.UPLOAD_PHOTO) await render_result.reply_photo(message, filename=f"{client.player_id}.png", allow_sending_without_reply=True) - async def save_ledger_data(self, uid: int, ledger_data: "Diary"): + @staticmethod + async def save_ledger_data( + history_data_ledger: "HistoryDataLedgerServices", uid: int, ledger_data: "Diary" + ) -> bool: month = int(ledger_data.date.split("-")[1]) if month == ledger_data.month: - return - model = self.history_data_ledger.create(uid, ledger_data) - old_data = await self.history_data_ledger.get_by_user_id_data_id(uid, model.data_id) + 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 self.history_data_ledger.add(model) + 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) diff --git a/plugins/jobs/refresh_history.py b/plugins/jobs/refresh_history.py new file mode 100644 index 00000000..2d547f2c --- /dev/null +++ b/plugins/jobs/refresh_history.py @@ -0,0 +1,131 @@ +import datetime +from asyncio import sleep +from typing import TYPE_CHECKING, List + +from simnet.errors import ( + TimedOut as SimnetTimedOut, + BadRequest as SimnetBadRequest, + InvalidCookies, +) +from telegram.constants import ParseMode +from telegram.error import BadRequest, Forbidden + +from core.plugin import Plugin, job +from core.services.history_data.services import HistoryDataAbyssServices, HistoryDataLedgerServices +from gram_core.basemodel import RegionEnum +from gram_core.services.cookies import CookiesService +from gram_core.services.cookies.models import CookiesStatusEnum +from plugins.genshin.abyss import AbyssPlugin +from plugins.genshin.ledger import LedgerPlugin +from plugins.tools.genshin import GenshinHelper, PlayerNotFoundError, CookiesNotFoundError +from utils.log import logger + +if TYPE_CHECKING: + from telegram.ext import ContextTypes + + from simnet import GenshinClient + +REGION = [RegionEnum.HYPERION, RegionEnum.HOYOLAB] +NOTICE_TEXT = """#### %s更新 #### +时间:%s (UTC+8) +UID: %s +结果: 新的%s已保存,可通过命令回顾""" + + +class RefreshHistoryJob(Plugin): + """历史记录定时刷新""" + + def __init__( + self, + cookies: CookiesService, + genshin_helper: GenshinHelper, + history_abyss: HistoryDataAbyssServices, + history_ledger: HistoryDataLedgerServices, + ): + self.cookies = cookies + self.genshin_helper = genshin_helper + self.history_data_abyss = history_abyss + self.history_data_ledger = history_ledger + + @staticmethod + async def send_notice(context: "ContextTypes.DEFAULT_TYPE", user_id: int, notice_text: str): + try: + await context.bot.send_message(user_id, notice_text, parse_mode=ParseMode.HTML) + except (BadRequest, Forbidden) as exc: + logger.error("执行自动刷新历史记录时发生错误 user_id[%s] Message[%s]", user_id, exc.message) + except Exception as exc: + logger.error("执行自动刷新历史记录时发生错误 user_id[%s]", user_id, exc_info=exc) + + async def save_abyss_data(self, client: "GenshinClient") -> bool: + uid = client.player_id + abyss_data = await client.get_genshin_spiral_abyss(uid, previous=False, lang="zh-cn") + avatars = await client.get_genshin_characters(uid, lang="zh-cn") + avatar_data = {i.id: i.constellation for i in avatars} + if abyss_data.unlocked and abyss_data.ranks and abyss_data.ranks.most_kills: + return await AbyssPlugin.save_abyss_data(self.history_data_abyss, uid, abyss_data, avatar_data) + return False + + async def send_abyss_notice(self, context: "ContextTypes.DEFAULT_TYPE", user_id: int, uid: int): + now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + notice_text = NOTICE_TEXT % ("深渊历史记录", now, uid, "挑战记录") + await self.send_notice(context, user_id, notice_text) + + async def _save_ledger_data(self, client: "GenshinClient", month: int) -> bool: + diary_info = await client.get_genshin_diary(client.player_id, month=month) + return await LedgerPlugin.save_ledger_data(self.history_data_ledger, client.player_id, diary_info) + + @staticmethod + def get_ledger_months() -> List[int]: + now = datetime.datetime.now() + now_time = (now - datetime.timedelta(days=1)) if now.day == 1 and now.hour <= 4 else now + months = [] + last_month = now_time.replace(day=1) - datetime.timedelta(days=1) + months.append(last_month.month) + + last_month = last_month.replace(day=1) - datetime.timedelta(days=1) + months.append(last_month.month) + return months + + async def save_ledger_data(self, client: "GenshinClient") -> bool: + months = self.get_ledger_months() + ok = False + for month in months: + if await self._save_ledger_data(client, month): + ok = True + return ok + + async def send_ledger_notice(self, context: "ContextTypes.DEFAULT_TYPE", user_id: int, uid: int): + now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + notice_text = NOTICE_TEXT % ("旅行札记历史记录", now, uid, "旅行札记历史记录") + await self.send_notice(context, user_id, notice_text) + + @job.run_daily(time=datetime.time(hour=6, minute=1, second=0), name="RefreshHistoryJob") + async def daily_refresh_history(self, context: "ContextTypes.DEFAULT_TYPE"): + logger.info("正在执行每日刷新历史记录任务") + for database_region in REGION: + for cookie_model in await self.cookies.get_all( + region=database_region, status=CookiesStatusEnum.STATUS_SUCCESS + ): + user_id = cookie_model.user_id + try: + async with self.genshin_helper.genshin(user_id) as client: + if await self.save_abyss_data(client): + await self.send_abyss_notice(context, user_id, client.player_id) + if await self.save_ledger_data(client): + await self.send_ledger_notice(context, user_id, client.player_id) + except (InvalidCookies, PlayerNotFoundError, CookiesNotFoundError): + continue + except SimnetBadRequest as exc: + logger.warning( + "用户 user_id[%s] 请求历史记录失败 [%s]%s", user_id, exc.ret_code, exc.original or exc.message + ) + continue + except SimnetTimedOut: + logger.info("用户 user_id[%s] 请求历史记录超时", user_id) + continue + except Exception as exc: + logger.error("执行自动刷新历史记录时发生错误 user_id[%s]", user_id, exc_info=exc) + continue + await sleep(1) + + logger.success("执行每日刷新历史记录任务完成")