From dfa6436f28fa826d10baa32b6a836d1b96c1f38d Mon Sep 17 00:00:00 2001 From: lvwenzhuo Date: Fri, 28 Jul 2023 11:09:09 +0800 Subject: [PATCH 1/2] Fix captcha problem in daily sign and daily note --- .env.prod | 3 + .../plugins/Paimon_Autobbs/sign_handle.py | 108 ++++++++++++++--- .../plugins/Paimon_DailyNote/handler.py | 110 ++++++++++++++++-- 3 files changed, 195 insertions(+), 26 deletions(-) diff --git a/.env.prod b/.env.prod index 82e48b65..81c9a774 100644 --- a/.env.prod +++ b/.env.prod @@ -5,3 +5,6 @@ SUPERUSERS=["123456"] # 超级用户 NICKNAME=["派蒙", "bot"] # 机器人的昵称 COMMAND_START=[""] # 命令前缀,根据需要自行修改 COMMAND_SEP=[""] # 命令分隔符 +CAPTCHA_ENABLED=true#启用anti-captcha.com打码平台 +CAPTCHA_API_KEY="YOUR-API-KEY"#验证码打码平台api key +CAPTCHA_API_ENDPOINT="YOUR-API-PLATFORM"#验证码打码平台api调用网址 \ No newline at end of file diff --git a/LittlePaimon/plugins/Paimon_Autobbs/sign_handle.py b/LittlePaimon/plugins/Paimon_Autobbs/sign_handle.py index 0451cbc4..a1253496 100644 --- a/LittlePaimon/plugins/Paimon_Autobbs/sign_handle.py +++ b/LittlePaimon/plugins/Paimon_Autobbs/sign_handle.py @@ -5,6 +5,7 @@ from collections import defaultdict from typing import Tuple, Union +import nonebot from nonebot import get_bot from LittlePaimon.config import config @@ -15,12 +16,12 @@ from .draw import SignResult, draw_result SIGN_ACTION_API = 'https://api-takumi.mihoyo.com/event/bbs_sign_reward/sign' -GEETEST_HEADER = {"Accept": "*/*", +GEETEST_HEADER = {"Accept": "*/*", "X-Requested-With": "com.mihoyo.hyperion", - "User-Agent": 'Mozilla/5.0 (Linux; Android 12; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Version/4.0 Chrome/103.0.5060.129 Mobile Safari/537.36 miHoYoBBS/2.35.2', - "Referer": "https://webstatic.mihoyo.com/", - "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" + "User-Agent": 'Mozilla/5.0 (Linux; Android 12; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Version/4.0 Chrome/103.0.5060.129 Mobile Safari/537.36 miHoYoBBS/2.35.2', + "Referer": "https://webstatic.mihoyo.com/", + "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" } sign_reward_list: dict = {} @@ -31,7 +32,7 @@ async def sign_action(user_id: str, uid: str) -> Union[dict, str]: resp = await aiorequests.post(SIGN_ACTION_API, headers=mihoyo_sign_headers(cookie_info.cookie), json={ 'act_id': 'e202009291139501', - 'uid': uid, + 'uid': uid, 'region': server_id }) data = resp.json() @@ -54,7 +55,7 @@ async def mhy_bbs_sign(user_id: str, uid: str) -> Tuple[SignResult, str]: if isinstance(sign_info, str): logger.info('米游社原神签到', '➤', {'用户': user_id, 'UID': uid}, '未绑定私人cookie或已失效', False) await MihoyoBBSSub.filter(user_id=user_id, uid=uid).delete() - return SignResult.FAIL, sign_info + return SignResult.INVALID, sign_info elif sign_info['data']['is_sign']: signed_days = sign_info['data']['total_sign_day'] - 1 logger.info('米游社原神签到', '➤', {'用户': user_id, 'UID': uid}, '今天已经签过了', True) @@ -62,6 +63,7 @@ async def mhy_bbs_sign(user_id: str, uid: str) -> Tuple[SignResult, str]: return SignResult.DONE, f'UID{uid}今天已经签过了,获得的奖励为\n{sign_reward_list[signed_days]["name"]}*{sign_reward_list[signed_days]["cnt"]}' else: return SignResult.DONE, f'UID{uid}今天已经签过了' + message = None for i in range(3): sign_data = await sign_action(user_id, uid) if isinstance(sign_data, str): @@ -87,13 +89,84 @@ async def mhy_bbs_sign(user_id: str, uid: str) -> Tuple[SignResult, str]: else: return SignResult.SUCCESS, '签到成功' else: - wait_time = random.randint(90, 120) - logger.info('米游社原神签到', '➤', {'用户': user_id, 'UID': uid}, f'出现验证码,等待{wait_time}秒后进行第{i + 1}次尝试绕过', False) - await asyncio.sleep(wait_time) - logger.info('米游社原神签到', '➤', {'用户': user_id, 'UID': uid}, '尝试3次签到失败,无法绕过验证码', False) - return SignResult.FAIL, f'{uid}签到失败,无法绕过验证码' + result, message = await geetest_verify(user_id=user_id, uid=uid, sign_data=sign_data) + if result == SignResult.INVALID: + return SignResult.INVALID, f'{uid}签到失败,你的UID{uid}的cookie疑似失效了' + elif result == SignResult.FAIL: + logger.warning('米游社原神签到', '➤', f'{uid}第{i}次签到失败,正在重试,报错信息:{message}') + elif result == SignResult.SUCCESS: + return SignResult.SUCCESS, '签到成功' + else: + logger.warning('米游社原神签到', '➤', f'{uid}第{i}次签到失败,正在重试,报错信息:{message}') + return SignResult.FAIL, f'{uid}签到失败,报错信息{message}' +async def sign_action_after_geetest(user_id: str, + uid: str, + geetest_challenge: str, + geetest_validate: str, + geetest_seccode: str): + server_id = 'cn_qd01' if uid[0] == '5' else 'cn_gf01' + cookie_info = await PrivateCookie.get_or_none(user_id=user_id, uid=uid) + headers = mihoyo_sign_headers(cookie_info.cookie) + headers["x-rpc-validate"] = geetest_validate + headers["x-rpc-challenge"] = geetest_challenge + headers["x-rpc-seccode"] = geetest_seccode + resp = await aiorequests.post(url=SIGN_ACTION_API, headers=headers, + json={ + 'act_id': 'e202009291139501', + 'uid': uid, + 'region': server_id + }) + data = resp.json() + if await check_retcode(data, cookie_info, user_id, uid): + return data + else: + return f'你的UID{uid}的cookie疑似失效了' + + +async def geetest_verify(user_id: str, uid: str, sign_data: dict): + global_config = nonebot.get_driver().config + captcha_enable = global_config.captcha_enabled + superuser = global_config.superusers + if not captcha_enable: + logger.info('米游社原神签到➤', + '未开启验证码绕过,请添加captcha_api_key并根据需求修改geetest_verify(), sign_action_after_geetest(), ' + 'geetest_handle_init(), 以及geetest_handle_finish()函数') + if user_id not in superuser: + return SignResult.FAIL, f'{uid}签到失败,请联系管理员检查后台日志并开启验证码绕过' + else: + return SignResult.FAIL, f'{uid}签到失败,请检查后台日志开启验证码绕过' + api_key = global_config.captcha_api_key + api_endpoint = global_config.captcha_api_endpoint + gt = sign_data['data']['gt'] + challenge = sign_data['data']['challenge'] + page_url = f'https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true&act_id' \ + f'=e202009291139501<uid={uid}' + token, err_message = await geetest_handle_init(api_key=api_key, gt=gt, challenge=challenge, + page_url=page_url, api_endpoint=api_endpoint) + if err_message == "": + sign_result = await sign_action_after_geetest(user_id=user_id, + uid=uid, + geetest_challenge=token['challenge'], + geetest_seccode=token['seccode'], + geetest_validate=token['validate']) + if sign_result == f'你的UID{uid}的cookie疑似失效了': + return SignResult.INVALID, f'你的UID{uid}的cookie疑似失效了' + else: + return SignResult.SUCCESS, '签到成功' + else: + return SignResult.FAIL, f'{uid}平台返回错误{err_message}' + + +async def geetest_handle_init(api_key: str, + api_endpoint: str, + gt: str, + challenge: str, + page_url: str): + # TODO: Please modify this function according to the captcha resolver you are using + raise NotImplementedError + @scheduler.scheduled_job('cron', hour=config.auto_sign_hour, minute=config.auto_sign_minute, misfire_grace_time=10) async def _(): @@ -111,7 +184,8 @@ async def bbs_auto_sign(): if not subs: # 如果没有米游社原神签到订阅,则不执行签到任务 return - logger.info('米游社原神签到', f'开始执行米游社自动签到,共{len(subs)}个任务,预计花费{len(subs) * 2}分钟') + logger.info('米游社原神签到', + f'开始执行米游社自动签到,共{len(subs)}个任务,预计花费{len(subs) * 2}分钟') sign_result_group = defaultdict(list) sign_result_private = defaultdict(list) for sub in subs: @@ -120,13 +194,13 @@ async def bbs_auto_sign(): if sub.user_id != sub.group_id: sign_result_group[sub.group_id].append({ 'user_id': sub.user_id, - 'uid': sub.uid, - 'result': result, - 'reward': msg.split('\n')[-1] if result in [SignResult.SUCCESS, SignResult.DONE] else '' + 'uid': sub.uid, + 'result': result, + 'reward': msg.split('\n')[-1] if result in [SignResult.SUCCESS, SignResult.DONE] else '' }) else: sign_result_private[sub.user_id].append({ - 'uid': sub.uid, + 'uid': sub.uid, 'result': result, 'reward': msg.split('\n')[-1] if result in [SignResult.SUCCESS, SignResult.DONE] else '' }) diff --git a/LittlePaimon/plugins/Paimon_DailyNote/handler.py b/LittlePaimon/plugins/Paimon_DailyNote/handler.py index 9fef91fc..e7d727b2 100644 --- a/LittlePaimon/plugins/Paimon_DailyNote/handler.py +++ b/LittlePaimon/plugins/Paimon_DailyNote/handler.py @@ -4,16 +4,19 @@ import re import time +import nonebot import pytz from nonebot import get_bot from nonebot.adapters.onebot.v11 import Message from nonebot.params import CommandArg, Depends from LittlePaimon.config import config -from LittlePaimon.database import DailyNoteSub, Player, LastQuery +from LittlePaimon.database import DailyNoteSub, Player, LastQuery, PrivateCookie from LittlePaimon.utils import logger, scheduler -from LittlePaimon.utils.api import get_mihoyo_private_data +from LittlePaimon.utils.api import get_mihoyo_private_data, mihoyo_headers, get_cookie, DAILY_NOTE_API, \ + mihoyo_sign_headers, mihoyo_verify_verification_headers from .draw import draw_daily_note_card +from ...utils.requests import aiorequests def SubList() -> dict: @@ -45,6 +48,75 @@ async def get_subs(**kwargs) -> str: return f'会在{result.strip(",")}时向你发送提醒' if result else '当前没有订阅' +async def geetest_verify_verification(api: str, cookie: str, validate: dict): + geetest_challenge = validate['challenge'] + geetest_seccode = validate['seccode'] + geetest_validate = validate['validate'] + body = { + "geetest_challenge": geetest_challenge, + "geetest_seccode": geetest_seccode, + "geetest_validate": geetest_validate + } + header = mihoyo_verify_verification_headers(cookie=cookie, request_body=body) + url = api + "/game_record/app/card/wapi/verifyVerification" + resp = await aiorequests.post(url=url, headers=header, json=body) + return resp.json() + + +async def geetest_create_verification(api: str, cookie: str): + url = api + "/game_record/app/card/wapi/createVerification?is_high=true" + header = mihoyo_headers(cookie=cookie, q="is_high=true") + result = await aiorequests.get(url=url, headers=header) + return result.json() + + +async def geetest_handle_init(api_key: str, + api_endpoint: str, + gt: str, + challenge: str, + page_url: str): + # TODO: Please modify this function according to the captcha resolver you are using + raise NotImplementedError + + +async def geetest_handle(user_id: str, uid: str, ssbq_data: dict): + global_config = nonebot.get_driver().config + captcha_enable = global_config.captcha_enabled + superuser = global_config.superusers + if not captcha_enable: + if user_id not in superuser: + logger.warning('原神实时便签', '➤', '非特权用户试图绕过验证码失败:未开启验证码绕过') + return 1, f'服务器返回1034,疑似验证码。请联系管理员开启验证码绕过程序。' + else: + logger.warning('原神实时便签', '➤', '特权用户试图绕过验证码失败:未开启验证码绕过') + return 1, f'服务器返回1034,疑似验证码,请配置验证码绕过程序。' + cookie_info = await get_cookie(user_id=user_id, uid=uid, check=True) + if not cookie_info: + logger.warning('原神实时便签', '➤', '用户试图绕过验证码失败:cookie失效') + return 1, f'当前uid{uid}cookie已失效,请使用[原神扫码绑定]/[ysb]绑定私人cookie/联系管理员添加私人cookie或公共cookie' + api_endpoint = 'https://api-takumi-record.mihoyo.com' + geetest_create = await geetest_create_verification(api=api_endpoint, cookie=cookie_info.cookie) + captcha_api_endpoint = "" + captcha_api_key = global_config.captcha_api_key + gt = geetest_create['data']['gt'] + challenge = geetest_create['data']['challenge'] + page_url = "https://webstatic.mihoyo.com/app/community-game-records/" + token, err_message = await geetest_handle_init(api_key=captcha_api_key, + api_endpoint=captcha_api_endpoint, + gt=gt, + challenge=challenge, + page_url=page_url) + if err_message != "": + logger.warning('原神实时便签', '➤', f'用户试图绕过验证码失败:打码平台返回错误{err_message}') + return 1, f'{uid}查询实时便签失败,米哈游服务器返回1034疑似验证码,绕过验证码失败:{err_message}' + geetest_verify = await geetest_verify_verification(api=api_endpoint, validate=token, cookie=cookie_info.cookie) + if geetest_verify['retcode'] != 0: + logger.warning('原神实时便签', '➤', + f'用户试图绕过验证码失败:米哈游服务器返回验证码结果{geetest_verify["message"]}') + return 1, f'{uid}查询实时便签失败,米哈游服务器返回1034疑似验证码,绕过验证码失败:米哈游服务器返回验证码结果{geetest_verify["message"]}' + return 0, challenge + + async def handle_ssbq(player: Player): await LastQuery.update_last_query(player.user_id, player.uid) data = await get_mihoyo_private_data(player.uid, player.user_id, 'daily_note') @@ -54,7 +126,21 @@ async def handle_ssbq(player: Player): elif data['retcode'] == 1034: logger.info('原神实时便签', '➤', {'用户': player.user_id, 'UID': player.uid}, '获取数据失败,状态码为1034,疑似验证码', False) - return f'{player.uid}遇验证码阻拦,需手动前往米游社进行验证后才能继续使用\n' + status, result = await geetest_handle(user_id=player.user_id, uid=player.uid, ssbq_data=data) + if status == 0: + data = await get_mihoyo_private_data(uid=player.uid, user_id=player.user_id, mode='daily_note', + geetest_challenge=result) + logger.info('原神实时便签', '➤', {'用户': player.user_id, 'UID': player.uid}, '获取数据成功', True) + try: + img = await draw_daily_note_card(data['data'], player.uid) + logger.info('原神实时便签', '➤➤', {'用户': player.user_id, 'UID': player.uid}, '绘制图片成功', True) + return img + except Exception as e: + logger.info('原神实时便签', '➤➤', {'用户': player.user_id, 'UID': player.uid}, f'绘制图片失败,{e}', + False) + return f'{player.uid}绘制图片失败,{e}\n' + else: + return result elif data['retcode'] != 0: logger.info('原神实时便签', '➤', {'用户': player.user_id, 'UID': player.uid}, f'获取数据失败,code为{data["retcode"]}, msg为{data["message"]}', False) @@ -84,7 +170,8 @@ async def check_note(): if not (subs := await DailyNoteSub.all()): return time_now = time.time() - logger.info('原神实时便签', f'开始执行定时检查,共{len(subs)}个任务,预计花费{round(6 * len(subs) / 60, 2)}分钟') + logger.info('原神实时便签', + f'开始执行定时检查,共{len(subs)}个任务,预计花费{round(6 * len(subs) / 60, 2)}分钟') for sub in subs: limit_num = 5 if sub.resin_num and sub.coin_num else 3 if sub.today_remind_num <= limit_num and ( @@ -93,11 +180,13 @@ async def check_note(): tzinfo=pytz.timezone('Asia/Shanghai'))))): data = await get_mihoyo_private_data(sub.uid, str(sub.user_id), 'daily_note') if isinstance(data, str): - logger.info('原神实时便签', '➤', {'用户': sub.user_id, 'UID': sub.uid}, 'Cookie未绑定或已失效,删除任务', False) + logger.info('原神实时便签', '➤', {'用户': sub.user_id, 'UID': sub.uid}, 'Cookie未绑定或已失效,删除任务', + False) try: if sub.remind_type == 'group': await get_bot().send_group_msg(group_id=sub.group_id, - message=f'[CQ:at,qq={sub.user_id}]你的UID{sub.uid}️未绑定Cookie或已失效,无法继续为你检查实时便签') + message=f'[CQ:at,qq={sub.user_id}]你的UID{sub.uid}️未绑定Cookie' + f'或已失效,无法继续为你检查实时便签') else: await get_bot().send_private_msg(user_id=sub.user_id, message=f'你的UID{sub.uid}未绑定Cookie或已失效,无法继续为你检查实时便签') @@ -119,9 +208,11 @@ async def check_note(): result += f'银币达到了{str(data["data"]["current_home_coin"])},' result_log += '银币' if result_log: - logger.info('原神实时便签', '➤', {'用户': sub.user_id, 'UID': sub.uid}, f'{result_log}达到了阈值,发送提醒', True) + logger.info('原神实时便签', '➤', {'用户': sub.user_id, 'UID': sub.uid}, + f'{result_log}达到了阈值,发送提醒', True) else: - logger.info('原神实时便签', '➤➤', {'用户': sub.user_id, 'UID': sub.uid}, '检查完成,未达到阈值', True) + logger.info('原神实时便签', '➤➤', {'用户': sub.user_id, 'UID': sub.uid}, '检查完成,未达到阈值', + True) if result: sub.last_remind_time = datetime.datetime.now() sub.today_remind_num += 1 @@ -134,7 +225,8 @@ async def check_note(): await get_bot().send_private_msg(user_id=sub.user_id, message=f'⚠️你的UID{sub.uid}{result}记得清理哦⚠️') except Exception as e: - logger.info('原神实时便签', '➤➤', {'用户': sub.user_id, 'UID': sub.uid}, f'发送提醒失败,{e}', False) + logger.info('原神实时便签', '➤➤', {'用户': sub.user_id, 'UID': sub.uid}, f'发送提醒失败,{e}', + False) # 等待一会再检查下一个,防止检查过快 await asyncio.sleep(random.randint(4, 8)) logger.info('原神实时便签', f'树脂检查完成,共花费{round((time.time() - time_now) / 60, 2)}分钟') From 803b3c59a5e0ce53d7c9bb72396edfa042ef7e51 Mon Sep 17 00:00:00 2001 From: lvwenzhuo Date: Fri, 28 Jul 2023 11:53:43 +0800 Subject: [PATCH 2/2] Update api to sync with 918f496e --- .env.prod | 2 +- LittlePaimon/plugins/Paimon_Autobbs/draw.py | 3 +- LittlePaimon/utils/api.py | 73 ++++++++++++++------- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/.env.prod b/.env.prod index 81c9a774..4c6e8d82 100644 --- a/.env.prod +++ b/.env.prod @@ -5,6 +5,6 @@ SUPERUSERS=["123456"] # 超级用户 NICKNAME=["派蒙", "bot"] # 机器人的昵称 COMMAND_START=[""] # 命令前缀,根据需要自行修改 COMMAND_SEP=[""] # 命令分隔符 -CAPTCHA_ENABLED=true#启用anti-captcha.com打码平台 +CAPTCHA_ENABLED=true#启用打码平台 CAPTCHA_API_KEY="YOUR-API-KEY"#验证码打码平台api key CAPTCHA_API_ENDPOINT="YOUR-API-PLATFORM"#验证码打码平台api调用网址 \ No newline at end of file diff --git a/LittlePaimon/plugins/Paimon_Autobbs/draw.py b/LittlePaimon/plugins/Paimon_Autobbs/draw.py index 26ce5020..eff9a947 100644 --- a/LittlePaimon/plugins/Paimon_Autobbs/draw.py +++ b/LittlePaimon/plugins/Paimon_Autobbs/draw.py @@ -11,12 +11,13 @@ class SignResult(IntEnum): SUCCESS = auto() DONE = auto() FAIL = auto() + INVALID = auto() async def draw_result(group_id: int, data: list): img = PMImage(await load_image(RESOURCE_BASE_PATH / 'player_card' / 'white_bg.png')) success_list = [d for d in data if d['result'] in [SignResult.SUCCESS, SignResult.DONE]] - fail_list = [d['uid'] for d in data if d['result'] == SignResult.FAIL] + fail_list = [d['uid'] for d in data if d['result'] == SignResult.FAIL or d['uid'] for d in data if d['result'] == SignResult.INVALID] same_reward = defaultdict(list) for s in success_list: same_reward[s['reward']].append(s['uid']) diff --git a/LittlePaimon/utils/api.py b/LittlePaimon/utils/api.py index adf7c35b..fdce1127 100644 --- a/LittlePaimon/utils/api.py +++ b/LittlePaimon/utils/api.py @@ -65,7 +65,7 @@ def random_hex(length: int) -> str: :param length: 长度 :return: 随机字符串 """ - result = hex(random.randint(0, 16**length)).replace('0x', '').upper() + result = hex(random.randint(0, 16 ** length)).replace('0x', '').upper() if len(result) < length: result = '0' * (length - len(result)) + result return result @@ -104,6 +104,7 @@ def get_ds(q: str = '', b: dict = None, mhy_bbs_sign: bool = False) -> str: def get_old_version_ds(mhy_bbs: bool = False) -> str: """ 生成米游社旧版本headers的ds_token + salt对应的版本为2.35.2 """ if mhy_bbs: s = 'N50pqm7FSy2AkFz2B3TqtuZMJ5TOl3Ep' @@ -129,12 +130,34 @@ def mihoyo_headers(cookie, q='', b=None) -> dict: 'Cookie': cookie, 'x-rpc-app_version': "2.11.1", 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS ' - 'X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1', + 'X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1', 'x-rpc-client_type': '5', 'Referer': 'https://webstatic.mihoyo.com/', } +def mihoyo_verify_verification_headers(cookie: str, request_body: dict): + """ + 生成米游社实时便签验证码headers + :param cookie: cookie + :param request_body: post请求体 + :return headers + """ + header = { + 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS ' + 'X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1', + 'Cookie': cookie, + 'x-rpc-device_id': random_hex(32), + 'Origin': 'https://webstatic.mihoyo.com', + 'DS': get_ds(q="", b=request_body), + 'x-rpc-client_type': '5', + 'Referer': 'https://webstatic.mihoyo.com', + 'x-rpc-app_version': '2.11.1', + 'x-rpc-challenge_path': 'https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote' + } + return header + + def mihoyo_sign_headers(cookie: str, extra_headers: Optional[dict] = None) -> dict: """ 生成米游社签到headers @@ -144,7 +167,7 @@ def mihoyo_sign_headers(cookie: str, extra_headers: Optional[dict] = None) -> di """ header = { 'User_Agent': 'Mozilla/5.0 (Linux; Android 12; Unspecified Device) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Version/4.0 Chrome/103.0.5060.129 Mobile Safari/537.36 miHoYoBBS/2.35.2', + 'Version/4.0 Chrome/103.0.5060.129 Mobile Safari/537.36 miHoYoBBS/2.35.2', 'Cookie': cookie, 'x-rpc-device_id': random_hex(32), 'Origin': 'https://webstatic.mihoyo.com', @@ -152,7 +175,7 @@ def mihoyo_sign_headers(cookie: str, extra_headers: Optional[dict] = None) -> di 'DS': get_old_version_ds(mhy_bbs=True), 'x-rpc-client_type': '5', 'Referer': 'https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?bbs_auth_required=true&act_id' - '=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon', + '=e202009291139501&utm_source=bbs&utm_medium=mys&utm_campaign=icon', 'x-rpc-app_version': '2.35.2', } if extra_headers: @@ -222,7 +245,7 @@ async def check_retcode(data: dict, cookie_info, user_id: str, uid: str) -> bool async def get_cookie( - user_id: str, uid: str, check: bool = True, own: bool = False + user_id: str, uid: str, check: bool = True, own: bool = False ) -> Union[None, PrivateCookie, PublicCookie, CookieCache]: """ 获取可用的cookie @@ -233,14 +256,14 @@ async def get_cookie( """ query = Q(status=1) | Q(status=0) if check else Q(status=1) if private_cookie := await PrivateCookie.filter( - Q(Q(query) & Q(user_id=user_id) & Q(uid=uid)) + Q(Q(query) & Q(user_id=user_id) & Q(uid=uid)) ).first(): return private_cookie elif not own: if cache_cookie := await CookieCache.get_or_none(uid=uid): return cache_cookie elif private_cookie := await PrivateCookie.filter( - Q(Q(query) & Q(user_id=user_id)) + Q(Q(query) & Q(user_id=user_id)) ).first(): return private_cookie else: @@ -270,10 +293,10 @@ async def get_bind_game_info(cookie: str, mys_id: str): async def get_mihoyo_public_data( - uid: str, - user_id: Optional[str], - mode: Literal['abyss', 'player_card', 'role_detail'], - schedule_type: Optional[str] = '1', + uid: str, + user_id: Optional[str], + mode: Literal['abyss', 'player_card', 'role_detail'], + schedule_type: Optional[str] = '1', ): server_id = 'cn_qd01' if uid[0] == '5' else 'cn_gf01' check = True @@ -319,18 +342,19 @@ async def get_mihoyo_public_data( async def get_mihoyo_private_data( - uid: str, - user_id: Optional[str], - mode: Literal['role_skill', 'month_info', 'daily_note', 'sign_info', 'sign_action'], - role_id: Optional[str] = None, - month: Optional[str] = None, + uid: str, + user_id: Optional[str], + mode: Literal['role_skill', 'month_info', 'daily_note', 'sign_info', 'sign_action'], + role_id: Optional[str] = None, + month: Optional[str] = None, + geetest_challenge: str = None ): server_id = 'cn_qd01' if uid[0] == '5' else 'cn_gf01' cookie_info = await get_cookie(user_id, uid, True, True) if not cookie_info: return ( - '未绑定私人cookie,绑定方法二选一:\n1.通过米游社扫码绑定:\n请发送指令[原神扫码绑定]\n2.获取cookie的教程:\ndocs.qq.com/doc/DQ3JLWk1vQVllZ2Z1\n获取后,使用[ysb cookie]指令绑定' - + (f'或前往{config.CookieWeb_url}网页添加绑定' if config.CookieWeb_enable else '') + '未绑定私人cookie,绑定方法二选一:\n1.通过米游社扫码绑定:\n请发送指令[原神扫码绑定]\n2.获取cookie的教程:\ndocs.qq.com/doc/DQ3JLWk1vQVllZ2Z1\n获取后,使用[ysb cookie]指令绑定' + + (f'或前往{config.CookieWeb_url}网页添加绑定' if config.CookieWeb_enable else '') ) if mode == 'role_skill': data = await aiorequests.get( @@ -351,11 +375,12 @@ async def get_mihoyo_private_data( params={"month": month, "bind_uid": uid, "bind_region": server_id}, ) elif mode == 'daily_note': + headers = mihoyo_headers(q=f'role_id={uid}&server={server_id}', cookie=cookie_info.cookie) + if geetest_challenge is not None: + headers["x-rpc-challenge"] = geetest_challenge data = await aiorequests.get( url=DAILY_NOTE_API, - headers=mihoyo_headers( - q=f'role_id={uid}&server={server_id}', cookie=cookie_info.cookie - ), + headers=headers, params={"server": server_id, "role_id": uid}, ) elif mode == 'sign_info': @@ -368,7 +393,7 @@ async def get_mihoyo_private_data( 'Referer': 'https://webstatic.mihoyo.com/', 'Cookie': cookie_info.cookie, 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS ' - 'X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1', + 'X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/2.11.1', }, params={'act_id': 'e202009291139501', 'region': server_id, 'uid': uid}, ) @@ -392,7 +417,7 @@ async def get_sign_reward_list() -> dict: headers = { 'x-rpc-app_version': '2.11.1', 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (' - 'KHTML, like Gecko) miHoYoBBS/2.11.1', + 'KHTML, like Gecko) miHoYoBBS/2.11.1', 'x-rpc-client_type': '5', 'Referer': 'https://webstatic.mihoyo.com/', } @@ -442,7 +467,7 @@ async def get_cookie_token_by_stoken(stoken: str, mys_id: str) -> Optional[str]: async def get_authkey_by_stoken( - user_id: str, uid: str + user_id: str, uid: str ) -> Tuple[Optional[str], bool, Optional[PrivateCookie]]: """ 根据stoken获取authkey