🍻 实验性加入fpdeviceid支持

This commit is contained in:
Wuyi无疑 2023-05-23 00:57:14 +08:00
parent ed846b5851
commit 23c499c82b
7 changed files with 126 additions and 18 deletions

View File

@ -134,4 +134,6 @@ CALENDAR_URL = f'{DRAW_BASE_URL}/calendar'
RECEIVE_URL = f'{DRAW_BASE_URL}/post_my_draw' RECEIVE_URL = f'{DRAW_BASE_URL}/post_my_draw'
BS_INDEX_URL = f'{DRAW_BASE_URL}/index' BS_INDEX_URL = f'{DRAW_BASE_URL}/index'
GET_FP_URL = 'https://public-data-api.mihoyo.com/device-fp/api/getFp'
_API = locals() _API = locals()

View File

@ -14,6 +14,7 @@ from typing import Any, Dict, List, Tuple, Union, Literal, Optional, cast
from aiohttp import TCPConnector, ClientSession, ContentTypeError from aiohttp import TCPConnector, ClientSession, ContentTypeError
from gsuid_core.logger import logger 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 gsuid_core.utils.plugins_config.gs_config import core_plugins_config
from .api import _API from .api import _API
@ -88,6 +89,7 @@ class BaseMysApi:
is_sr = False is_sr = False
RECOGNIZE_SERVER = RECOGNIZE_SERVER RECOGNIZE_SERVER = RECOGNIZE_SERVER
chs = {} chs = {}
dbsqla: DBSqla = DBSqla()
@abstractmethod @abstractmethod
async def _upass(self, header: Dict) -> str: async def _upass(self, header: Dict) -> str:
@ -109,6 +111,59 @@ class BaseMysApi:
async def get_stoken(self, uid: str) -> Optional[str]: 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( async def simple_mys_req(
self, self,
URL: str, URL: str,
@ -200,8 +255,15 @@ class BaseMysApi:
connector=TCPConnector(verify_ssl=ssl_verify) connector=TCPConnector(verify_ssl=ssl_verify)
) as client: ) as client:
raw_data = {} 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): for _ in range(2):
if 'Cookie' in header and header['Cookie'] in self.chs: if 'Cookie' in header and header['Cookie'] in self.chs:
# header['x-rpc-challenge']=self.chs.pop(header['Cookie'])
if self.is_sr: if self.is_sr:
header['x-rpc-challenge'] = self.chs.pop( header['x-rpc-challenge'] = self.chs.pop(
header['Cookie'] header['Cookie']
@ -216,6 +278,7 @@ class BaseMysApi:
header['x-rpc-page'] = ( header['x-rpc-page'] = (
'3.1.3_#/rpg' if self.is_sr else '3.1.3_#/ys' '3.1.3_#/rpg' if self.is_sr else '3.1.3_#/ys'
) )
async with client.request( async with client.request(
method, method,
url=url, url=url,
@ -244,6 +307,11 @@ class BaseMysApi:
if retcode == 1034: if retcode == 1034:
ch = await self._upass(header) ch = await self._upass(header)
self.chs[header['Cookie']] = ch 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: elif retcode != 0:
return retcode return retcode
else: else:

View File

@ -1,15 +1,12 @@
from typing import Literal, Optional from typing import Literal, Optional
from gsuid_core.utils.api.mys import MysApi 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 from gsuid_core.utils.plugins_config.gs_config import core_plugins_config
gsconfig = core_plugins_config gsconfig = core_plugins_config
class _MysApi(MysApi): class _MysApi(MysApi):
dbsqla: DBSqla = DBSqla()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -24,5 +21,23 @@ class _MysApi(MysApi):
async def get_stoken(self, uid: str) -> Optional[str]: async def get_stoken(self, uid: str) -> Optional[str]:
return await self.dbsqla.get_sqla('TEMP').get_user_stoken(uid) 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() mys_api = _MysApi()

View File

@ -236,8 +236,16 @@ async def _deal_ck(bot_id: str, mes: str, user_id: str) -> str:
if uid is None: if uid is None:
uid = '0' uid = '0'
device_id = mys_api.get_device_id()
fp = await mys_api.generate_fp_by_uid(uid)
await sqla.insert_user_data( 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( im_list.append(

View File

@ -1,6 +1,5 @@
import re import re
import asyncio import asyncio
import contextlib
from typing import Dict, List, Literal, Optional from typing import Dict, List, Literal, Optional
from sqlmodel import SQLModel from sqlmodel import SQLModel
@ -43,13 +42,17 @@ class SQLA:
'ALTER TABLE GsBind ADD COLUMN sr_uid TEXT', 'ALTER TABLE GsBind ADD COLUMN sr_uid TEXT',
'ALTER TABLE GsUser 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 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', 'ALTER TABLE GsCache ADD COLUMN sr_uid TEXT',
] ]
with contextlib.suppress(Exception): async with self.async_session() as session:
async with self.async_session() as session: for _t in exec_list:
for _t in exec_list: try:
await session.execute(text(_t)) await session.execute(text(_t))
await session.commit() await session.commit()
except: # noqa: E722
pass
##################### #####################
# GsBind 部分 # # GsBind 部分 #
@ -244,6 +247,14 @@ class SQLA:
await session.execute(sql) await session.execute(sql)
return True 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( async def insert_cache_data(
self, self,
cookie: str, cookie: str,
@ -267,6 +278,8 @@ class SQLA:
sr_uid: Optional[str] = None, sr_uid: Optional[str] = None,
cookie: Optional[str] = None, cookie: Optional[str] = None,
stoken: Optional[str] = None, stoken: Optional[str] = None,
fp: Optional[str] = None,
device_id: Optional[str] = None,
) -> bool: ) -> bool:
async with self.async_session() as session: async with self.async_session() as session:
async with session.begin(): async with session.begin():
@ -281,6 +294,7 @@ class SQLA:
bot_id=self.bot_id, bot_id=self.bot_id,
user_id=user_id, user_id=user_id,
sr_uid=sr_uid, sr_uid=sr_uid,
fp=fp,
) )
) )
await session.execute(sql) await session.execute(sql)
@ -295,6 +309,7 @@ class SQLA:
bot_id=self.bot_id, bot_id=self.bot_id,
user_id=user_id, user_id=user_id,
uid=uid, uid=uid,
fp=fp,
) )
) )
await session.execute(sql) await session.execute(sql)
@ -321,6 +336,8 @@ class SQLA:
sr_region=SR_SERVER.get(sr_uid[0], None) sr_region=SR_SERVER.get(sr_uid[0], None)
if sr_uid if sr_uid
else None, else None,
fp=fp,
device_id=device_id,
) )
session.add(user_data) session.add(user_data)
await session.commit() await session.commit()
@ -330,13 +347,9 @@ class SQLA:
async with self.async_session() as session: async with self.async_session() as session:
async with session.begin(): async with session.begin():
sql = ( sql = (
update(GsUser).where( update(GsUser).where(GsUser.sr_uid == uid)
GsUser.sr_uid == uid, GsUser.bot_id == self.bot_id
)
if self.is_sr if self.is_sr
else update(GsUser).where( else update(GsUser).where(GsUser.uid == uid)
GsUser.uid == uid, GsUser.bot_id == self.bot_id
)
) )
if data is not None: if data is not None:
query = sql.values(**data) query = sql.values(**data)

View File

@ -29,6 +29,8 @@ class GsUser(SQLModel, table=True):
sign_switch: str = Field(title='自动签到') sign_switch: str = Field(title='自动签到')
bbs_switch: str = Field(title='自动米游币') bbs_switch: str = Field(title='自动米游币')
status: Optional[str] = Field(default=None, 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): class GsCache(SQLModel, table=True):

6
poetry.lock generated
View File

@ -1486,14 +1486,14 @@ reference = "mirrors"
[[package]] [[package]]
name = "nodeenv" name = "nodeenv"
version = "1.7.0" version = "1.8.0"
description = "Node.js virtual environment builder" description = "Node.js virtual environment builder"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
files = [ files = [
{file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
{file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
] ]
[package.dependencies] [package.dependencies]