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