From 31361541fd0cab411841b1a7d54545cd2cb8dafb Mon Sep 17 00:00:00 2001
From: Remik1r3n <remik1r3n@outlook.com>
Date: Wed, 11 Jun 2025 09:52:45 +0800
Subject: [PATCH] 2nd ver

---
 API_TitleServer.py | 142 +++++++++++++++++++++++----------------------
 1 file changed, 73 insertions(+), 69 deletions(-)

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("重试多次仍然无法成功请求服务器")