diff --git a/API_TitleServer.py b/API_TitleServer.py index fc7227b..aeafb03 100644 --- a/API_TitleServer.py +++ b/API_TitleServer.py @@ -11,6 +11,7 @@ from ctypes import c_int32 from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from Config import * +from typing import Optional import certifi # 舞萌DX 2024 @@ -70,62 +71,53 @@ def getSDGBApiHash(api): # 有空做一下 Hash 的彩虹表? return hashlib.md5((api+"MaimaiChn"+ObfuscateParam).encode()).hexdigest() -def apiSDGB(data:str, targetApi:str, userAgentExtraData:str, noLog:bool=False, timeout:int=5): +def apiSDGB( + data: str, + targetApi: str, + userAgentExtraData: str, + noLog: bool = False, + timeout: int = 5, + useProxy: bool = False, + proxyUrl: Optional[str] = None +) -> str: """ - 舞萌DX 2024 API 通讯用函数 + 舞萌DX API 通讯用函数(增强版) :param data: 请求数据 :param targetApi: 使用的 API :param userAgentExtraData: UA 附加信息,机台相关则为狗号(如A63E01E9564),用户相关则为 UID :param noLog: 是否不记录日志 + :param timeout: 请求超时时间(秒) + :param useProxy: 是否使用代理 + :param proxyUrl: 代理地址(如果使用代理) + :return: 解码后的响应数据 """ maxRetries = 3 agentExtra = str(userAgentExtraData) - aes = aes_pkcs7(AesKey,AesIV) + aes = aes_pkcs7(AesKey, AesIV) # Assuming aes_pkcs7, AesKey, AesIV are defined elsewhere + endpoint = "https://maimai-gm.wahlap.com:42081/Maimai2Servlet/" - data = bytes(data, encoding="utf-8") - data_def = zlib.compress(data) - data_enc = aes.encrypt(data_def) - endpoint = "https://maimai-gm.wahlap.com:42081/Maimai2Servlet/" - r = httpx.post( - endpoint + getSDGBApiHash(targetApi), - headers = { - "User-Agent": f"{getSDGBApiHash(targetApi)}#{agentExtra}", - "Content-Type": "application/json", - "Mai-Encoding": "1.50", - "Accept-Encoding": "", - "Charset": "UTF-8", - "Content-Encoding": "deflate", - "Expect": "100-continue" - }, - data = data_enc - ) - resp_enc = r.content - try: - resp_def = aes.decrypt(resp_enc) - except: - resp_def = resp_enc - return zlib.decompress(resp_def).decode('utf-8') - - # Begin Build + # Prepare request data requestDataFinal = aes.encrypt(zlib.compress(data.encode('utf-8'))) - # End Build - endpoint = "https://maimai-gm.wahlap.com:42081/Maimai2Servlet/" + if not noLog: logger.debug(f"开始请求 {targetApi},以 {data}") retries = 0 while retries < maxRetries: try: - if useProxy: - # 使用代理 - logger.debug("使用代理") + # Configure HTTP client + if useProxy and proxyUrl: + if not noLog: + logger.debug("使用代理") httpClient = httpx.Client(proxy=proxyUrl, verify=False) else: - # 不使用代理 - logger.debug("不使用代理") + if not noLog: + logger.debug("不使用代理") httpClient = httpx.Client(verify=False) - responseDataRaw = httpClient.post( - url=endpoint + getSDGBApiHash(targetApi), + + # Send request + response = httpClient.post( + url=endpoint + getSDGBApiHash(targetApi), # Assuming getSDGBApiHash is defined headers={ "User-Agent": f"{getSDGBApiHash(targetApi)}#{agentExtra}", "Content-Type": "application/json", @@ -136,57 +128,69 @@ def apiSDGB(data:str, targetApi:str, userAgentExtraData:str, noLog:bool=False, t "Expect": "100-continue" }, content=requestDataFinal, - # 经测试,加 Verify 之后速度慢好多,因此建议选择性开 - #verify=certifi.where(), - #verify=False, timeout=timeout ) if not noLog: - logger.info(f"{targetApi} 请求结果: {responseDataRaw.status_code}") + logger.info(f"{targetApi} 请求结果: {response.status_code}") - if responseDataRaw.status_code == 200: - logger.debug("200 OK!") - else: - errorMessage = f"请求失败: {responseDataRaw.status_code}" + if response.status_code != 200: + errorMessage = f"请求失败: {response.status_code}" logger.error(errorMessage) raise SDGBRequestError(errorMessage) - responseContentRaw = responseDataRaw.content - + # Process response + responseContentRaw = response.content try: responseContentDecrypted = aes.decrypt(responseContentRaw) - logger.debug(f"成功解密响应!") - except: - logger.warning(f"解密失败,得到的原始响应: {responseContentRaw}") + if not noLog: + logger.debug("成功解密响应!") + except Exception as e: + logger.warning(f"解密失败,原始响应: {responseContentRaw}, 错误: {e}") raise SDGBResponseError("解密失败") + + try: - zlib.decompress(responseContentDecrypted).decode('utf-8') - logger.debug("成功解压响应!") - except: - logger.warning(f"无法解压,解密的原始响应: {responseContentDecrypted}") + # 检查 ResponseContentDecrypted 是否为 zlib 压缩格式 + if not responseContentDecrypted.startswith(b'\x78\x9c'): + logger.warning("Not Zlib. Not decompressed.") + raise Exception(f"响应内容不是 zlib 压缩格式, 内容: {responseContentDecrypted}") + responseContentFinal = zlib.decompress(responseContentDecrypted).decode('utf-8') + if not noLog: + logger.debug("成功解压响应!") + logger.debug(f"响应: {responseContentFinal}") + return responseContentFinal + except zlib.error as e: + logger.warning(f"解压失败,原始响应: {responseContentDecrypted}, 错误: {e}") raise SDGBResponseError("解压失败") - - if not noLog: - logger.debug(f"响应: {responseContentDecrypted}") - return responseContentDecrypted.decode('utf-8') - - # 异常处理 + # If decompression fails after attempts, trigger a retry of the entire request + retries += 1 + if retries < maxRetries: + logger.warning(f"解压失败,将重试请求 (第 {retries + 1}/{maxRetries} 次)") + time.sleep(2) + continue + raise SDGBResponseError("多次尝试后仍无法解压响应") + except SDGBRequestError as e: - # 请求格式错误,不需要重试 - raise SDGBRequestError("请求格式错误") + # Request format error, no retry + logger.error(f"请求格式错误: {e}") + raise except SDGBResponseError as e: - # 响应解析错误,这种有一定可能是我们的问题,所以只重试一次 - logger.warning(f"将重试一次 Resp Err: {e}") - retries += 2 - time.sleep(2) - except Exception as e: - # 其他错误,重试多次 - logger.warning(f"将开始重试请求. {e}") + # Response parsing error, retry once more + logger.warning(f"响应错误,将重试: {e}") retries += 1 time.sleep(2) + except Exception as e: + # Other errors, retry + logger.warning(f"请求失败,将重试: {e}") + retries += 1 + time.sleep(2) + + finally: + if 'httpClient' in locals(): + httpClient.close() raise SDGBApiError("重试多次仍然无法成功请求服务器")