diff --git a/ActionLoginBonus.py b/ActionLoginBonus.py index 0610aa1..ee6b406 100644 --- a/ActionLoginBonus.py +++ b/ActionLoginBonus.py @@ -9,6 +9,10 @@ from API_TitleServer import apiSDGB from HelperLogInOut import apiLogin, apiLogout, generateTimestamp from HelperFullPlay import implFullPlayAction +class NoSelectedBonusError(Exception): + pass + + def apiQueryLoginBonus(userId:int) -> str: '''ログインボーナスを取得する API''' data = json.dumps({ @@ -65,13 +69,20 @@ def generateLoginBonusList(UserLoginBonusList, generateMode=1): generateMode は、ログインボーナスを生成する方法を指定します。 1: 選択したボーナスのみ MAX にする(選択したボーナスはないの場合は False を返す) 2: 全部 MAX にする - ''' # HDDから、ログインボーナスデータを読み込む # アップデートがある場合、このファイルを更新する必要があります # 必ず最新のデータを使用してください - with open('./Data/loginBonus.json', encoding='utf-8') as file: - cache = json.load(file) + try: + with open(loginBonusDBPath, encoding='utf-8') as file: + cache = json.load(file) + except FileNotFoundError: + try: + with open(loginBonusDBPathFallback, encoding='utf-8') as file: + cache = json.load(file) + except: + raise FileNotFoundError("ログインボーナスデータベースを読み込めません") + loginBonusIdList = [item['id'] for item in cache] logger.debug(f"ログインボーナスIDリスト: {loginBonusIdList}") @@ -100,8 +111,7 @@ def generateLoginBonusList(UserLoginBonusList, generateMode=1): } bonusList.append(data) if len(bonusList) == 0: - logger.warning("このユーザーはログインボーナスを選択していませんから失敗") - return False + raise NoSelectedBonusError("選択したログインボーナスがありません") elif generateMode == 2: #全部 MAX にする # 存在しているボーナスを追加 for item in UserLoginBonusList: @@ -131,8 +141,7 @@ def generateLoginBonusList(UserLoginBonusList, generateMode=1): } bonusList.append(data) else: - logger.error("generateMode は 1 または 2 でなければなりません") - return False + raise ValueError("generateMode は 1 または 2 でなければなりません") logger.debug(f"ログインボーナスリスト: {bonusList}") return bonusList diff --git a/Best50_To_Diving_Fish.py b/Best50_To_Diving_Fish.py index 691b81a..aebd08e 100644 --- a/Best50_To_Diving_Fish.py +++ b/Best50_To_Diving_Fish.py @@ -102,11 +102,13 @@ def isVaildFishToken(importToken:str): def implUserMusicToDivingFish(userId:int, fishImportToken:str): '''上传所有成绩到水鱼的参考实现''' + logger.info("Start to upload user music detail to DivingFish") userFullMusicDetailList = getUserFullMusicDetail(userId) + logger.info("Got UserData, Convert to Fish Format") divingFishData = maimaiUserMusicDetailToDivingFishFormat(userFullMusicDetailList) + logger.ionfo("Convert OK. Start to Update Fish Records") updateFishRecords(fishImportToken, divingFishData) - if __name__ == '__main__': if True: userId = testUid diff --git a/Config.py b/Config.py index 0cf9313..f4e779e 100644 --- a/Config.py +++ b/Config.py @@ -4,5 +4,11 @@ placeId = 3490 placeName = "赛博时空枣庄市中店" clientId = "A63E01E9564" +loginBonusDBPath = "./Data/loginBonusDB.json" +musicDBPath = "./Data/musicDB.json" + +loginBonusDBPathFallback = "./maimaiDX-Api/Data/loginBonusDB.json" +musicDBPathFallback = "./maimaiDX-Api/Data/musicDB.json" + # 日本精工,安全防漏 #from MyConfig import * diff --git a/Data/loginBonus.json b/Data/loginBonusDB.json similarity index 100% rename from Data/loginBonus.json rename to Data/loginBonusDB.json diff --git a/HelperGetUserMusicDetail.py b/HelperGetUserMusicDetail.py index 349d190..e4b1e0e 100644 --- a/HelperGetUserMusicDetail.py +++ b/HelperGetUserMusicDetail.py @@ -50,13 +50,13 @@ def parseUserFullMusicDetail(userFullMusicDetailList: list) -> dict: return musicDetailList if __name__ == '__main__': - userId = testUid5 + userId = 11088995 currentLoginTimestamp = generateTimestamp() - loginResult = apiLogin(currentLoginTimestamp, userId) + #loginResult = apiLogin(currentLoginTimestamp, userId) - if loginResult['returnCode'] != 1: - logger.info("登录失败") - exit() + #if loginResult['returnCode'] != 1: + # logger.info("登录失败") + # exit() try: userFullMusicDetailList = getUserFullMusicDetail(userId) parsedUserFullMusicDetail = parseUserFullMusicDetail(userFullMusicDetailList) diff --git a/HelperMisc.py b/HelperMisc.py new file mode 100644 index 0000000..8607970 --- /dev/null +++ b/HelperMisc.py @@ -0,0 +1,209 @@ +# 杂项助手函数 +# 主要用于当作模块使用的时候的一些生活质量提升 +import json +from loguru import logger +from HelperGetUserThing import implGetUser_ +import unicodedata + +levelIdDict = { + "绿": 0, + "黄": 1, + "红": 2, + "紫": 3, + "白": 4 +} + +def getHalfWidthString(s): + """全角转半角,舞萌ID用""" + return unicodedata.normalize('NFKC', s) + +def getHumanReadableLoginErrorCode(loginResult) -> str: + '''解析登录结果并且给出中文的报错解释''' + match loginResult['returnCode']: + case 1: + return False + case 100: + return "❌ 用户正在上机游玩,请下机后再试,或等待 15 分钟。" + case 102: + return "⚠️ 请在微信公众号内点击一次获取新的二维码,然后再试。" + case _: + return "❌ 登录失败!请反馈给作者。错误详情:"+ loginResult + +def getFriendlyUserData(userId:int) -> str: + '''生成一个人类可读的用户数据''' + userData1 = implGetUser_("Data", userId) + userData = userData1.get("userData", {}) + userRegion = implGetUser_("Region", userId) + banState = userData1.get("banState") + + result = f"用户: {getHalfWidthString(userData.get('userName', '未知'))}\n" + result += f"DX RATING: {userData.get('playerRating', '未知')} " + result += f"B35: {userData.get('playerOldRating', '未知')} " + result += f"B15: {userData.get('playerNewRating', '未知')}\n" + result += f"总游戏次数: {userData.get('playCount', '未知')} " + result += f"当前版本游戏次数: {userData.get('currentPlayCount', '未知')}\n" + result += f"最近登录时间: {userData.get('lastLoginDate')} " + result += f"最近登录版本: {userData.get('lastDataVersion', '未知')} " + result += f"最近登录地区: {userData.get('lastRegionName', '未知')}\n" + result += f"注册日期: {userData.get('firstPlayDate')} " + result += f"注册版本: {userData.get('firstDataVersion', '未知')}\n" + result += f"封号状态(banState): {banState}\n" + try: + logger.info(userRegion) + result += getHumanReadableRegionData(userRegion) + except Exception as e: + result += f"地区数据获取失败:{e}\n" + + return result + +def getHumanReadableRegionData(userRegion:str) -> str: + '''生成一个人类可读的地区数据''' + #Example Data: {"userId":11088995,"length":7,"userRegionList":[{"regionId":1,"playCount":1,"created":"2023-06-23 20:06:20"},{"regionId":8,"playCount":3,"created":"2024-06-05 22:57:41"},{"regionId":13,"playCount":7,"created":"2024-10-08 19:16:27"},{"regionId":16,"playCount":3,"created":"2024-08-02 11:54:29"},{"regionId":17,"playCount":46,"created":"2023-11-18 20:14:56"},{"regionId":22,"playCount":907,"created":"2022-08-18 20:19:08"},{"regionId":27,"playCount":18,"created":"2024-11-05 23:42:43"}]} + userRegionList = userRegion.get("userRegionList") + logger.info(userRegionList) + result = "" + for region in userRegionList: + regionName = WAHLAP_REGIONS.get(region['regionId'], '未知') + playCount = region['playCount'] + created = region['created'] + result += f"\n{regionName} 游玩次数: {playCount} 首次游玩: {created}" + return result + + +def getHumanReadablePreview(preview_json_content:str) -> str: + '''简单,粗略地解释 Preview 的 Json String 为人话。''' + previewData = json.loads(preview_json_content) + userName = getHalfWidthString(previewData['userName']) + playerRating = previewData['playerRating'] + finalString = f"用户名:{userName}\nDX RATING:{playerRating}\n" + return finalString + +def getHumanReadableLoginBonusList(jsonString: str): + '''生成一个人类可读的 Login Bonus 的列表''' + data = json.loads(jsonString) + + result = [] + for bonus in data["userLoginBonusList"]: + if not bonus["isComplete"]: # 过滤已经集满的 + line = f"BonusID {bonus['bonusId']} 已集 {bonus['point']} 点" + if bonus["isCurrent"]: # 如果是当前选中,追加标记 + line += "(当前选中)" + result.append(line) + + resultString = "" + for line in result: # 转成字符串 + resultString += line + "\n" + + return resultString + +def getHumanReadableTicketList(jsonString: str): + '''生成一个人类可读的 UserCharge 的列表''' + data = json.loads(jsonString) + + userId = data['userId'] + length = data['length'] + userChargeList = data['userChargeList'] + + result = f"UID: {userId} 票槽大小: {length} 所有记录:" + for currentItem in userChargeList: + chargeId = currentItem['chargeId'] + stock = currentItem['stock'] + purchaseDate = currentItem['purchaseDate'] + validDate = currentItem['validDate'] + + result += f"\nID: {chargeId} 持有: {stock}, 购买日期: {purchaseDate}, 有效期限: {validDate}" + + return result + +def getHumanReadableUserData(userDataJson:str) -> str: + loadedUserData = json.loads(userDataJson) + userId = loadedUserData.get("userId") + userData = loadedUserData.get("userData", {}) + banState = loadedUserData.get("banState") + logger.info(userData) + + result = f"用户名: {userData.get('userName', '未知')} " + result += f"UID: {userId}\n" + result += f"当前 RATING: {userData.get('playerRating', '未知')} " + result += f"B35: {userData.get('playerOldRating', '未知')} " + result += f"B15: {userData.get('playerNewRating', '未知')} " + result += f"最高 RATING: {userData.get('highestRating', '未知')}\n" + result += f"级别段位: {userData.get('gradeRank', '未知')} " + result += f"段位认定: {userData.get('courseRank', '未知')} " + result += f"友人对战段位: {userData.get('classRank', '未知')}\n" + result += f"总游戏次数: {userData.get('playCount', '未知')} " + result += f"当前版本游戏次数: {userData.get('currentPlayCount', '未知')}\n" + result += f"总DX分: {userData.get('totalDeluxscore', '未知')} " + result += f"绿谱总DX分: {userData.get('totalBasicDeluxscore', '未知')} " + result += f"黄谱总DX分: {userData.get('totalAdvancedDeluxscore', '未知')} " + result += f"红谱总DX分: {userData.get('totalExpertDeluxscore', '未知')} " + result += f"紫谱总DX分: {userData.get('totalMasterDeluxscore', '未知')} " + result += f"白谱总DX分: {userData.get('totalReMasterDeluxscore', '未知')}\n" + result += f"总SYNC: {userData.get('totalSync', '未知')} " + result += f"绿谱总SYNC: {userData.get('totalBasicSync', '未知')} " + result += f"黄谱总SYNC: {userData.get('totalAdvancedSync', '未知')} " + result += f"红谱总SYNC: {userData.get('totalExpertSync', '未知')} " + result += f"紫谱总SYNC: {userData.get('totalMasterSync', '未知')} " + result += f"白谱总SYNC: {userData.get('totalReMasterSync', '未知')}\n" + result += f"总分: {userData.get('totalAchievement', '未知')} " + result += f"绿谱总分: {userData.get('totalBasicAchievement', '未知')} " + result += f"黄谱总分: {userData.get('totalAdvancedAchievement', '未知')} " + result += f"红谱总分: {userData.get('totalExpertAchievement', '未知')} " + result += f"紫谱总分: {userData.get('totalMasterAchievement', '未知')} " + result += f"白谱总分: {userData.get('totalReMasterAchievement', '未知')}\n" + result += f"活动事件日期: {userData.get('eventWatchedDate')}\n" + result += f"最后 ROM 版本: {userData.get('lastRomVersion', '未知')}\n" + result += f"最后数据版本: {userData.get('lastDataVersion', '未知')}\n" + result += f"最后登录时间: {userData.get('lastLoginDate')}\n" + result += f"最后游戏时间: {userData.get('lastPlayDate')}\n" + result += f"最后双人登录时间: {userData.get('lastPairLoginDate')}\n" + result += f"最后免费游戏时间: {userData.get('lastTrialPlayDate')}\n" + result += f"最后游戏花费: {userData.get('lastPlayCredit', '未知')}\n" + result += f"最后地区 ID: {userData.get('lastRegionId', '未知')}\n" + result += f"最后地区名称: {userData.get('lastRegionName', '未知')}\n" + result += f"最后选择功能票: {userData.get('lastSelectTicket', '未知')}\n" + result += f"最后一次段位认定: {userData.get('lastSelectCourse', '未知')}\n" + result += f"最后一次 Course 计数: {userData.get('lastCountCourse', '未知')}\n" + result += f"注册 ROM 版本: {userData.get('firstRomVersion', '未知')}\n" + result += f"注册数据版本: {userData.get('firstDataVersion', '未知')}\n" + result += f"注册日期: {userData.get('firstPlayDate')}\n" + result += f"总觉醒: {userData.get('totalAwake', '未知')}\n" + result += f"签到日期: {userData.get('dailyCourseBonusDate')}\n" + result += f"跑图存储距离: {userData.get('mapStock', '未知')}m\n" + result += f"封号状态: {banState}\n" + return result + +WAHLAP_REGIONS = { + 1: '北京', + 2: '重庆', + 3: '上海', + 4: '天津', + 5: '安徽', + 6: '福建', + 7: '甘肃', + 8: '广东', + 9: '贵州', + 10: '海南', + 11: '河北', + 12: '黑龙江', + 13: '河南', + 14: '湖北', + 15: '湖南', + 16: '江苏', + 17: '江西', + 18: '吉林', + 19: '辽宁', + 20: '青海', + 21: '陕西', + 22: '山东', + 23: '山西', + 24: '四川', + 25: '(未知)', + 26: '云南', + 27: '浙江', + 28: '广西', + 29: '内蒙古', + 30: '宁夏', + 31: '新疆', + 32: '西藏', +} diff --git a/HelperUnlockThing.py b/HelperUnlockThing.py index ca21cd7..95a0896 100644 --- a/HelperUnlockThing.py +++ b/HelperUnlockThing.py @@ -24,3 +24,56 @@ def implUnlockThing(newUserItemList, userId: int, currentLoginTimestamp:int, cur }} result = implFullPlayAction(userId, currentLoginTimestamp, currentLoginResult, musicData, userAllPatches) return result + +itemKindDict = { + "PLATE": 1, # 姓名框 + "TITLE": 2, # 称号 + "ICON": 3, # 头像 +# "PRESENT": 4, # ? + "MUSIC": 5, # 歌 + "MUSIC_MASTER": 6, # 紫谱 + "MUSIC_RE_MASTER": 7,# 白谱 +# "MUSIC_STRONG": 8, # ? + "CHARACTER": 9, # 旅行伙伴 + "PARTNER": 10, # 搭档 + "FRAME": 11, # 背景板 + "TICKET": 12 # 功能票 +} + +itemKindzhCNDict = { + "姓名框": "PLATE", + "称号": "TITLE", + "头像": "ICON", +# "礼物": "PRESENT", + "歌": "MUSIC", + "紫谱": "MUSIC_MASTER", + "白谱": "MUSIC_RE_MASTER", +# "STRONG": "MUSIC_STRONG", + "旅行伙伴": "CHARACTER", + "搭档": "PARTNER", + "背景板": "FRAME", + "功能票": "TICKET" +} + +partnerList = { + "1": "迪拉熊", + "17": "青柠熊&柠檬熊", + "11": "乙姫", + "12": "拉兹", + "13": "雪纺", + "14": "莎露朵", + "15": "夏玛", + "16": "咪璐库", + "18": "乙姫(Splash)", + "19": "夏玛(UNiVERSE)", + "20": "咪璐库(UNiVERSE)", + "21": "小咪璐库", + "22": "百合咲美香", + "23": "拉兹(2023)", + "24": "雪纺(2023)", + "25": "莎露朵(2023)", + "26": "黒姫", + "27": "俊达萌", + "28": "乙姫(2024)", + "29": "青柠熊&柠檬熊(2024)" +} \ No newline at end of file diff --git a/MusicDB.py b/MusicDB.py index 88a1bf7..b2e3047 100644 --- a/MusicDB.py +++ b/MusicDB.py @@ -1,4 +1,5 @@ import rapidjson as json +from Config import * from typing import Dict, Union # 定义音乐数据库的类型注解 @@ -8,9 +9,15 @@ MusicDBType = Dict[int, Dict[str, Union[int, str]]] __all__ = ['musicDB'] # 读取并解析 JSON 文件 -with open('./maimaiDX-Api/Data/musicDB.json', 'r', encoding='utf-8') as f: - # 使用 json.load 直接从文件对象读取 JSON 数据 - data = json.load(f) +try: + with open(musicDBPath, 'r', encoding='utf-8') as f: + data = json.load(f) +except FileNotFoundError: + try: + with open(musicDBPathFallback, 'r', encoding='utf-8') as f: + data = json.load(f) + except: + raise FileNotFoundError("musicDB.json 文件不存在!") # 将 JSON 数据转换为指定格式的字典 musicDB: MusicDBType = {int(k): v for k, v in data.items()}