新增终末地签到+更新gitignore

This commit is contained in:
2026-05-08 16:36:01 +08:00
parent 3bd95a4fb6
commit 3996889769
4 changed files with 180 additions and 42 deletions
+2
View File
@@ -1 +1,3 @@
/config/user_info_local.json /config/user_info_local.json
/.venv/
/data/ArknightsStoryJson-main/
+8 -6
View File
@@ -34,12 +34,12 @@ def start_data_retrieve():
return 'No data supplied' return 'No data supplied'
if input_param_dict.get('admin_key') != admin_key: if input_param_dict.get('admin_key') != admin_key:
return '403 Forbidden' return '403 Forbidden'
if ark_back.is_retrieving_data(): if ark_back.is_retrieving_data:
return json.dumps({'status': 1, 'data': 'already started'}) return json.dumps({'status': 1, 'data': 'already started'})
else: else:
ark_back.is_retrieving_data = True ark_back.is_retrieving_data = True
s1 = threading.Thread(target=data_retrieve_thread) s1 = threading.Thread(target=data_retrieve_thread)
s2 = threading.Thread(target=skland_sign_in_thread()) s2 = threading.Thread(target=skland_sign_in_thread)
s1.start() s1.start()
s2.start() s2.start()
return json.dumps({'status': 0, 'data': ark_back.is_retrieving_data}) return json.dumps({'status': 0, 'data': ark_back.is_retrieving_data})
@@ -55,7 +55,9 @@ def change_status():
if input_param_dict.get('status') == 'on going' and not ark_back.is_retrieving_data: if input_param_dict.get('status') == 'on going' and not ark_back.is_retrieving_data:
ark_back.is_retrieving_data = True ark_back.is_retrieving_data = True
s1 = threading.Thread(target=data_retrieve_thread) s1 = threading.Thread(target=data_retrieve_thread)
s2 = threading.Thread(target=skland_sign_in_thread)
s1.start() s1.start()
s2.start()
return json.dumps({'status': 0, 'data': ark_back.is_retrieving_data}) return json.dumps({'status': 0, 'data': ark_back.is_retrieving_data})
elif input_param_dict.get('status') == 'breaking' and ark_back.is_retrieving_data: elif input_param_dict.get('status') == 'breaking' and ark_back.is_retrieving_data:
ark_back.is_retrieving_data = False ark_back.is_retrieving_data = False
@@ -112,7 +114,7 @@ def skland_sign_in_thread():
try: try:
last_run = None last_run = None
next_run_time = random_time(480, 720) next_run_time = random_time(480, 720)
ark_back.logger.info(f'Next skland sign-in time: {next_run_time}') ark_back.logger.info(f'Next daily sign-in time: {next_run_time}')
while 1: while 1:
if not ark_back.is_retrieving_data: if not ark_back.is_retrieving_data:
break break
@@ -120,12 +122,12 @@ def skland_sign_in_thread():
now = datetime.datetime.now() now = datetime.datetime.now()
if now.strftime('%H:%M') == next_run_time and last_run != now.date(): if now.strftime('%H:%M') == next_run_time and last_run != now.date():
next_run_time = random_time(480, 720) next_run_time = random_time(480, 720)
ark_back.skland_sign_new() ark_back.daily_sign_new()
last_run = now.date() last_run = now.date()
except Exception as e: except Exception as e:
print(e, traceback.format_exc()) print(e, traceback.format_exc())
ark_back.logger.error(traceback.format_exc()) ark_back.logger.error(traceback.format_exc())
new_send_email(target_email_list, 'Error in ArkHelper Skland Sign-in', str(traceback.format_exc())) new_send_email(target_email_list, 'Error in ArkHelper Daily Sign-in', str(traceback.format_exc()))
finally: finally:
ark_back.logger.info('Data Retrieve Stopped') ark_back.logger.info('Data Retrieve Stopped')
@@ -139,7 +141,7 @@ threading.Thread(target=data_retrieve_thread).start()
if __name__ == '__main__': if __name__ == '__main__':
print('start') print('start')
ark_back.skland_sign_new() ark_back.daily_sign_new()
# logger = LoggerClass().get_log(logging.INFO) # logger = LoggerClass().get_log(logging.INFO)
# ark_back = ArkHelperBackend(logger=logger) # ark_back = ArkHelperBackend(logger=logger)
# ark_back.update_gacha_data() # ark_back.update_gacha_data()
+154 -31
View File
@@ -19,6 +19,12 @@ token_env = os.environ.get('TOKEN')
# 现在想做什么? # 现在想做什么?
current_type = os.environ.get('SKYLAND_TYPE') current_type = os.environ.get('SKYLAND_TYPE')
ARKNIGHTS_APP_CODE = 'arknights'
ENDFIELD_APP_CODE = 'endfield'
ARKNIGHTS_GAME_ID = 1
ENDFIELD_SIGN_PLATFORM = '3'
ENDFIELD_SIGN_VNAME = '1.0.0'
http_local = threading.local() http_local = threading.local()
header = { header = {
'cred': '', 'cred': '',
@@ -44,6 +50,7 @@ header_for_sign = {
# 签到url # 签到url
sign_url = "https://zonai.skland.com/api/v1/game/attendance" sign_url = "https://zonai.skland.com/api/v1/game/attendance"
endfield_sign_url = "https://zonai.skland.com/api/v1/game/endfield/attendance"
# 绑定的角色url # 绑定的角色url
binding_url = "https://zonai.skland.com/api/v1/game/player/binding" binding_url = "https://zonai.skland.com/api/v1/game/player/binding"
# 验证码url # 验证码url
@@ -57,6 +64,13 @@ grant_code_url = "https://as.hypergryph.com/user/oauth2/v2/grant"
# 使用认证代码获得cred # 使用认证代码获得cred
cred_code_url = "https://zonai.skland.com/web/v1/user/auth/generate_cred_by_code" cred_code_url = "https://zonai.skland.com/web/v1/user/auth/generate_cred_by_code"
header_for_endfield_sign = {
'platform': ENDFIELD_SIGN_PLATFORM,
'timestamp': '',
'dId': '',
'vName': ENDFIELD_SIGN_VNAME
}
def config_logger(): def config_logger():
current_date = date.today().strftime('%Y-%m-%d') current_date = date.today().strftime('%Y-%m-%d')
@@ -103,7 +117,7 @@ def config_logger():
requests.post = post requests.post = post
def generate_signature(token: str, path, body_or_query): def generate_signature(token: str, path, body_or_query, sign_header_template=None):
""" """
获得签名头 获得签名头
接口地址+方法为Get请求?用query否则用body+时间戳+ 请求头的四个重要参数(dIdplatformtimestampvName.toJSON() 接口地址+方法为Get请求?用query否则用body+时间戳+ 请求头的四个重要参数(dIdplatformtimestampvName.toJSON()
@@ -117,7 +131,7 @@ def generate_signature(token: str, path, body_or_query):
# 总是说请勿修改设备时间,怕不是yj你的服务器有问题吧,所以这里特地-2 # 总是说请勿修改设备时间,怕不是yj你的服务器有问题吧,所以这里特地-2
t = str(int(time.time()) - 2) t = str(int(time.time()) - 2)
token = token.encode('utf-8') token = token.encode('utf-8')
header_ca = json.loads(json.dumps(header_for_sign)) header_ca = json.loads(json.dumps(sign_header_template or header_for_sign))
header_ca['timestamp'] = t header_ca['timestamp'] = t
header_ca_str = json.dumps(header_ca, separators=(',', ':')) header_ca_str = json.dumps(header_ca, separators=(',', ':'))
s = path + body_or_query + t + header_ca_str s = path + body_or_query + t + header_ca_str
@@ -204,7 +218,13 @@ def get_cred(grant):
return resp['data'] return resp['data']
def get_binding_list(): def setup_cred_session(cred_resp):
http_local.token = cred_resp['token']
http_local.header = header.copy()
http_local.header['cred'] = cred_resp['cred']
def get_binding_list(target_app_code=ARKNIGHTS_APP_CODE):
v = [] v = []
resp = requests.get(binding_url, headers=get_sign_header(binding_url, 'get', None, http_local.header)).json() resp = requests.get(binding_url, headers=get_sign_header(binding_url, 'get', None, http_local.header)).json()
@@ -215,67 +235,170 @@ def get_binding_list():
os.remove(token_save_name) os.remove(token_save_name)
return [] return []
for i in resp['data']['list']: for i in resp['data']['list']:
if i.get('appCode') != 'arknights': if i.get('appCode') != target_app_code:
continue continue
v.extend(i.get('bindingList')) v.extend(i.get('bindingList') or [])
return v return v
def is_duplicate_sign_message(message):
return '请勿重复签到' in (message or '') or 'Please do not sign in again!' in (message or '')
def format_arknights_sign_message(character, resp):
role_name = character.get('nickName') or '未知角色'
channel_name = character.get('channelName') or '未知服务器'
if resp['code'] != 0:
error_message = resp.get('message') or '未知错误'
if is_duplicate_sign_message(error_message):
return True, f'角色{role_name}({channel_name})今日已签到,请勿重复签到'
return False, f'角色{role_name}({channel_name})签到失败了!原因:{error_message}'
awards = resp.get('data', {}).get('awards') or []
award_text = []
for award in awards:
resource = award.get('resource') or {}
resource_name = resource.get('name')
if not resource_name:
continue
award_text.append(f'{resource_name}×{award.get("count") or 1}')
if award_text:
return True, f'角色{role_name}({channel_name})签到成功,获得了{"".join(award_text)}'
return True, f'角色{role_name}({channel_name})签到成功'
def get_endfield_role(binding):
role = binding.get('defaultRole')
if role:
return role
roles = binding.get('roles') or []
return roles[0] if roles else None
def get_endfield_sign_header(role_str):
parsed_url = parse.urlparse(endfield_sign_url)
sign, header_ca = generate_signature(http_local.token, parsed_url.path, '', header_for_endfield_sign)
sign_header = http_local.header.copy()
sign_header['sign'] = sign
sign_header['platform'] = header_ca['platform']
sign_header['timestamp'] = header_ca['timestamp']
sign_header['dId'] = header_ca['dId']
sign_header['vName'] = header_ca['vName']
sign_header['sk-game-role'] = role_str
sign_header['Content-Type'] = 'application/json'
return sign_header
def format_endfield_awards(resp_data):
award_ids = resp_data.get('awardIds') or []
resource_map = resp_data.get('resourceInfoMap') or {}
award_text = []
for award in award_ids:
award_id = award.get('id')
if award_id is None:
continue
resource = resource_map.get(award_id) or resource_map.get(str(award_id))
if not resource:
continue
count = award.get('count') or resource.get('count') or 1
award_text.append(f'{resource.get("name", award_id)}×{count}')
return award_text
def format_endfield_sign_message(binding, resp):
role = get_endfield_role(binding) or {}
role_name = role.get('nickname') or binding.get('nickName') or '未知角色'
channel_name = role.get('serverName') or binding.get('channelName') or '未知服务器'
if resp['code'] != 0:
error_message = resp.get('message') or '未知错误'
if is_duplicate_sign_message(error_message):
return True, f'终末地角色{role_name}({channel_name})今日已签到,请勿重复签到'
return False, f'终末地角色{role_name}({channel_name})签到失败了!原因:{error_message}'
award_text = format_endfield_awards(resp.get('data', {}))
if award_text:
return True, f'终末地角色{role_name}({channel_name})签到成功,获得了{"".join(award_text)}'
return True, f'终末地角色{role_name}({channel_name})签到成功'
def list_awards(game_id, uid): def list_awards(game_id, uid):
resp = requests.get(sign_url, headers=http_local.header, params={'gameId': game_id, 'uid': uid}).json() resp = requests.get(sign_url, headers=http_local.header, params={'gameId': game_id, 'uid': uid}).json()
print(resp) print(resp)
def do_sign(cred_resp): def do_sign(cred_resp):
http_local.token = cred_resp['token'] setup_cred_session(cred_resp)
http_local.header = header.copy()
http_local.header['cred'] = cred_resp['cred']
characters = get_binding_list() characters = get_binding_list()
for i in characters: for i in characters:
body = { body = {
'gameId': 1, 'gameId': ARKNIGHTS_GAME_ID,
'uid': i.get('uid') 'uid': i.get('uid')
} }
# list_awards(1, i.get('uid')) # list_awards(1, i.get('uid'))
resp = requests.post(sign_url, headers=get_sign_header(sign_url, 'post', body, http_local.header), resp = requests.post(sign_url, headers=get_sign_header(sign_url, 'post', body, http_local.header),
json=body).json() json=body).json()
if resp['code'] != 0: _, msg = format_arknights_sign_message(i, resp)
print(f'角色{i.get("nickName")}({i.get("channelName")})签到失败了!原因:{resp.get("message")}') print(msg)
continue
awards = resp['data']['awards']
for j in awards:
res = j['resource']
print(
f'角色{i.get("nickName")}({i.get("channelName")})签到成功,获得了{res["name"]}×{j.get("count") or 1}'
)
def single_sign(cred_resp): def single_sign(cred_resp):
http_local.token = cred_resp['token'] setup_cred_session(cred_resp)
http_local.header = header.copy()
http_local.header['cred'] = cred_resp['cred']
characters = get_binding_list() characters = get_binding_list()
code, msg = -1, 'Default Error' if not characters:
return '未绑定明日方舟角色,已跳过', 0
code = 0
messages = []
for i in characters: for i in characters:
body = { body = {
'gameId': 1, 'gameId': ARKNIGHTS_GAME_ID,
'uid': i.get('uid') 'uid': i.get('uid')
} }
# list_awards(1, i.get('uid')) # list_awards(1, i.get('uid'))
resp = requests.post(sign_url, headers=get_sign_header(sign_url, 'post', body, http_local.header), resp = requests.post(sign_url, headers=get_sign_header(sign_url, 'post', body, http_local.header),
json=body).json() json=body).json()
if resp['code'] != 0: success, msg = format_arknights_sign_message(i, resp)
msg = f'角色{i.get("nickName")}({i.get("channelName")})签到失败了!原因:{resp.get("message")}' messages.append(msg)
if not success:
code = -1
return '\n'.join(messages), code
def single_endfield_sign(cred_resp):
setup_cred_session(cred_resp)
bindings = get_binding_list(ENDFIELD_APP_CODE)
if not bindings:
return '未绑定终末地角色,已跳过', 0
code = 0
messages = []
for binding in bindings:
role = get_endfield_role(binding)
if not role:
messages.append('终末地角色信息缺失,已跳过')
code = -1 code = -1
continue continue
awards = resp['data']['awards']
for j in awards: role_id = role.get('roleId')
res = j['resource'] server_id = role.get('serverId')
msg = f'角色{i.get("nickName")}({i.get("channelName")})签到成功,获得了{res["name"]}×{j.get("count") or 1}' if role_id is None or server_id is None:
code = 0 messages.append('终末地角色信息不完整,已跳过')
return msg, code code = -1
continue
role_str = f'{ENDFIELD_SIGN_PLATFORM}_{role_id}_{server_id}'
resp = requests.post(endfield_sign_url, headers=get_endfield_sign_header(role_str), json=None).json()
success, msg = format_endfield_sign_message(binding, resp)
messages.append(msg)
if not success:
code = -1
return '\n'.join(messages), code
def save(token): def save(token):
+16 -5
View File
@@ -10,7 +10,7 @@ from pathlib import Path
from src.util.utils import hg_get_new_user_token, load_user_config, validate_token from src.util.utils import hg_get_new_user_token, load_user_config, validate_token
from src.My_Logger.project_logger import LoggerClass from src.My_Logger.project_logger import LoggerClass
from src.SKland_Auto_Sign.new_auto_sign import get_cred_by_token, single_sign from src.SKland_Auto_Sign.new_auto_sign import get_cred_by_token, single_endfield_sign, single_sign
PROJECT_PATH = Path(__file__).parent.parent.parent.absolute() PROJECT_PATH = Path(__file__).parent.parent.parent.absolute()
DATA_PATH = os.path.join(PROJECT_PATH, 'data', 'ArkHelperData') DATA_PATH = os.path.join(PROJECT_PATH, 'data', 'ArkHelperData')
@@ -137,17 +137,28 @@ class ArkHelperBackend:
self.single_user_login(phone) self.single_user_login(phone)
self.save_data(payment_status=False, gacha_status=False) self.save_data(payment_status=False, gacha_status=False)
def skland_sign_new(self): def _run_sign_jobs(self, sign_jobs):
for user, content in self.user_data_dict.items(): for user, content in self.user_data_dict.items():
token = content.get('token') token = content.get('token')
if token: if token:
try: try:
msg, code = single_sign(get_cred_by_token(token)) cred_resp = get_cred_by_token(token)
self.logger.info(f'{msg}: {code}') for sign_name, sign_handler in sign_jobs:
msg, code = sign_handler(cred_resp)
self.logger.info(f'{user} {sign_name}签到结果: {msg} [{code}]')
except Exception as ex: except Exception as ex:
self.logger.error(f'签到失败,原因:{str(ex)}') self.logger.error(f'签到失败,原因:{str(ex)}')
time.sleep(random.randint(100, 200)) time.sleep(random.randint(100, 200))
def skland_sign_new(self):
self._run_sign_jobs((('明日方舟', single_sign),))
def endfield_sign_new(self):
self._run_sign_jobs((('终末地', single_endfield_sign),))
def daily_sign_new(self):
self._run_sign_jobs((('明日方舟', single_sign), ('终末地', single_endfield_sign)))
def update_gacha_data(self): def update_gacha_data(self):
for phone in self.user_data_dict: for phone in self.user_data_dict:
gacha_history_data = self.user_data_dict[phone]['gacha'] gacha_history_data = self.user_data_dict[phone]['gacha']
@@ -209,7 +220,7 @@ if __name__ == '__main__':
# ak_back = ArkHelperBackend() # ak_back = ArkHelperBackend()
# ak_back.update_gacha_data() # ak_back.update_gacha_data()
# ak_back.update_payment_data() # ak_back.update_payment_data()
# ak_back.skland_sign_new() # ak_back.daily_sign_new()
login_url = 'https://ak.hypergryph.com/user/login' login_url = 'https://ak.hypergryph.com/user/login'
session = requests.session() session = requests.session()