From 8da425391ae54a7dcb07453ccb0905c53c3ac856 Mon Sep 17 00:00:00 2001 From: 91c0e59d-6161-45ab-8aa4-2371574db28f <91c0e59d-6161-45ab-8aa4-2371574db28f@bank-of-china.com> Date: Thu, 22 Jan 2026 23:35:46 +0800 Subject: [PATCH] update to 1.53 --- sdgb/PowerOn.py | 66 +++++++++++++++++++ sdgb/UpsertMusic.py | 68 +++++++++++++++++++ sdgb/payload.py | 157 +++++++++++++++++++++++++++++++------------- 3 files changed, 246 insertions(+), 45 deletions(-) create mode 100644 sdgb/PowerOn.py create mode 100644 sdgb/UpsertMusic.py diff --git a/sdgb/PowerOn.py b/sdgb/PowerOn.py new file mode 100644 index 0000000..a5d673e --- /dev/null +++ b/sdgb/PowerOn.py @@ -0,0 +1,66 @@ +import httpx +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad +from urllib.parse import unquote + +def enc(key, iv, data): + cipher = AES.new(key, AES.MODE_CBC, iv) + encrypted = cipher.encrypt(data) + return encrypted + +def dec(key, iv, data): + de_cipher = AES.new(key, AES.MODE_CBC, iv) + decrypted = de_cipher.decrypt(data) + return decrypted + +def hello(): + key = bytes([ 47, 63, 106, 111, 43, 34, 76, 38, 92, 67, 114, 57, 40, 61, 107, 71 ]) + #key = bytes([ 45, 97, 53, 55, 85, 88, 52, 121, 57, 47, 104, 40, 73, 109, 65, 81 ]) + iv = bytes.fromhex('00000000000000000000000000000000') + ua = 'SDGB;Windows/Lite' + #ua = 'SDHJ;Windows/Lite' + + # 构建 payload + content = bytes([0] * 16) + b'title_id=SDGB&title_ver=1.52&client_id=A63E01E6149' + print(f"Content: {content}") + + header = bytes.fromhex('00000000000000000000000000000000') + bytes_data = pad(header + content, 16) + encrypted = enc(key, iv, bytes_data) + + # --- HTTPX 修改部分 --- + headers = { + 'User-Agent': ua, + 'Pragma': 'DFI' + } + + try: + # 发送 POST 请求 + # urllib3 的 body 参数在 httpx 中对应 content (用于二进制数据) + r = httpx.post( + 'http://at.sys-allnet.cn/net/initialize', + content=encrypted, + headers=headers + ) + + # 检查响应状态码 (可选,但在 httpx 中推荐) + # r.raise_for_status() + + # urllib3 的 r.data 在 httpx 中对应 r.content + resp_data = r.content + + # 解密逻辑保持不变 + # 注意:这里逻辑是用响应的前16字节作为IV,同时解密整个数据,然后丢弃前16字节 + if len(resp_data) >= 16: + decrypted = dec(key, resp_data[:16], resp_data) + decrypted_bytes = decrypted[16:] + decrypted_str = unquote(decrypted_bytes.decode('UTF-8'), 'utf-8') + print(f"Decrypted: {decrypted_str}") + else: + print("Response data too short.") + + except httpx.RequestError as e: + print(f"An error occurred while requesting: {e}") + +if __name__ == '__main__': + hello() \ No newline at end of file diff --git a/sdgb/UpsertMusic.py b/sdgb/UpsertMusic.py new file mode 100644 index 0000000..b3f8509 --- /dev/null +++ b/sdgb/UpsertMusic.py @@ -0,0 +1,68 @@ +import json +import asyncio +import httpx +import time +import logging +from sdgb import MaimaiClient + +from settings import userId, musicData +from chime import * +from payload import * + +maimai = MaimaiClient() + + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +async def run_workflow(self): + + async with httpx.AsyncClient(verify=False) as client: + + # Preview 探测 + + PreviewResponse = json.loads(await self.call_api(client, "GetUserPreviewApi", requestData_UserPreview, userId)) + if PreviewResponse["isLogin"] == True: + logger.error("已在他处登录。") + return + + # UserLogin + + LoginResponse = json.loads(await self.call_api(client, "UserLoginApi", requestData_UserLogin, userId)) + if LoginResponse["returnCode"] != 1: + logger.error("login failed.") + return + + loginId = LoginResponse['loginId'] + loginDate = LoginResponse['lastLoginDate'] + + # UserData 等 + + tasks = [ + self.call_api(client, "GetUserDataApi", requestData_UserData, userId), + self.call_api(client, "GetUserExtendApi", requestData_UserData, userId), + self.call_api(client, "GetUserOptionApi", requestData_UserData, userId), + self.call_api(client, "GetUserRatingApi", requestData_UserData, userId), + self.call_api(client, "GetUserChargeApi", requestData_UserData, userId), + self.call_api(client, "GetUserActivityApi", requestData_UserData, userId), + self.call_api(client, "GetUserMissionDataApi", requestData_UserData, userId), + ] + + GeneralUserInfo = await asyncio.gather(*tasks) + await asyncio.sleep(60) # 模拟游戏时间 + + # UserAll + + requestData_UserAll = UserAll_payload(loginId, loginDate, musicData, GeneralUserInfo) + + await self.call_api(client, "UpsertUserAllApi", requestData_UserAll, userId) + + # UserLogout + + await self.call_api(client, "UserLogoutApi", requestData_UserLogout, userId) + +if __name__ == "__main__": + asyncio.run(run_workflow(maimai)) \ No newline at end of file diff --git a/sdgb/payload.py b/sdgb/payload.py index f0f6dab..9440803 100644 --- a/sdgb/payload.py +++ b/sdgb/payload.py @@ -4,8 +4,14 @@ import json from datetime import datetime, timedelta from encrypt import CalcRandom from settings import * +from chime import * import logging + +userId = qr_api(qrCode)['userID'] +token = qr_api(qrCode)['token'] + + logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' @@ -17,7 +23,9 @@ TimeStamp = int(time.time()) requestData_UserPreview = { "userId": userId, - "segaIdAuthKey":"" + "segaIdAuthKey":"", + "token": token, + "clientId": clientId } requestData_UserLogin = { @@ -26,9 +34,11 @@ requestData_UserLogin = { "regionId": regionId, "placeId": placeId, "clientId": clientId, - "dateTime": 1767000000, + "dateTime": TimeStamp - 600, + "loginDateTime": TimeStamp, "isContinue": False, - "genericFlag":0 + "genericFlag": 0, + "token": token } requestData_UserData = { @@ -41,22 +51,32 @@ requestData_UserLogout = { "regionId": regionId, "placeId": placeId, "clientId": clientId, - "dateTime": 1767000000, + "loginDateTime": TimeStamp, "type": 1 } -def UserPlaylog_payload(loginId: int, musicData: dict, userData: str): +def UserAll_payload(loginId: int, loginDate: str, musicData: dict, GeneralUserInfo: list): - userData = json.loads(userData) - - requestData_UserPlaylog = { + userData = json.loads(GeneralUserInfo[0]) + userExtend = json.loads(GeneralUserInfo[1]) + userOption = json.loads(GeneralUserInfo[2]) + userRating = json.loads(GeneralUserInfo[3]) + userChargeList = json.loads(GeneralUserInfo[4]) + userActivity = json.loads(GeneralUserInfo[5]) + userMissionDataList = json.loads(GeneralUserInfo[6]) + + requestData_UserAll = { "userId": userId, + "playlogId": loginId, + "isEventMode": False, + "isFreePlay": False, + "loginDateTime": TimeStamp, "userPlaylogList": [ { "userId": 0, "orderId": 0, "playlogId": loginId, - "version": 1052000, + "version": 1053000, "placeId": placeId, "placeName": placeName, "loginDate": TimeStamp, @@ -102,34 +122,34 @@ def UserPlaylog_payload(loginId: int, musicData: dict, userData: str): "deluxscore": musicData['deluxscoreMax'], "scoreRank": musicData['scoreRank'], "maxCombo": 0, - "totalCombo": 987, + "totalCombo": 128, "maxSync": 0, "totalSync": 0, - "tapCriticalPerfect": 0, + "tapCriticalPerfect": 101, "tapPerfect": 0, "tapGreat": 0, "tapGood": 0, - "tapMiss": 590, - "holdCriticalPerfect": 0, + "tapMiss": 0, + "holdCriticalPerfect": 9, "holdPerfect": 0, "holdGreat": 0, "holdGood": 0, - "holdMiss": 21, - "slideCriticalPerfect": 0, + "holdMiss": 0, + "slideCriticalPerfect": 4, "slidePerfect": 0, "slideGreat": 0, "slideGood": 0, - "slideMiss": 176, + "slideMiss": 0, "touchCriticalPerfect": 0, "touchPerfect": 0, "touchGreat": 0, "touchGood": 0, "touchMiss": 0, - "breakCriticalPerfect": 0, + "breakCriticalPerfect": 1, "breakPerfect": 0, "breakGreat": 0, "breakGood": 0, - "breakMiss": 200, + "breakMiss": 0, "isTap": True, "isHold": True, "isSlide": True, @@ -159,29 +179,11 @@ def UserPlaylog_payload(loginId: int, musicData: dict, userData: str): "trialPlayAchievement": -1, "extNum1": 0, "extNum2": 0, - "extNum4": 3020, + "extNum4": 101, "extBool1": False, "extBool2": False } - ] - } - return requestData_UserPlaylog - -def UserAll_payload(loginId: int, loginDate: str, musicData: dict, GeneralUserInfo: list): - - userData = json.loads(GeneralUserInfo[0]) - userExtend = json.loads(GeneralUserInfo[1]) - userOption = json.loads(GeneralUserInfo[2]) - userRating = json.loads(GeneralUserInfo[3]) - userChargeList = json.loads(GeneralUserInfo[4]) - userActivity = json.loads(GeneralUserInfo[5]) - userMissionDataList = json.loads(GeneralUserInfo[6]) - - requestData_UserAll = { - "userId": userId, - "playlogId": loginId, - "isEventMode": False, - "isFreePlay": False, + ], "upsertUserAll": { "userData": [ { @@ -207,8 +209,8 @@ def UserAll_payload(loginId: int, loginDate: str, musicData: dict, GeneralUserIn "charaSlot": userData['userData']['charaSlot'], "charaLockSlot": userData['userData']['charaLockSlot'], "contentBit": userData['userData']['contentBit'], - "playCount": userData['userData']['playCount'], - "currentPlayCount": userData['userData']['currentPlayCount'], + "playCount": userData['userData']['playCount'] + 1, + "currentPlayCount": userData['userData']['currentPlayCount'] + 1, "renameCredit": userData['userData']['renameCredit'], "mapStock": userData['userData']['mapStock'], "eventWatchedDate": userData['userData']['eventWatchedDate'], @@ -281,10 +283,75 @@ def UserAll_payload(loginId: int, loginDate: str, musicData: dict, GeneralUserIn "userCourseList": [], "userFriendSeasonRankingList": [], "userChargeList": userChargeList['userChargeList'], - "userFavoriteList": [], + "userFavoriteList": [ + {"itemKind": 3,"itemIdList": []}, + {"itemKind": 1,"itemIdList": []}, + {"itemKind": 2,"itemIdList": []}, + {"itemKind": 10,"itemIdList": []}, + {"itemKind": 11,"itemIdList": []} + ], "userActivityList": [userActivity['userActivity']], - "userMissionDataList": userMissionDataList['userMissionDataList'], - "userWeeklyData": userMissionDataList['userWeeklyData'], + "userMissionDataList": [ + { + "type": userMissionDataList['userMissionDataList'][0]['type'], + "difficulty": userMissionDataList['userMissionDataList'][0]['difficulty'], + "targetGenreId": userMissionDataList['userMissionDataList'][0]['targetGenreId'], + "targetGenreTableId": userMissionDataList['userMissionDataList'][0]['targetGenreTableId'], + "conditionGenreId": userMissionDataList['userMissionDataList'][0]['conditionGenreId'], + "conditionGenreTableId": userMissionDataList['userMissionDataList'][0]['conditionGenreTableId'], + "clearFlag": userMissionDataList['userMissionDataList'][0]['clearFlag'] + }, + { + "type": userMissionDataList['userMissionDataList'][1]['type'], + "difficulty": userMissionDataList['userMissionDataList'][1]['difficulty'], + "targetGenreId": userMissionDataList['userMissionDataList'][1]['targetGenreId'], + "targetGenreTableId": userMissionDataList['userMissionDataList'][1]['targetGenreTableId'], + "conditionGenreId": userMissionDataList['userMissionDataList'][1]['conditionGenreId'], + "conditionGenreTableId": userMissionDataList['userMissionDataList'][1]['conditionGenreTableId'], + "clearFlag": userMissionDataList['userMissionDataList'][1]['clearFlag'] + }, + { + "type": userMissionDataList['userMissionDataList'][2]['type'], + "difficulty": userMissionDataList['userMissionDataList'][2]['difficulty'], + "targetGenreId": userMissionDataList['userMissionDataList'][2]['targetGenreId'], + "targetGenreTableId": userMissionDataList['userMissionDataList'][2]['targetGenreTableId'], + "conditionGenreId": userMissionDataList['userMissionDataList'][2]['conditionGenreId'], + "conditionGenreTableId": userMissionDataList['userMissionDataList'][2]['conditionGenreTableId'], + "clearFlag": userMissionDataList['userMissionDataList'][2]['clearFlag'] + }, + { + "type": userMissionDataList['userMissionDataList'][3]['type'], + "difficulty": userMissionDataList['userMissionDataList'][3]['difficulty'], + "targetGenreId": userMissionDataList['userMissionDataList'][3]['targetGenreId'], + "targetGenreTableId": userMissionDataList['userMissionDataList'][3]['targetGenreTableId'], + "conditionGenreId": userMissionDataList['userMissionDataList'][3]['conditionGenreId'], + "conditionGenreTableId": userMissionDataList['userMissionDataList'][3]['conditionGenreTableId'], + "clearFlag": userMissionDataList['userMissionDataList'][3]['clearFlag'] + }, + { + "type": userMissionDataList['userMissionDataList'][4]['type'], + "difficulty": userMissionDataList['userMissionDataList'][4]['difficulty'], + "targetGenreId": userMissionDataList['userMissionDataList'][4]['targetGenreId'], + "targetGenreTableId": userMissionDataList['userMissionDataList'][4]['targetGenreTableId'], + "conditionGenreId": userMissionDataList['userMissionDataList'][4]['conditionGenreId'], + "conditionGenreTableId": userMissionDataList['userMissionDataList'][4]['conditionGenreTableId'], + "clearFlag": userMissionDataList['userMissionDataList'][4]['clearFlag'] + }, + { + "type": userMissionDataList['userMissionDataList'][5]['type'], + "difficulty": userMissionDataList['userMissionDataList'][5]['difficulty'], + "targetGenreId": userMissionDataList['userMissionDataList'][5]['targetGenreId'], + "targetGenreTableId": userMissionDataList['userMissionDataList'][5]['targetGenreTableId'], + "conditionGenreId": userMissionDataList['userMissionDataList'][5]['conditionGenreId'], + "conditionGenreTableId": userMissionDataList['userMissionDataList'][5]['conditionGenreTableId'], + "clearFlag": userMissionDataList['userMissionDataList'][5]['clearFlag'] + } + ], + "userWeeklyData": { + "lastLoginWeek": userMissionDataList['userWeeklyData']['lastLoginWeek'], + "beforeLoginWeek": userMissionDataList['userWeeklyData']['beforeLoginWeek'], + "friendBonusFlag": userMissionDataList['userWeeklyData']['friendBonusFlag'] + }, "userGamePlaylogList": [ { "playlogId": loginId, @@ -324,13 +391,13 @@ def UserAll_payload(loginId: int, loginDate: str, musicData: dict, GeneralUserIn "isNewItemList": "", "isNewMusicDetailList": "0", "isNewCourseList": "", - "isNewFavoriteList": "", + "isNewFavoriteList": "11111", "isNewFriendSeasonRankingList": "", "isNewUserIntimateList": "", "isNewFavoritemusicList": "", "isNewKaleidxScopeList": "" } } - + logger.info(f"🫥 [INFO] userId: '{userId}', loginId: '{loginId}', loginDate: '{loginDate}', timestamp: '{TimeStamp}'") return requestData_UserAll \ No newline at end of file