diff --git a/gsuid_core/utils/api/mys/api.py b/gsuid_core/utils/api/mys/api.py index e8598e0..2995b58 100644 --- a/gsuid_core/utils/api/mys/api.py +++ b/gsuid_core/utils/api/mys/api.py @@ -134,4 +134,6 @@ CALENDAR_URL = f'{DRAW_BASE_URL}/calendar' RECEIVE_URL = f'{DRAW_BASE_URL}/post_my_draw' BS_INDEX_URL = f'{DRAW_BASE_URL}/index' +GET_FP_URL = 'https://public-data-api.mihoyo.com/device-fp/api/getFp' + _API = locals() diff --git a/gsuid_core/utils/api/mys/request.py b/gsuid_core/utils/api/mys/request.py index 39f426f..fcb6d10 100644 --- a/gsuid_core/utils/api/mys/request.py +++ b/gsuid_core/utils/api/mys/request.py @@ -14,6 +14,7 @@ from typing import Any, Dict, List, Tuple, Union, Literal, Optional, cast from aiohttp import TCPConnector, ClientSession, ContentTypeError from gsuid_core.logger import logger +from gsuid_core.utils.database.api import DBSqla from gsuid_core.utils.plugins_config.gs_config import core_plugins_config from .api import _API @@ -88,6 +89,7 @@ class BaseMysApi: is_sr = False RECOGNIZE_SERVER = RECOGNIZE_SERVER chs = {} + dbsqla: DBSqla = DBSqla() @abstractmethod async def _upass(self, header: Dict) -> str: @@ -109,6 +111,59 @@ class BaseMysApi: async def get_stoken(self, uid: str) -> Optional[str]: ... + @abstractmethod + async def get_user_fp(self, uid: str) -> Optional[str]: + ... + + @abstractmethod + async def get_user_device_id(self, uid: str) -> Optional[str]: + ... + + def get_device_id(self) -> str: + device_id = uuid.uuid4().hex + return device_id + + def generate_seed(self, length: int): + characters = '0123456789abcdef' + result = ''.join(random.choices(characters, k=length)) + return result + + async def generate_fp_by_uid(self, uid: str) -> str: + seed_id = self.generate_seed(16) + seed_time = str(int(time.time() * 1000)) + ext_fields = f'{{"userAgent":"{self._HEADER["User-Agent"]}",\ +"browserScreenSize":281520,"maxTouchPoints":5,\ +"isTouchSupported":true,"browserLanguage":"zh-CN","browserPlat":"iPhone",\ +"browserTimeZone":"Asia/Shanghai","webGlRender":"Apple GPU",\ +"webGlVendor":"Apple Inc.",\ +"numOfPlugins":0,"listOfPlugins":"unknown","screenRatio":3,"deviceMemory":"unknown",\ +"hardwareConcurrency":"4","cpuClass":"unknown","ifNotTrack":"unknown","ifAdBlock":0,\ +"hasLiedResolution":1,"hasLiedOs":0,"hasLiedBrowser":0}}' + body = { + 'seed_id': seed_id, + 'device_id': await self.get_user_device_id(uid), + 'platform': '5', + 'seed_time': seed_time, + 'ext_fields': ext_fields, + 'app_name': 'account_cn', + 'device_fp': '38d7ee834d1e9', + } + HEADER = copy.deepcopy(self._HEADER) + res = await self._mys_request( + url=self.MAPI['GET_FP_URL'], + method='POST', + header=HEADER, + data=body, + ) + if not isinstance(res, Dict): + logger.error(f"获取fp连接失败{res}") + return random_hex(13).lower() + elif res["data"]["code"] != 200: + logger.error(f"获取fp参数不正确{res['data']['msg']}") + return random_hex(13).lower() + else: + return res["data"]["device_fp"] + async def simple_mys_req( self, URL: str, @@ -200,8 +255,15 @@ class BaseMysApi: connector=TCPConnector(verify_ssl=ssl_verify) ) as client: raw_data = {} + uid = None + if params and 'role_id' in params: + uid = params['role_id'] + header['x-rpc-device_id'] = await self.get_user_device_id(uid) + header['x-rpc-device_fp'] = await self.get_user_fp(uid) + for _ in range(2): if 'Cookie' in header and header['Cookie'] in self.chs: + # header['x-rpc-challenge']=self.chs.pop(header['Cookie']) if self.is_sr: header['x-rpc-challenge'] = self.chs.pop( header['Cookie'] @@ -216,6 +278,7 @@ class BaseMysApi: header['x-rpc-page'] = ( '3.1.3_#/rpg' if self.is_sr else '3.1.3_#/ys' ) + async with client.request( method, url=url, @@ -244,6 +307,11 @@ class BaseMysApi: if retcode == 1034: ch = await self._upass(header) self.chs[header['Cookie']] = ch + elif retcode == -10001 and uid: + sqla = self.dbsqla.get_sqla('TEMP') + new_fp = await self.generate_fp_by_uid(uid) + await sqla.update_user_data(uid, {'fp': new_fp}) + header['x-rpc-device_fp'] = new_fp elif retcode != 0: return retcode else: diff --git a/gsuid_core/utils/api/mys_api.py b/gsuid_core/utils/api/mys_api.py index 3b03e1e..ea3ac9b 100644 --- a/gsuid_core/utils/api/mys_api.py +++ b/gsuid_core/utils/api/mys_api.py @@ -1,15 +1,12 @@ from typing import Literal, Optional from gsuid_core.utils.api.mys import MysApi -from gsuid_core.utils.database.api import DBSqla from gsuid_core.utils.plugins_config.gs_config import core_plugins_config gsconfig = core_plugins_config class _MysApi(MysApi): - dbsqla: DBSqla = DBSqla() - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -24,5 +21,23 @@ class _MysApi(MysApi): async def get_stoken(self, uid: str) -> Optional[str]: return await self.dbsqla.get_sqla('TEMP').get_user_stoken(uid) + async def get_user_fp(self, uid: str) -> Optional[str]: + data = await self.dbsqla.get_sqla('TEMP').get_user_fp(uid) + if data is None: + data = await self.generate_fp_by_uid(uid) + await self.dbsqla.get_sqla('TEMP').update_user_data( + uid, {'fp': data} + ) + return data + + async def get_user_device_id(self, uid: str) -> Optional[str]: + data = await self.dbsqla.get_sqla('TEMP').get_user_device_id(uid) + if data is None: + data = self.get_device_id() + await self.dbsqla.get_sqla('TEMP').update_user_data( + uid, {'device_id': data} + ) + return data + mys_api = _MysApi() diff --git a/gsuid_core/utils/cookie_manager/add_ck.py b/gsuid_core/utils/cookie_manager/add_ck.py index ce45088..d790146 100644 --- a/gsuid_core/utils/cookie_manager/add_ck.py +++ b/gsuid_core/utils/cookie_manager/add_ck.py @@ -236,8 +236,16 @@ async def _deal_ck(bot_id: str, mes: str, user_id: str) -> str: if uid is None: uid = '0' + device_id = mys_api.get_device_id() + fp = await mys_api.generate_fp_by_uid(uid) await sqla.insert_user_data( - user_id, uid_bind, sr_uid_bind, account_cookie, app_cookie + user_id, + uid_bind, + sr_uid_bind, + account_cookie, + app_cookie, + fp, + device_id, ) im_list.append( diff --git a/gsuid_core/utils/database/dal.py b/gsuid_core/utils/database/dal.py index 99ca830..2d52169 100644 --- a/gsuid_core/utils/database/dal.py +++ b/gsuid_core/utils/database/dal.py @@ -1,6 +1,5 @@ import re import asyncio -import contextlib from typing import Dict, List, Literal, Optional from sqlmodel import SQLModel @@ -43,13 +42,17 @@ class SQLA: 'ALTER TABLE GsBind ADD COLUMN sr_uid TEXT', 'ALTER TABLE GsUser ADD COLUMN sr_uid TEXT', 'ALTER TABLE GsUser ADD COLUMN sr_region TEXT', + 'ALTER TABLE GsUser ADD COLUMN fp TEXT', + 'ALTER TABLE GsUser ADD COLUMN device_id TEXT', 'ALTER TABLE GsCache ADD COLUMN sr_uid TEXT', ] - with contextlib.suppress(Exception): - async with self.async_session() as session: - for _t in exec_list: + async with self.async_session() as session: + for _t in exec_list: + try: await session.execute(text(_t)) - await session.commit() + await session.commit() + except: # noqa: E722 + pass ##################### # GsBind 部分 # @@ -244,6 +247,14 @@ class SQLA: await session.execute(sql) return True + async def get_user_fp(self, uid: str) -> Optional[str]: + data = await self.select_user_data(uid) + return data.fp if data else None + + async def get_user_device_id(self, uid: str) -> Optional[str]: + data = await self.select_user_data(uid) + return data.device_id if data else None + async def insert_cache_data( self, cookie: str, @@ -267,6 +278,8 @@ class SQLA: sr_uid: Optional[str] = None, cookie: Optional[str] = None, stoken: Optional[str] = None, + fp: Optional[str] = None, + device_id: Optional[str] = None, ) -> bool: async with self.async_session() as session: async with session.begin(): @@ -281,6 +294,7 @@ class SQLA: bot_id=self.bot_id, user_id=user_id, sr_uid=sr_uid, + fp=fp, ) ) await session.execute(sql) @@ -295,6 +309,7 @@ class SQLA: bot_id=self.bot_id, user_id=user_id, uid=uid, + fp=fp, ) ) await session.execute(sql) @@ -321,6 +336,8 @@ class SQLA: sr_region=SR_SERVER.get(sr_uid[0], None) if sr_uid else None, + fp=fp, + device_id=device_id, ) session.add(user_data) await session.commit() @@ -330,13 +347,9 @@ class SQLA: async with self.async_session() as session: async with session.begin(): sql = ( - update(GsUser).where( - GsUser.sr_uid == uid, GsUser.bot_id == self.bot_id - ) + update(GsUser).where(GsUser.sr_uid == uid) if self.is_sr - else update(GsUser).where( - GsUser.uid == uid, GsUser.bot_id == self.bot_id - ) + else update(GsUser).where(GsUser.uid == uid) ) if data is not None: query = sql.values(**data) diff --git a/gsuid_core/utils/database/models.py b/gsuid_core/utils/database/models.py index eddff00..a869125 100644 --- a/gsuid_core/utils/database/models.py +++ b/gsuid_core/utils/database/models.py @@ -29,6 +29,8 @@ class GsUser(SQLModel, table=True): sign_switch: str = Field(title='自动签到') bbs_switch: str = Field(title='自动米游币') status: Optional[str] = Field(default=None, title='状态') + fp: Optional[str] = Field(default=None, title='Fingerprint') + device_id: Optional[str] = Field(default=None, title='设备ID') class GsCache(SQLModel, table=True): diff --git a/poetry.lock b/poetry.lock index 85dfa10..d7f5743 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1486,14 +1486,14 @@ reference = "mirrors" [[package]] name = "nodeenv" -version = "1.7.0" +version = "1.8.0" description = "Node.js virtual environment builder" category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ - {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, - {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] [package.dependencies]