mirror of
https://github.com/baiqwerdvd/ArknightsUID.git
synced 2025-06-18 21:35:05 +08:00
✨ 完整支持新版 sign
This commit is contained in:
parent
ab9b434bd9
commit
7386758016
@ -1,3 +1,4 @@
|
||||
import re
|
||||
from ..utils.ark_api import ark_skd_api
|
||||
from ..utils.database.models import ArknightsBind, ArknightsUser
|
||||
|
||||
@ -9,8 +10,16 @@ async def deal_skd_cred(bot_id: str, cred: str, user_id: str) -> str:
|
||||
uid_list = await ArknightsBind.get_uid_list_by_game(user_id, bot_id)
|
||||
if uid_list is None:
|
||||
return UID_HINT
|
||||
|
||||
match = re.search(r'\S+', cred)
|
||||
if not match:
|
||||
return 'Cred无效!'
|
||||
|
||||
check_cred = await ark_skd_api.check_cred_valid(cred)
|
||||
# refresh token
|
||||
token = await ark_skd_api.refresh_token(match.group())
|
||||
print(token)
|
||||
|
||||
check_cred = await ark_skd_api.check_cred_valid(match.group(), token)
|
||||
|
||||
if isinstance(check_cred, bool):
|
||||
return 'Cred无效!'
|
||||
@ -24,8 +33,8 @@ async def deal_skd_cred(bot_id: str, cred: str, user_id: str) -> str:
|
||||
skd_data = await ArknightsUser.select_data_by_uid(uid)
|
||||
if not skd_data:
|
||||
await ArknightsUser.insert_data(user_id, bot_id,
|
||||
cred=cred, uid=uid, skd_uid=skd_uid)
|
||||
cred=match.group(), uid=uid, skd_uid=skd_uid, token=token)
|
||||
else:
|
||||
await ArknightsUser.update_data(user_id, bot_id,
|
||||
cred=cred, uid=uid, skd_uid=skd_uid)
|
||||
cred=match.group(), uid=uid, skd_uid=skd_uid, token=token)
|
||||
return '添加成功!'
|
||||
|
@ -1,5 +1,7 @@
|
||||
ARK_USER_ME = 'https://zonai.skland.com/api/v1/user/me'
|
||||
|
||||
ARK_REFRESH_TOKEN = 'https://zonai.skland.com/api/v1/auth/refresh'
|
||||
|
||||
ARK_PLAYER_INFO = 'https://zonai.skland.com/api/v1/game/player/info'
|
||||
ARK_GEN_CRED_BY_CODE = 'https://zonai.skland.com/api/v1/user/auth/generate_cred_by_code'
|
||||
|
||||
|
@ -3,7 +3,8 @@ import hashlib
|
||||
import json
|
||||
import time
|
||||
import hmac
|
||||
from typing import Any, ClassVar, Literal
|
||||
from typing import Any, ClassVar, Literal, cast
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import msgspec
|
||||
from aiohttp import ClientSession, ContentTypeError, TCPConnector
|
||||
@ -17,21 +18,31 @@ from ...models.skland.models import (
|
||||
ArknightsPlayerInfoModel,
|
||||
ArknightsUserMeModel,
|
||||
)
|
||||
from .api import ARK_GEN_CRED_BY_CODE, ARK_PLAYER_INFO, ARK_SKD_SIGN, ARK_USER_ME
|
||||
from .api import ARK_PLAYER_INFO, ARK_REFRESH_TOKEN, ARK_SKD_SIGN, ARK_USER_ME
|
||||
|
||||
proxy_url = core_plugins_config.get_config('proxy').data
|
||||
ssl_verify = core_plugins_config.get_config('MhySSLVerify').data
|
||||
|
||||
|
||||
class TokenExpiredError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TokenRefreshFailed(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BaseArkApi:
|
||||
proxy_url: str | None = proxy_url if proxy_url else None
|
||||
_HEADER: ClassVar[dict[str, str]] = {
|
||||
'Host': 'zonai.skland.com',
|
||||
'Platform': '1',
|
||||
'platform': '1',
|
||||
'Origin': 'https://www.skland.com',
|
||||
'Referer': 'https://www.skland.com/',
|
||||
'content-type': 'application/json',
|
||||
'user-agent': 'Skland/1.0.1 (com.hypergryph.skland; build:100001014; Android 33; ) Okhttp/4.11.0',
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'Skland/1.1.0 (com.hypergryph.skland; build:100100047; Android 33; ) Okhttp/4.11.0',
|
||||
'vName': '1.1.0',
|
||||
'vCode': '100100047',
|
||||
}
|
||||
|
||||
async def _pass(self, gt: str, ch: str) -> tuple[str | None, str | None]:
|
||||
@ -60,8 +71,9 @@ class BaseArkApi:
|
||||
await ArknightsUser.delete_user_data_by_uid(uid)
|
||||
return -61
|
||||
header = deepcopy(self._HEADER)
|
||||
header['Cred'] = cred
|
||||
raw_data = await self._ark_request(
|
||||
header['cred'] = cred
|
||||
header = await self.set_sign(ARK_PLAYER_INFO, header=header)
|
||||
raw_data = await self.ark_request(
|
||||
url=ARK_PLAYER_INFO,
|
||||
params={'uid': uid},
|
||||
header=header,
|
||||
@ -83,8 +95,9 @@ class BaseArkApi:
|
||||
await ArknightsUser.delete_user_data_by_uid(uid)
|
||||
return -61
|
||||
header = deepcopy(self._HEADER)
|
||||
header['Cred'] = cred
|
||||
raw_data = await self._ark_request(
|
||||
header['cred'] = cred
|
||||
header = await self.set_sign(ARK_SKD_SIGN, header=header)
|
||||
raw_data = await self.ark_request(
|
||||
url=ARK_SKD_SIGN,
|
||||
method='POST',
|
||||
data={
|
||||
@ -110,8 +123,9 @@ class BaseArkApi:
|
||||
await ArknightsUser.delete_user_data_by_uid(uid)
|
||||
return -61
|
||||
header = deepcopy(self._HEADER)
|
||||
header['Cred'] = cred
|
||||
raw_data = await self._ark_request(
|
||||
header['cred'] = cred
|
||||
header = await self.set_sign(ARK_SKD_SIGN, header=header)
|
||||
raw_data = await self.ark_request(
|
||||
url=ARK_SKD_SIGN,
|
||||
method='GET',
|
||||
params={
|
||||
@ -128,10 +142,11 @@ class BaseArkApi:
|
||||
else:
|
||||
return msgspec.convert(unpack_data, ArknightsAttendanceCalendarModel)
|
||||
|
||||
async def check_cred_valid(self, Cred: str) -> bool | ArknightsUserMeModel:
|
||||
async def check_cred_valid(self, cred: str, token: str | None = None) -> bool | ArknightsUserMeModel:
|
||||
header = deepcopy(self._HEADER)
|
||||
header['Cred'] = Cred
|
||||
raw_data = await self._ark_request(ARK_USER_ME, header=header)
|
||||
header['cred'] = cred
|
||||
header = await self.set_sign(ARK_USER_ME, header=header, token=token)
|
||||
raw_data = await self.ark_request(ARK_USER_ME, header=header)
|
||||
if isinstance(raw_data, int):
|
||||
return False
|
||||
if 'code' in raw_data and raw_data['code'] == 10001:
|
||||
@ -140,56 +155,105 @@ class BaseArkApi:
|
||||
unpack_data = self.unpack(raw_data)
|
||||
return msgspec.convert(unpack_data, type=ArknightsUserMeModel)
|
||||
|
||||
async def check_code_valid(self, code: str) -> bool | str:
|
||||
data = {
|
||||
'kind': 1,
|
||||
'code': code
|
||||
}
|
||||
raw_data = await self._ark_request(
|
||||
ARK_GEN_CRED_BY_CODE,
|
||||
data=data
|
||||
)
|
||||
if isinstance(raw_data, int):
|
||||
return False
|
||||
else:
|
||||
cred = raw_data['cred']
|
||||
return cred
|
||||
# async def check_code_valid(self, code: str) -> bool | str:
|
||||
# data = {
|
||||
# 'kind': 1,
|
||||
# 'code': code
|
||||
# }
|
||||
# raw_data = await self.ark_request(
|
||||
# ARK_GEN_CRED_BY_CODE,
|
||||
# data=data
|
||||
# )
|
||||
# if isinstance(raw_data, int):
|
||||
# return False
|
||||
# else:
|
||||
# cred = raw_data['cred']
|
||||
# return cred
|
||||
|
||||
def unpack(self, raw_data: dict) -> dict:
|
||||
return raw_data['data']
|
||||
|
||||
async def refresh_token(self, uid: str) -> int | str:
|
||||
pass
|
||||
async def refresh_token(self, cred: str, uid: str | None = None) -> str:
|
||||
header = deepcopy(self._HEADER)
|
||||
header['cred'] = cred
|
||||
header['sign_enable'] = 'false'
|
||||
raw_data = await self.ark_request(url=ARK_REFRESH_TOKEN, header=header)
|
||||
if isinstance(raw_data, int):
|
||||
raise TokenRefreshFailed
|
||||
else:
|
||||
token = cast(str, self.unpack(raw_data)['token'])
|
||||
uid = await ArknightsUser.get_uid_by_cred(cred)
|
||||
if uid is not None:
|
||||
await ArknightsUser.update_user_attr_by_uid(uid=uid, attr='token', value=token)
|
||||
return token
|
||||
|
||||
async def set_sign(
|
||||
self,
|
||||
url: str,
|
||||
headers: dict[str, Any],
|
||||
header: dict[str, Any],
|
||||
data: dict[str, Any] | None = None,
|
||||
params: dict[str, Any] | None = None,
|
||||
token: str | None = None,
|
||||
) -> dict:
|
||||
path = url.split("://")[1].split("/")[1]
|
||||
parsed_url = urlparse(url)
|
||||
path = parsed_url.path
|
||||
timestamp = str(int(time.time()))
|
||||
str1=json.dumps(
|
||||
{
|
||||
"platform":headers.get(['platform'],""),
|
||||
"platform": header.get('platform', ''),
|
||||
'timestamp': timestamp,
|
||||
'dId': headers.get(["dId"],""),
|
||||
"vName":headers.get(["vName"],"")
|
||||
}
|
||||
,separators=(',', ':')
|
||||
'dId': header.get('dId', ''),
|
||||
"vName": header.get('vName', '')
|
||||
},separators=(',', ':')
|
||||
)
|
||||
s2=""
|
||||
if params:
|
||||
s2+="&".join(["=".join(x) for x in params])
|
||||
print(f'params {params}')
|
||||
s2 += "&".join([str(x) + "=" + str(params[x]) for x in params])
|
||||
if data:
|
||||
s2+=json.dumps(data,separators=(',', ':'))
|
||||
str2=path+s2+headers["timestamp"]+str1
|
||||
token: str | None = await ArknightsUser.get_token_by_cred(headers['Cred'])
|
||||
sign=hashlib.md5(hmac.new(token.encode(),str2.encode(), hashlib.sha256).hexdigest().encode()).hexdigest()
|
||||
headers["sign"]=sign
|
||||
headers["timestamp"]=timestamp
|
||||
return headers
|
||||
s2 += json.dumps(data,separators=(',', ':'))
|
||||
print(f'{path} {s2} {timestamp} {str1}')
|
||||
str2 = path + s2 + timestamp + str1
|
||||
token_ = await ArknightsUser.get_token_by_cred(header['cred'])
|
||||
token = token if token else token_
|
||||
if token is None:
|
||||
raise Exception("token is None")
|
||||
sign = hashlib.md5(hmac.new(token.encode(),str2.encode(), hashlib.sha256).hexdigest().encode()).hexdigest()
|
||||
header["sign"] = sign
|
||||
header["timestamp"] = timestamp
|
||||
print(header)
|
||||
return header
|
||||
|
||||
async def ark_request(
|
||||
self,
|
||||
url: str,
|
||||
method: Literal['GET', 'POST'] = 'GET',
|
||||
header: dict[str, Any] = _HEADER,
|
||||
params: dict[str, Any] | None = None,
|
||||
data: dict[str, Any] | None = None,
|
||||
use_proxy: bool | None = False,
|
||||
) -> dict | int:
|
||||
try:
|
||||
raw_data = await self._ark_request(
|
||||
url=url,
|
||||
method=method,
|
||||
header=header,
|
||||
params=params,
|
||||
data=data,
|
||||
use_proxy=use_proxy,
|
||||
)
|
||||
except TokenExpiredError:
|
||||
await self.refresh_token(header['cred'])
|
||||
header = await self.set_sign(url, header, data, params)
|
||||
raw_data = await self._ark_request(
|
||||
url=url,
|
||||
method=method,
|
||||
header=header,
|
||||
params=params,
|
||||
data=data,
|
||||
use_proxy=use_proxy,
|
||||
)
|
||||
return raw_data
|
||||
|
||||
async def _ark_request(
|
||||
self,
|
||||
@ -204,11 +268,11 @@ class BaseArkApi:
|
||||
connector=TCPConnector(verify_ssl=ssl_verify)
|
||||
) as client:
|
||||
raw_data = {}
|
||||
if 'Cred' not in header:
|
||||
if 'cred' not in header:
|
||||
return 10001
|
||||
|
||||
|
||||
async with client.request(
|
||||
method,
|
||||
method=method,
|
||||
url=url,
|
||||
headers=header,
|
||||
params=params,
|
||||
@ -224,32 +288,29 @@ class BaseArkApi:
|
||||
logger.info(raw_data)
|
||||
|
||||
# 判断code
|
||||
if 'code' in raw_data and raw_data['code'] != 0:
|
||||
logger.info(raw_data)
|
||||
return raw_data
|
||||
elif 'code' in raw_data and raw_data['code'] == 10000:
|
||||
if 'code' in raw_data and raw_data['code'] == 10000:
|
||||
#token失效
|
||||
logger.info(raw_data)
|
||||
await self.refresh_token(header['Cred'])
|
||||
|
||||
raise TokenExpiredError
|
||||
|
||||
return raw_data
|
||||
# 判断status
|
||||
if 'status' in raw_data and 'msg' in raw_data:
|
||||
retcode = 1
|
||||
else:
|
||||
retcode = 0
|
||||
# if 'status' in raw_data and 'msg' in raw_data:
|
||||
# retcode = 1
|
||||
# else:
|
||||
# retcode = 0
|
||||
|
||||
if retcode == 1 and data:
|
||||
vl, ch = await self._pass(
|
||||
gt=raw_data['data']['captcha']['gt'],
|
||||
ch=raw_data['data']['captcha']['challenge']
|
||||
)
|
||||
data['captcha'] = {}
|
||||
data['captcha']['geetest_challenge'] = ch
|
||||
data['captcha']['geetest_validate'] = vl
|
||||
data['captcha']['geetest_seccode'] = f'{vl}|jordan'
|
||||
elif retcode != 0:
|
||||
return retcode
|
||||
else:
|
||||
return raw_data
|
||||
return 10001
|
||||
# if retcode == 1 and data:
|
||||
# vl, ch = await self._pass(
|
||||
# gt=raw_data['data']['captcha']['gt'],
|
||||
# ch=raw_data['data']['captcha']['challenge']
|
||||
# )
|
||||
# data['captcha'] = {}
|
||||
# data['captcha']['geetest_challenge'] = ch
|
||||
# data['captcha']['geetest_validate'] = vl
|
||||
# data['captcha']['geetest_seccode'] = f'{vl}|jordan'
|
||||
# elif retcode != 0:
|
||||
# return retcode
|
||||
# else:
|
||||
# return raw_data
|
||||
# return 10001
|
||||
|
@ -1,6 +1,6 @@
|
||||
from typing import Literal
|
||||
|
||||
from gsuid_core.utils.database.base_models import Bind, Push, T_BaseIDModel, T_User, User, with_session
|
||||
from gsuid_core.utils.database.base_models import Bind, Push, T_BaseIDModel, T_User, User, with_session, BaseModel
|
||||
from gsuid_core.webconsole.mount_app import GsAdminModel, PageSchema, site
|
||||
from sqlmodel import Field
|
||||
from sqlalchemy.future import select
|
||||
@ -15,20 +15,17 @@ class ArknightsUser(User, table=True):
|
||||
uid: str | None = Field(default=None, title='明日方舟UID')
|
||||
skd_uid: str | None = Field(default=None, title='SKD用户ID')
|
||||
cred: str | None = Field(default=None, title='SKD凭证')
|
||||
toekn: str | None = Field(default=None, title='SKD Token')
|
||||
token: str | None = Field(default=None, title='SKD Token')
|
||||
|
||||
@classmethod
|
||||
@with_session
|
||||
async def select_data_by_cred(
|
||||
cls: type[T_User],
|
||||
cls,
|
||||
session: AsyncSession,
|
||||
cred: str
|
||||
) -> T_User | None:
|
||||
result = await session.execute(
|
||||
select(cls).where(
|
||||
'cred' == cred,
|
||||
)
|
||||
)
|
||||
) -> BaseModel | None:
|
||||
sql= select(cls).where(cls.cred == cred)
|
||||
result = await session.execute(sql)
|
||||
data = result.scalars().all()
|
||||
return data[0] if data else None
|
||||
|
||||
@ -37,6 +34,25 @@ class ArknightsUser(User, table=True):
|
||||
result = await cls.select_data_by_cred(cred)
|
||||
return getattr(result, 'token') if result else None
|
||||
|
||||
@classmethod
|
||||
async def get_uid_by_cred(cls, cred: str) -> str | None:
|
||||
result = await cls.select_data_by_cred(cred)
|
||||
return getattr(result, 'uid') if result else None
|
||||
|
||||
@classmethod
|
||||
async def update_user_attr_by_uid(
|
||||
cls,
|
||||
uid: str,
|
||||
attr: str,
|
||||
value: str,
|
||||
) -> bool:
|
||||
retcode = -1
|
||||
if await cls.data_exist(uid=uid):
|
||||
retcode = await cls.update_data_by_uid(
|
||||
uid, cls.bot_id, None, **{attr: value}
|
||||
)
|
||||
return not bool(retcode)
|
||||
|
||||
|
||||
class ArknightsPush(Push, table=True):
|
||||
uid: str | None = Field(default=None, title='明日方舟UID')
|
||||
|
Loading…
x
Reference in New Issue
Block a user