Initial commit: Add maimaiDX API web application with AimeDB scanning and logging features

This commit is contained in:
kejiz
2025-09-18 10:19:08 +08:00
commit 4e83f159f0
84 changed files with 14012 additions and 0 deletions

302
backend/HelperMisc.py Normal file
View File

@@ -0,0 +1,302 @@
# 杂项助手函数
# 主要用于当作模块使用的时候的一些生活质量提升
import rapidjson as json
from loguru import logger
from HelperGetUserThing import implGetUser_
import unicodedata
from HelperLogInOut import apiLogin, apiLogout, generateTimestamp
from MyConfig import testUid
def numberToLetter(number):
"""
将数字转换为字母1-26 to A-Z
"""
if 1 <= number <= 26:
return chr(number + 64)
else:
return None
def maimaiVersionToHumanReadable(romVersion: str, dataVersion: str) -> str:
try:
romVersionList = romVersion.split(".")
dataVersionList = dataVersion.split(".")
except Exception as e:
logger.warning(f"无法解析版本号: {romVersion} {dataVersion},错误:{e}")
return "无效版本号:无法解析"
try:
romVersionList = [int(i) for i in romVersionList]
dataVersionList = [int(i) for i in dataVersionList]
except Exception as e:
logger.warning(f"无法解析版本号: {romVersion} {dataVersion},错误:{e}")
return "无效版本号:无法解读数字"
finalVersionList = []
finalVersionList.append(romVersionList[0])
# quirk
minorVer = max(romVersionList[1], dataVersionList[1])
if minorVer == 0:
finalVersionList.append("00")
else:
finalVersionList.append(minorVer)
finalVersionLetter = numberToLetter(max(romVersionList[2], dataVersionList[2]))
if finalVersionLetter:
finalVersionLetter = f"-{finalVersionLetter}"
else:
finalVersionLetter = ""
finalVersionList.append(finalVersionLetter)
if int(finalVersionList[1]) < 30:
versionStringPrefix = "CH"
else:
versionStringPrefix = "CN"
finalVersionString = f"{versionStringPrefix}{finalVersionList[0]}.{finalVersionList[1]}{finalVersionList[2]}"
return finalVersionString
levelIdDict = {"绿": 0, "": 1, "": 2, "": 3, "": 4, "": 5}
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 103:
return "❌ 试图登录的账号 UID 无效,请检查账号是否正确。"
case _:
return "❌ 登录失败!这不应该发生,请反馈此问题。错误详情:" + loginResult
def checkTechnologyUseCount(userId: int) -> int:
"""猜测账号是否用了科技0没用过其他为用过"""
userData1 = implGetUser_("Data", userId)
userData = userData1.get("userData", {})
userRegion = implGetUser_("Region", userId)
userRegionList = userRegion.get("userRegionList", [])
playCount = userData.get("playCount", 0)
allRegionPlayCount = 0
for region in userRegionList:
allRegionPlayCount += region.get("playCount", 0)
logger.info(
f"用户 {userId} 的总游玩次数: {playCount}, 各地区游玩次数: {allRegionPlayCount}"
)
# 计算全部的 Region 加起来的游玩次数是否和 playCount 对不上,对不上就是用了科技
# 返回差值
return playCount - allRegionPlayCount
def getFriendlyUserData(userId: int) -> str:
"""生成一个(相对)友好的UserData的人话"""
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"最近登录版本: {maimaiVersionToHumanReadable(userData.get('lastRomVersion'), userData.get('lastDataVersion'))} "
result += f"最近登录地区: {userData.get('lastRegionName', '未知')}\n"
result += f"注册日期: {userData.get('firstPlayDate')} "
result += f"注册版本: {maimaiVersionToHumanReadable(userData.get('firstRomVersion'), 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:
"""生成一个人类可读的地区数据"""
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(userData) -> str:
"""生成一个人类可读的 UserData 的数据(比较详细)"""
userId = userData.get("userId")
userData = userData.get("userData", {})
banState = userData.get("banState")
logger.debug(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: "未知25",
26: "云南",
27: "浙江",
28: "广西",
29: "内蒙古",
30: "宁夏",
31: "新疆",
32: "西藏",
}
if __name__ == "__main__":
# test version string convert
print(maimaiVersionToHumanReadable("1.20.0", "1.0.0"))
print(maimaiVersionToHumanReadable("1.41.00", "1.40.11"))
print(maimaiVersionToHumanReadable("1.00.00", "1.00.00"))
userId = testUid
currentLoginTimestamp = generateTimestamp()
loginResult = apiLogin(currentLoginTimestamp, userId)
if loginResult["returnCode"] != 1:
logger.info("登录失败")
exit()
try:
logger.info(checkTechnologyUseCount(userId))
# logger.info(apiQueryTicket(userId))
finally:
logger.info(apiLogout(currentLoginTimestamp, userId))
# logger.warning("Error")