diff --git a/alembic/versions/c6282bc5bf67_devices_valid.py b/alembic/versions/c6282bc5bf67_devices_valid.py new file mode 100644 index 0000000..1e2b6f4 --- /dev/null +++ b/alembic/versions/c6282bc5bf67_devices_valid.py @@ -0,0 +1,25 @@ +"""devices_valid +Revision ID: c6282bc5bf67 +Revises: 1df05b897d3f +Create Date: 2023-10-19 14:54:35.164497 +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = "c6282bc5bf67" +down_revision = "1df05b897d3f" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column("devices", sa.Column("is_valid", sa.Boolean(), nullable=False, server_default="1")) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("devices", "is_valid") + # ### end Alembic commands ### diff --git a/core/services/cookies/services.py b/core/services/cookies/services.py index 2aaad2e..2916cba 100644 --- a/core/services/cookies/services.py +++ b/core/services/cookies/services.py @@ -1,3 +1,5 @@ +from typing import Optional + from gram_core.base_service import BaseService from gram_core.basemodel import RegionEnum from gram_core.services.cookies.error import CookieServiceError @@ -17,11 +19,27 @@ __all__ = ("CookiesService", "PublicCookiesService") class PublicCookiesService(BaseService, BasePublicCookiesService): - async def check_public_cookie(self, region: RegionEnum, cookies: Cookies, public_id: int): + async def initialize(self) -> None: + logger.info("正在初始化公共Cookies池") + await self.refresh() + logger.success("刷新公共Cookies池成功") + + async def check_public_cookie(self, region: RegionEnum, cookies: Cookies, public_id: int): # skipcq: PY-R1000 # + device_id: Optional[str] = None + device_fp: Optional[str] = None + devices = await self.devices_repository.get(cookies.account_id) + if devices: + device_id = devices.device_id + device_fp = devices.device_fp + if region == RegionEnum.HYPERION: - client = StarRailClient(cookies=cookies.data, region=Region.CHINESE) + client = StarRailClient( + cookies=cookies.data, region=Region.CHINESE, device_id=device_id, device_fp=device_fp + ) elif region == RegionEnum.HOYOLAB: - client = StarRailClient(cookies=cookies.data, region=Region.OVERSEAS, lang="zh-cn") + client = StarRailClient( + cookies=cookies.data, region=Region.OVERSEAS, lang="zh-cn", device_id=device_id, device_fp=device_fp + ) else: raise CookieServiceError try: @@ -61,6 +79,7 @@ class PublicCookiesService(BaseService, BasePublicCookiesService): raise exc if exc.ret_code == 1034: logger.warning("用户 [%s] 触发验证", public_id) + await self.set_device_valid(client.account_id, False) else: logger.warning("用户 [%s] 获取账号信息发生错误,错误信息为", public_id) logger.exception(exc) diff --git a/gram_core b/gram_core index f599484..f18ce6d 160000 --- a/gram_core +++ b/gram_core @@ -1 +1 @@ -Subproject commit f5994842531164370fef6a34400d04b955c68d11 +Subproject commit f18ce6dd6da2bb54a375bbb3ab46f638af8a6e51 diff --git a/plugins/account/account.py b/plugins/account/account.py index 7966123..9189f3c 100644 --- a/plugins/account/account.py +++ b/plugins/account/account.py @@ -15,10 +15,10 @@ from telegram.helpers import escape_markdown from core.basemodel import RegionEnum from core.plugin import Plugin, conversation, handler from core.services.cookies.error import TooManyRequestPublicCookies -from core.services.cookies.models import CookiesStatusEnum from core.services.cookies.services import CookiesService, PublicCookiesService from core.services.players.models import PlayersDataBase as Player, PlayerInfoSQLModel from core.services.players.services import PlayersService, PlayerInfoService +from plugins.tools.genshin import GenshinHelper from utils.log import logger if TYPE_CHECKING: @@ -51,11 +51,13 @@ class BindAccountPlugin(Plugin.Conversation): cookies_service: CookiesService = None, player_info_service: PlayerInfoService = None, public_cookies_service: PublicCookiesService = None, + helper: GenshinHelper = None, ): self.public_cookies_service = public_cookies_service self.cookies_service = cookies_service self.players_service = players_service self.player_info_service = player_info_service + self.helper = helper @conversation.entry_point @handler.command(command="setuid", filters=filters.ChatType.PRIVATE, block=False) @@ -106,10 +108,10 @@ class BindAccountPlugin(Plugin.Conversation): await message.reply_text("退出任务", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END if message.text == "通过玩家ID": - await message.reply_text("请输入你的游戏ID(非通行证ID)", reply_markup=ReplyKeyboardRemove()) + await message.reply_text("请输入你的玩家ID(非通行证ID),此 ID 在 游戏客户端 左/右下角。", reply_markup=ReplyKeyboardRemove()) return CHECK_PLAYER_ID if message.text == "通过账号ID": - await message.reply_text("请输入你的通行证ID(非玩家ID)", reply_markup=ReplyKeyboardRemove()) + await message.reply_text("请输入你的通行证ID(非玩家ID),此 ID 在 社区APP '我的' 页面。", reply_markup=ReplyKeyboardRemove()) return CHECK_ACCOUNT_ID await message.reply_text("选择错误,请重新选择") return CHECK_METHOD @@ -192,18 +194,11 @@ class BindAccountPlugin(Plugin.Conversation): await message.reply_text("ID 格式有误,请检查", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END try: - cookies = await self.public_cookies_service.get_cookies(user.id, region) + async with self.helper.public_genshin(user.id, region=region, uid=player_id) as client: + player_stats = await client.get_starrail_user(player_id) except TooManyRequestPublicCookies: await message.reply_text("用户查询次数过多,请稍后重试", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END - if region == RegionEnum.HYPERION: - client = StarRailClient(cookies=cookies.data, region=Region.CHINESE, lang="zh-cn") - elif region == RegionEnum.HOYOLAB: - client = StarRailClient(cookies=cookies.data, region=Region.OVERSEAS, lang="zh-cn") - else: - return ConversationHandler.END - try: - player_stats = await client.get_starrail_user(player_id) except AccountNotFound: await message.reply_text("找不到用户,uid可能无效", reply_markup=ReplyKeyboardRemove()) logger.warning("获取账号信息发生错误 %s 找不到用户 uid可能无效", player_id) @@ -213,12 +208,11 @@ class BindAccountPlugin(Plugin.Conversation): logger.warning("获取账号信息发生错误 %s 账户信息未公开", player_id) return ConversationHandler.END except InvalidCookies: - await self.public_cookies_service.undo(user.id, cookies, CookiesStatusEnum.INVALID_COOKIES) + await self.public_cookies_service.undo(user.id) await message.reply_text("出错了呜呜呜 ~ 请稍后重试") return ConversationHandler.END except SimnetBadRequest as exc: if exc.retcode == 1034: - await self.public_cookies_service.undo(user.id) await message.reply_text("出错了呜呜呜 ~ 请稍后重试") return ConversationHandler.END await message.reply_text("获取账号信息发生错误", reply_markup=ReplyKeyboardRemove()) @@ -228,8 +222,6 @@ class BindAccountPlugin(Plugin.Conversation): except ValueError: await message.reply_text("ID 格式有误,请检查", reply_markup=ReplyKeyboardRemove()) return ConversationHandler.END - finally: - await client.shutdown() player_info = await self.players_service.get( user.id, player_id=player_id, region=bind_account_plugin_data.region ) diff --git a/plugins/account/cookies.py b/plugins/account/cookies.py index 382e076..9c808dc 100644 --- a/plugins/account/cookies.py +++ b/plugins/account/cookies.py @@ -237,7 +237,8 @@ class AccountCookiesPlugin(Plugin.Conversation): try: account_cookies_plugin_data.device = self.parse_headers(cookie) except ValueError: - await message.reply_text("警告,解析 Devices 出现错误,可能无法绕过风控,查询操作将需要通过验证。") + account_cookies_plugin_data.device = None + await message.reply_text("解析 Devices 出现错误,可能无法绕过风控,查询操作将需要通过验证。") if not cookies: logger.info("用户 %s[%s] Cookies格式有误", user.full_name, user.id) await message.reply_text("Cookies格式有误,请检查后重新尝试绑定", reply_markup=ReplyKeyboardRemove()) @@ -382,6 +383,7 @@ class AccountCookiesPlugin(Plugin.Conversation): device_model.device_id = device.device_id device_model.device_fp = device.device_fp device_model.device_name = device.device_name + device_model.is_valid = True await self.devices_service.update(device_model) else: device_model = Devices( @@ -389,6 +391,7 @@ class AccountCookiesPlugin(Plugin.Conversation): device_id=device.device_id, device_fp=device.device_fp, device_name=device.device_name, + is_valid=True, ) await self.devices_service.add(device_model) diff --git a/plugins/account/devices.py b/plugins/account/devices.py deleted file mode 100644 index 058613e..0000000 --- a/plugins/account/devices.py +++ /dev/null @@ -1,187 +0,0 @@ -from typing import Optional - -from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, TelegramObject, Update -from telegram.ext import CallbackContext, ConversationHandler, filters -from telegram.helpers import escape_markdown - -from core.basemodel import RegionEnum -from core.plugin import Plugin, conversation, handler -from core.services.cookies.services import CookiesService -from core.services.devices import DevicesService -from core.services.devices.models import DevicesDataBase as Devices -from core.services.players.services import PlayersService -from modules.apihelper.utility.devices import devices_methods -from utils.log import logger - -__all__ = ("AccountDevicesPlugin",) - - -class AccountDevicesPluginData(TelegramObject): - device_id: str = "" - device_fp: str = "" - device_name: Optional[str] = None - account_id: int = 0 - - def reset(self): - self.device_id = "" - self.device_fp = "" - self.device_name = None - self.account_id = 0 - - -CHECK_SERVER, INPUT_DEVICES, COMMAND_RESULT = range(10100, 10103) - - -class AccountDevicesPlugin(Plugin.Conversation): - """设备绑定""" - - def __init__( - self, - players_service: PlayersService = None, - cookies_service: CookiesService = None, - devices_service: DevicesService = None, - ): - self.cookies_service = cookies_service - self.players_service = players_service - self.devices_service = devices_service - devices_methods.service = devices_service - - @staticmethod - def parse_headers(data: AccountDevicesPluginData, headers_text: str) -> None: - headers = {} - for line in headers_text.splitlines(): - if not line: - continue - try: - k, v = line.split(":", 1) - headers[k.strip()] = v.strip() - except ValueError: - continue - must_keys = {"x-rpc-device_id": 36, "x-rpc-device_fp": 13} - optional_keys = ["x-rpc-device_name"] - for k, v in must_keys.items(): - if (k not in headers) or (not headers.get(k)): - raise ValueError - if len(headers.get(k)) != v: - raise ValueError - for k in optional_keys: - if k not in headers: - continue - elif headers.get(k) and len(headers.get(k)) > 64: - raise ValueError - - data.device_id = headers.get("x-rpc-device_id") - data.device_fp = headers.get("x-rpc-device_fp") - data.device_name = headers.get("x-rpc-device_name") - - @conversation.entry_point - @handler.command(command="setdevice", filters=filters.ChatType.PRIVATE, block=False) - @handler.command(command="setdevices", filters=filters.ChatType.PRIVATE, block=False) - async def command_start(self, update: Update, context: CallbackContext) -> int: - user = update.effective_user - message = update.effective_message - logger.info("用户 %s[%s] 绑定设备命令请求", user.full_name, user.id) - account_devices_plugin_data: AccountDevicesPluginData = context.chat_data.get("account_devices_plugin_data") - if account_devices_plugin_data is None: - account_devices_plugin_data = AccountDevicesPluginData() - context.chat_data["account_devices_plugin_data"] = account_devices_plugin_data - else: - account_devices_plugin_data.reset() - - text = f'你好 {user.mention_markdown_v2()} {escape_markdown("!请选择要绑定的服务器!或回复退出取消操作")}' - reply_keyboard = [["米游社"], ["退出"]] - await message.reply_markdown_v2(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)) - return CHECK_SERVER - - @conversation.state(state=CHECK_SERVER) - @handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False) - async def check_server(self, update: Update, context: CallbackContext) -> int: - user = update.effective_user - message = update.effective_message - account_devices_plugin_data: AccountDevicesPluginData = context.chat_data.get("account_devices_plugin_data") - if message.text == "退出": - await message.reply_text("退出任务", reply_markup=ReplyKeyboardRemove()) - return ConversationHandler.END - if message.text == "米游社": - region = RegionEnum.HYPERION - bbs_name = "米游社" - else: - await message.reply_text("选择错误,请重新选择") - return CHECK_SERVER - player_info = await self.players_service.get(user.id, region=region) - if player_info: - cookies_database = await self.cookies_service.get(user.id, player_info.account_id, region) - if not cookies_database: - await message.reply_text(f"你还没有绑定 {bbs_name} 的Cookies,请先绑定Cookies") - return ConversationHandler.END - account_devices_plugin_data.account_id = player_info.account_id - else: - await message.reply_text(f"你还没有绑定 {bbs_name} 的Cookies,请先绑定Cookies") - return ConversationHandler.END - help_message = ( - "关于如何获取Device\n" - "此操作只能在PC上进行。\n\n" - "PC:\n" - "1、打开通行证并登录\n" - "2、进入通行证按F12打开开发者工具\n" - "3、将开发者工具切换至网络(Network)并点击过滤栏中的 Fetch/XHR 并刷新页面\n" - "4、在请求列表中找到 login_by_cookie\n" - "5、右键并复制请求标头(Request Headers)\n" - "如发现没有此请求大概因为缓存的存在需要你点击禁用缓存(Disable Cache)再次刷新页面" - ) - await message.reply_html(help_message, disable_web_page_preview=True) - await message.reply_text(f"请输入{bbs_name}的请求标头!或回复退出取消操作", reply_markup=ReplyKeyboardRemove()) - return INPUT_DEVICES - - @conversation.state(state=INPUT_DEVICES) - @handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False) - async def input_cookies(self, update: Update, context: CallbackContext) -> int: - message = update.effective_message - user = update.effective_user - account_devices_plugin_data: AccountDevicesPluginData = context.chat_data.get("account_devices_plugin_data") - if message.text == "退出": - await message.reply_text("退出任务", reply_markup=ReplyKeyboardRemove()) - return ConversationHandler.END - try: - self.parse_headers(account_devices_plugin_data, message.text) - except ValueError as exc: - logger.info("用户 %s[%s] Devices解析出现错误\ntext:%s", user.full_name, user.id, message.text) - logger.debug("解析Devices出现错误", exc_info=exc) - await message.reply_text("解析Devices出现错误,请检查是否正确", reply_markup=ReplyKeyboardRemove()) - return ConversationHandler.END - reply_keyboard = [["确认", "退出"]] - await message.reply_markdown_v2( - "请确认修改!", reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) - ) - return COMMAND_RESULT - - @conversation.state(state=COMMAND_RESULT) - @handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False) - async def command_result(self, update: Update, context: CallbackContext) -> int: - user = update.effective_user - message = update.effective_message - account_devices_plugin_data: AccountDevicesPluginData = context.chat_data.get("account_devices_plugin_data") - if message.text == "退出": - await message.reply_text("退出任务", reply_markup=ReplyKeyboardRemove()) - return ConversationHandler.END - if message.text == "确认": - device = await self.devices_service.get(account_devices_plugin_data.account_id) - if device: - device.device_id = account_devices_plugin_data.device_id - device.device_fp = account_devices_plugin_data.device_fp - device.device_name = account_devices_plugin_data.device_name - await self.devices_service.update(device) - logger.success("用户 %s[%s] 更新Devices", user.full_name, user.id) - else: - device = Devices( - account_id=account_devices_plugin_data.account_id, - device_id=account_devices_plugin_data.device_id, - device_fp=account_devices_plugin_data.device_fp, - device_name=account_devices_plugin_data.device_name, - ) - await self.devices_service.add(device) - logger.info("用户 %s[%s] 绑定Devices成功", user.full_name, user.id) - await message.reply_text("保存成功", reply_markup=ReplyKeyboardRemove()) - return ConversationHandler.END - await message.reply_text("回复错误,请重新输入") - return COMMAND_RESULT diff --git a/plugins/account/players.py b/plugins/account/players.py index c86b665..d5319bf 100644 --- a/plugins/account/players.py +++ b/plugins/account/players.py @@ -12,6 +12,7 @@ from core.services.cookies import CookiesService from core.services.players import PlayersService from core.services.players.services import PlayerInfoService from gram_core.services.cookies.models import CookiesStatusEnum +from gram_core.services.devices import DevicesService from modules.apihelper.models.genshin.cookies import CookiesModel from utils.log import logger @@ -24,10 +25,17 @@ __all__ = ("PlayersManagesPlugin",) class PlayersManagesPlugin(Plugin): - def __init__(self, players: PlayersService, cookies: CookiesService, player_info_service: PlayerInfoService): + def __init__( + self, + players: PlayersService, + cookies: CookiesService, + player_info_service: PlayerInfoService, + devices_service: DevicesService, + ): self.cookies_service = cookies self.players_service = players self.player_info_service = player_info_service + self.devices_service = devices_service @staticmethod def players_manager_callback(callback_query_data: str) -> Tuple[str, int, int]: @@ -273,10 +281,15 @@ class PlayersManagesPlugin(Plugin): await callback_query.edit_message_text( f"玩家 {player.player_id} {player_info.nickname} cookies 未找到", reply_markup=InlineKeyboardMarkup(buttons) ) + return + device = await self.devices_service.get(player.account_id) cookies = SimpleCookie() for key, value in cookies_data.data.items(): cookies[key] = value + if device is not None: + cookies["x-rpc-device_id"] = device.device_id + cookies["x-rpc-device_fp"] = device.device_fp cookie_str = cookies.output(header="", sep=";") diff --git a/plugins/app/gift.py b/plugins/app/gift.py index 1886a4a..4b0aed7 100644 --- a/plugins/app/gift.py +++ b/plugins/app/gift.py @@ -1,8 +1,8 @@ from datetime import datetime from typing import List, Optional + from httpx import AsyncClient from pydantic import BaseModel - from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton from telegram.ext import CallbackContext, filters diff --git a/plugins/app/inline.py b/plugins/app/inline.py index 9ad88a1..8931e36 100644 --- a/plugins/app/inline.py +++ b/plugins/app/inline.py @@ -15,8 +15,8 @@ from telegram.constants import ParseMode from telegram.error import BadRequest from telegram.ext import CallbackContext -from core.plugin import Plugin, handler from core.dependence.assets import AssetsService +from core.plugin import Plugin, handler from core.services.search.services import SearchServices from core.services.wiki.services import WikiService from utils.log import logger diff --git a/plugins/app/start.py b/plugins/app/start.py index 0eec97c..e108bd5 100644 --- a/plugins/app/start.py +++ b/plugins/app/start.py @@ -105,7 +105,7 @@ class StartPlugin(Plugin): async def process_validate(self, message: Message, user: User, bot_username: Optional[str] = None): await message.reply_text( "由于官方对第三方工具限制以及账户安全的考虑,频繁使用第三方工具会导致账号被风控并要求用过验证才能进行访问。\n" - "如出现频繁验证请求,可尝试使用 /setdevice 设置设备标识符以尝试解决该问题。\n" + "如出现频繁验证请求,可尝试使用 /setcookies 的新方法重新绑定以尝试解决该问题。\n" "如担心账号安全,建议修改密码以保护账号安全,修改账号密码后任何登录令牌都会被强制注销。\n" ) try: diff --git a/plugins/starrail/activity.py b/plugins/starrail/activity.py index 88fed40..00d9a56 100644 --- a/plugins/starrail/activity.py +++ b/plugins/starrail/activity.py @@ -80,7 +80,7 @@ class PlayerActivityPlugins(Plugin): logger.info("用户 %s[%s] 查询评书奇谭信息命令请求", user.full_name, user.id) try: uid = await self.get_uid(user.id, context.args, message.reply_to_message) - async with self.helper.genshin(user.id) as client: + async with self.helper.genshin_or_public(user.id, uid=uid) as client: render_result = await self.fantastic_story_render(client, uid) except AttributeError as exc: logger.error(ACTIVITY_DATA_ERROR) @@ -136,7 +136,7 @@ class PlayerActivityPlugins(Plugin): logger.info("用户 %s[%s] 查询以太战线信息命令请求", user.full_name, user.id) try: uid = await self.get_uid(user.id, context.args, message.reply_to_message) - async with self.helper.genshin(user.id) as client: + async with self.helper.genshin_or_public(user.id, uid=uid) as client: render_result = await self.yitai_battle_render(client, uid) except AttributeError as exc: logger.error(ACTIVITY_DATA_ERROR) @@ -184,7 +184,7 @@ class PlayerActivityPlugins(Plugin): logger.info("用户 %s[%s] 查询地城探宝信息命令请求", user.full_name, user.id) try: uid = await self.get_uid(user.id, context.args, message.reply_to_message) - async with self.helper.genshin(user.id) as client: + async with self.helper.genshin_or_public(user.id, uid=uid) as client: render_result = await self.treasure_dungeon_render(client, uid) if render_result is None: raise NotHaveData @@ -251,7 +251,7 @@ class PlayerActivityPlugins(Plugin): logger.info("用户 %s[%s] 查询金人巷信息命令请求", user.full_name, user.id) try: uid = await self.get_uid(user.id, context.args, message.reply_to_message) - async with self.helper.genshin(user.id) as client: + async with self.helper.genshin_or_public(user.id, uid=uid) as client: render_result = await self.copper_man_render(client, uid) except AttributeError as exc: logger.error(ACTIVITY_DATA_ERROR) diff --git a/plugins/starrail/challenge.py b/plugins/starrail/challenge.py index 3a77fdb..bc0a756 100644 --- a/plugins/starrail/challenge.py +++ b/plugins/starrail/challenge.py @@ -6,7 +6,6 @@ from typing import Any, List, Optional, Tuple, Union, TYPE_CHECKING from arkowrapper import ArkoWrapper from pytz import timezone -from simnet.errors import BadRequest as SimnetBadRequest from telegram import Message, Update from telegram.constants import ChatAction, ParseMode from telegram.ext import CallbackContext, filters @@ -16,7 +15,7 @@ from core.plugin import Plugin, handler from core.services.cookies.error import TooManyRequestPublicCookies from core.services.template.models import RenderGroupResult, RenderResult from core.services.template.services import TemplateService -from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError +from plugins.tools.genshin import GenshinHelper from utils.log import logger from utils.uid import mask_number @@ -135,20 +134,11 @@ class ChallengePlugin(Plugin): reply_text: Optional[Message] = None try: - try: - async with self.helper.genshin(user.id) as client: - if client.player_id != uid: - raise CookiesNotFoundError(uid) - if total: - reply_text = await message.reply_text("彦卿需要时间整理混沌回忆数据,还请耐心等待哦~") - await message.reply_chat_action(ChatAction.TYPING) - images = await self.get_rendered_pic(client, uid, floor, total, previous) - except CookiesNotFoundError: - async with self.helper.public_genshin(user.id) as client: - if total: - reply_text = await message.reply_text("彦卿需要时间整理混沌回忆数据,还请耐心等待哦~") - await message.reply_chat_action(ChatAction.TYPING) - images = await self.get_rendered_pic(client, uid, floor, total, previous) + async with self.helper.genshin_or_public(user.id, uid=uid) as client: + if total: + reply_text = await message.reply_text("彦卿需要时间整理混沌回忆数据,还请耐心等待哦~") + await message.reply_chat_action(ChatAction.TYPING) + images = await self.get_rendered_pic(client, uid, floor, total, previous) except TooManyRequestPublicCookies: reply_message = await message.reply_text("查询次数太多,请您稍后重试") if filters.ChatType.GROUPS.filter(message): @@ -161,11 +151,6 @@ class ChallengePlugin(Plugin): except IndexError: # 若混沌回忆为挑战此层 await reply_message_func("还没有挑战本层呢,咕咕咕~") return - except SimnetBadRequest as exc: - if exc.retcode == 1034 and client.player_id != uid: - await message.reply_text("出错了呜呜呜 ~ 请稍后重试 ~ 米游社风控太严力") - return - raise exc if images is None: await reply_message_func(f"还没有第 {floor} 层的挑战数据") return diff --git a/plugins/starrail/museum.py b/plugins/starrail/museum.py index fb6af1d..cdec14c 100644 --- a/plugins/starrail/museum.py +++ b/plugins/starrail/museum.py @@ -11,7 +11,7 @@ from core.plugin import Plugin, handler from core.services.cookies.error import TooManyRequestPublicCookies from core.services.template.models import RenderResult from core.services.template.services import TemplateService -from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError +from plugins.tools.genshin import GenshinHelper from utils.log import logger from utils.uid import mask_number @@ -70,19 +70,8 @@ class PlayerMuseumPlugins(Plugin): logger.info("用户 %s[%s] 查询博物馆信息命令请求", user.full_name, user.id) try: uid = await self.get_uid(user.id, context.args, message.reply_to_message) - try: - async with self.helper.genshin(user.id) as client: - if client.player_id != uid: - raise CookiesNotFoundError(uid) - render_result = await self.render(client, uid) - except CookiesNotFoundError: - async with self.helper.public_genshin(user.id) as client: - render_result = await self.render(client, uid) - except SimnetBadRequest as exc: - if exc.retcode == 1034: - await message.reply_text("出错了呜呜呜 ~ 请稍后重试") - return - raise exc + async with self.helper.genshin_or_public(user.id, uid=uid) as client: + render_result = await self.render(client, uid) except TooManyRequestPublicCookies: await message.reply_text("用户查询次数过多 请稍后重试") return diff --git a/plugins/starrail/rogue.py b/plugins/starrail/rogue.py index 2ceb9b5..217e18e 100644 --- a/plugins/starrail/rogue.py +++ b/plugins/starrail/rogue.py @@ -12,7 +12,7 @@ from core.plugin import Plugin, handler from core.services.cookies.error import TooManyRequestPublicCookies from core.services.template.models import RenderResult from core.services.template.services import TemplateService -from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError +from plugins.tools.genshin import GenshinHelper from utils.log import logger from utils.uid import mask_number @@ -91,19 +91,8 @@ class PlayerRoguePlugins(Plugin): logger.info("用户 %s[%s] 查询模拟宇宙信息命令请求", user.full_name, user.id) try: uid, pre = await self.get_uid(user.id, context.args, message.reply_to_message) - try: - async with self.helper.genshin(user.id) as client: - if client.player_id != uid: - raise CookiesNotFoundError(uid) - render_result = await self.render(client, pre, uid) - except CookiesNotFoundError: - async with self.helper.public_genshin(user.id) as client: - render_result = await self.render(client, pre, uid) - except SimnetBadRequest as exc: - if exc.retcode == 1034: - await message.reply_text("出错了呜呜呜 ~ 请稍后重试") - return - raise exc + async with self.helper.genshin_or_public(user.id, uid=uid) as client: + render_result = await self.render(client, pre, uid) except TooManyRequestPublicCookies: await message.reply_text("用户查询次数过多 请稍后重试") return diff --git a/plugins/starrail/stats.py b/plugins/starrail/stats.py index 3bcd827..1bdd38f 100644 --- a/plugins/starrail/stats.py +++ b/plugins/starrail/stats.py @@ -9,7 +9,7 @@ from core.plugin import Plugin, handler from core.services.cookies.error import TooManyRequestPublicCookies from core.services.template.models import RenderResult from core.services.template.services import TemplateService -from plugins.tools.genshin import GenshinHelper, CookiesNotFoundError +from plugins.tools.genshin import GenshinHelper from utils.log import logger from utils.uid import mask_number @@ -62,19 +62,8 @@ class PlayerStatsPlugins(Plugin): logger.info("用户 %s[%s] 查询游戏用户命令请求", user.full_name, user.id) try: uid: int = await self.get_uid(user.id, context.args, message.reply_to_message) - try: - async with self.helper.genshin(user.id) as client: - if client.player_id != uid: - raise CookiesNotFoundError(uid) - render_result = await self.render(client, uid) - except CookiesNotFoundError: - async with self.helper.public_genshin(user.id) as client: - render_result = await self.render(client, uid) - except SimnetBadRequest as exc: - if exc.retcode == 1034: - await message.reply_text("出错了呜呜呜 ~ 请稍后重试") - return - raise exc + async with self.helper.genshin_or_public(user.id, uid=uid) as client: + render_result = await self.render(client, uid) except TooManyRequestPublicCookies: await message.reply_text("用户查询次数过多 请稍后重试") return diff --git a/plugins/tools/genshin.py b/plugins/tools/genshin.py index 0b1eb21..da3c4de 100644 --- a/plugins/tools/genshin.py +++ b/plugins/tools/genshin.py @@ -10,6 +10,7 @@ from simnet import StarRailClient, Region from simnet.errors import BadRequest as SimnetBadRequest, InvalidCookies, NetworkError, CookieException from simnet.models.genshin.calculator import CalculatorCharacterDetails from simnet.models.genshin.chronicle.characters import Character +from simnet.utils.player import recognize_game_biz from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm.exc import StaleDataError from sqlmodel import BigInteger, Column, DateTime, Field, Index, Integer, SQLModel, String, delete, func, select @@ -193,7 +194,9 @@ class PlayerNotFoundError(Exception): class CookiesNotFoundError(Exception): - def __init__(self, user_id): + def __init__(self, user_id: int, region: Optional[RegionEnum] = None): + self.user_id = user_id + self.region = region super().__init__(f"{user_id} cookies not found") @@ -215,16 +218,16 @@ class GenshinHelper(Plugin): raise ServiceNotFoundError(*filter(lambda x: x is None, temp)) @asynccontextmanager - async def genshin(self, user_id: int, region: Optional[RegionEnum] = None) -> StarRailClient: + async def genshin(self, user_id: int, region: Optional[RegionEnum] = None) -> StarRailClient: # skipcq: PY-R1000 # player = await self.players_service.get_player(user_id, region) if player is None: raise PlayerNotFoundError(user_id) if player.account_id is None: - raise CookiesNotFoundError(user_id) + raise CookiesNotFoundError(user_id, player.region) cookie_model = await self.cookies_service.get(player.user_id, player.account_id, player.region) if cookie_model is None: - raise CookiesNotFoundError(user_id) + raise CookiesNotFoundError(user_id, player.region) cookies = cookie_model.data if player.region == RegionEnum.HYPERION: # 国服 @@ -252,6 +255,11 @@ class GenshinHelper(Plugin): ) as client: try: yield client + except SimnetBadRequest as exc: + if exc.ret_code == 1034 and devices is not None: + devices.is_valid = False + await self.devices_service.update(devices) + raise exc except InvalidCookies as exc: refresh = False cookie_model.status = CookiesStatusEnum.INVALID_COOKIES @@ -298,10 +306,10 @@ class GenshinHelper(Plugin): raise PlayerNotFoundError(user_id) if player.account_id is None: - raise CookiesNotFoundError(user_id) + raise CookiesNotFoundError(user_id, player.region) cookie_model = await self.cookies_service.get(player.user_id, player.account_id, player.region) if cookie_model is None: - raise CookiesNotFoundError(user_id) + raise CookiesNotFoundError(user_id, player.region) cookies = cookie_model.data if player.region == RegionEnum.HYPERION: @@ -329,16 +337,20 @@ class GenshinHelper(Plugin): ) @asynccontextmanager - async def public_genshin(self, user_id: int, region: Optional[RegionEnum] = None) -> StarRailClient: - player = await self.players_service.get_player(user_id, region) + async def public_genshin( + self, user_id: int, region: Optional[RegionEnum] = None, uid: Optional[int] = None + ) -> StarRailClient: + if not (region or uid): + player = await self.players_service.get_player(user_id, region) + if player: + region = player.region + uid = player.player_id - region = player.region cookies = await self.public_cookies_service.get_cookies(user_id, region) - uid = player.player_id - if player.region == RegionEnum.HYPERION: + if region == RegionEnum.HYPERION: region = Region.CHINESE - elif player.region == RegionEnum.HOYOLAB: + elif region == RegionEnum.HOYOLAB: region = Region.OVERSEAS else: raise TypeError("Region is not `RegionEnum.NULL`") @@ -353,7 +365,6 @@ class GenshinHelper(Plugin): async with StarRailClient( cookies.data, region=region, - account_id=player.account_id, player_id=uid, lang="zh-cn", device_id=device_id, @@ -364,4 +375,28 @@ class GenshinHelper(Plugin): except SimnetBadRequest as exc: if exc.ret_code == 1034: await self.public_cookies_service.undo(user_id) + await self.public_cookies_service.set_device_valid(client.account_id, False) raise exc + + @asynccontextmanager + async def genshin_or_public( + self, user_id: int, region: Optional[RegionEnum] = None, uid: Optional[int] = None + ) -> StarRailClient: + try: + async with self.genshin(user_id, region) as client: + client.public = False + if uid and recognize_game_biz(uid, client.game) != recognize_game_biz(client.player_id, client.game): + # 如果 uid 和 player_id 服务器不一致,说明是跨服的,需要使用公共的 cookies + raise CookiesNotFoundError(user_id) + yield client + except CookiesNotFoundError: + if uid: + region = RegionEnum.HYPERION if uid < 600000000 else RegionEnum.HOYOLAB + async with self.public_genshin(user_id, region, uid) as client: + try: + client.public = True + yield client + except SimnetBadRequest as exc: + if exc.ret_code == 1034: + raise CookiesNotFoundError(user_id) from exc + raise exc