v0 plus附加功能
- 新增 unlockmusic.py:解锁(解禁)歌曲 + 解禁 Master / Re:Master 难度,整个流程复用 upsert.py 的 run() - 买票前置闸门 api/ChargeGuard.py:必须先跑过 GetUserCharge,且同进程只允许买一次 - buy_ticket.py 票源改为 GetUserCharge,不再调用 GetGameCharge - UpsertUserAll 与买票的冷却统一为 15 秒 - api/ 文件名统一 PascalCase(文件名 = 函数名) - 项目改名 Astral,README 加 Logo、鸣谢与开发者说明 - 自检 284 项
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
unlockmusic.py —— 主脚本:扫码登录 → 解锁(解禁)歌曲 → 登出
|
||||
================================================================================
|
||||
|
||||
**它没有自己的协议实现** —— 整个流程直接复用 `upsert.py` 的 `run()`:
|
||||
扫码 → 预览 → 登录 → UpsertUserAll → 落库回查 → 登出,一模一样。
|
||||
唯一多出来的东西是"要解锁哪些歌"这个入参。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
为什么解锁不用单独的接口
|
||||
--------------------------------------------------------------------------------
|
||||
游戏里解锁不是一次独立的网络请求,而是**往 userItemList 里补几条 UserItem**:
|
||||
|
||||
itemKind = 5 歌曲本体解锁(MusicUnlockList)
|
||||
itemKind = 6 Master 难度解锁(MusicUnlockMasterList)
|
||||
itemKind = 7 Re:Master 难度解锁(MusicUnlockReMasterList)
|
||||
|
||||
依据 source/Assembly-CSharp/Net.VO.Mai2/VOExtensions.cs:981-1008
|
||||
(UserData.cs:132-138 那四个解锁列表不是独立节点,是并进 userItemList 的)。
|
||||
|
||||
条目长这样:
|
||||
|
||||
{"itemKind": 5, "itemId": 11414, "stock": 1, "isValid": true}
|
||||
|
||||
★ 标准谱(musicId < 10000)的 Master / Re:Master **天生解锁**,不会写 kind6/7
|
||||
(source/Assembly-CSharp/Manager/NotesListManager.cs:415/455 开头就是
|
||||
`if (id < 10000) return true;`);只有 DX 谱(>= 10000)才需要额外解禁。
|
||||
★ 服务器上已经有的条目**自动跳过** —— 重复插入会让服务端直接 HTTP 500。
|
||||
★ Re:Master 还受活动开关约束(IsUnlockReMaster 里的 IsOpenEvent),
|
||||
那是机台侧开关,脚本改不了;写了也可能进游戏仍是灰的。
|
||||
★ 本功能**没有真机抓包可对照**(机台侧的解锁不上报),是按源码实现的。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
用法
|
||||
--------------------------------------------------------------------------------
|
||||
python unlockmusic.py <二维码> --music 11414 # 预检,不发包
|
||||
python unlockmusic.py <二维码> --music 11414 --send # ★ 真实解锁
|
||||
python unlockmusic.py <二维码> --music 1085,11414 --send # 多首,逗号分隔
|
||||
python unlockmusic.py <二维码> --music 11414 --send --no-unlock-remaster
|
||||
|
||||
# 只要歌曲本体,不解禁任何难度
|
||||
python unlockmusic.py <二维码> --music 11414 --send --no-unlock-master --no-unlock-remaster
|
||||
|
||||
# 和其它主脚本一样的通用开关
|
||||
python unlockmusic.py <二维码> --music 11414 --send --debug
|
||||
python unlockmusic.py <二维码> --music 11414 --send --cooldown 0
|
||||
python unlockmusic.py <二维码> --music 11414 --send --no-verify
|
||||
|
||||
| 参数 | 说明 |
|
||||
|---|---|
|
||||
| `--music ID[,ID...]` | 要解锁的 musicId,可重复、可逗号分隔(`--unlock-music` 也认) |
|
||||
| `--send` | 真正发包(不加就是预检,零副作用) |
|
||||
| `--no-unlock-master` | 不解禁 Master |
|
||||
| `--no-unlock-remaster` | 不解禁 Re:Master |
|
||||
| `--cooldown N` | 发包前冷却秒数,默认取 config 的 15 秒 |
|
||||
| `--no-verify` | 跳过上传后的落库回查 |
|
||||
| `--debug` | 本次运行打印完整收发报文 |
|
||||
|
||||
日志同样是 logs/latest.log(覆盖)+ logs/YYYY-MM-DD.log(追加)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
import upsert
|
||||
from api import log_error, setup_logging, teardown_logging
|
||||
|
||||
|
||||
def parse_args(argv):
|
||||
"""极简命令行解析:第一个非 -- 开头的参数是二维码。"""
|
||||
opts = {"qr": "", "send": False, "debug": False, "no_verify": False,
|
||||
"cooldown": None, "extra_data": [],
|
||||
"unlock_music": [], "unlock_master": True, "unlock_remaster": True}
|
||||
i = 0
|
||||
while i < len(argv):
|
||||
a = argv[i]
|
||||
if a == "--send":
|
||||
opts["send"] = True
|
||||
elif a == "--debug":
|
||||
opts["debug"] = True
|
||||
elif a == "--no-verify":
|
||||
opts["no_verify"] = True
|
||||
elif a == "--extra-data" and i + 1 < len(argv):
|
||||
opts["extra_data"].append(argv[i + 1])
|
||||
i += 1
|
||||
elif a == "--cooldown" and i + 1 < len(argv):
|
||||
opts["cooldown"] = float(argv[i + 1])
|
||||
i += 1
|
||||
elif a in ("--music", "--unlock-music") and i + 1 < len(argv):
|
||||
# 可重复;也支持逗号分隔(中英文逗号都认)
|
||||
for part in str(argv[i + 1]).replace(",", ",").split(","):
|
||||
part = part.strip()
|
||||
if not part:
|
||||
continue
|
||||
try:
|
||||
opts["unlock_music"].append(int(part))
|
||||
except ValueError:
|
||||
opts["unlock_bad"] = part
|
||||
i += 1
|
||||
elif a == "--no-unlock-master":
|
||||
opts["unlock_master"] = False
|
||||
elif a == "--no-unlock-remaster":
|
||||
opts["unlock_remaster"] = False
|
||||
elif not a.startswith("--") and not opts["qr"]:
|
||||
opts["qr"] = a
|
||||
i += 1
|
||||
return opts
|
||||
|
||||
|
||||
def main() -> int:
|
||||
opts = parse_args(sys.argv[1:])
|
||||
if not opts["qr"] or not opts["unlock_music"]:
|
||||
# 没有二维码、或者没有指定要解锁哪首 —— 直接把用法打出来
|
||||
print(__doc__)
|
||||
return 1
|
||||
|
||||
if opts.get("unlock_bad"):
|
||||
# 参数本身就不对,连扫码都不用做
|
||||
setup_logging("unlockmusic")
|
||||
log_error("--music 里有非法 musicId:" + str(opts["unlock_bad"]))
|
||||
teardown_logging()
|
||||
return 6
|
||||
|
||||
# ★ 整个流程复用 upsert.run(),这里只多传三个解锁参数
|
||||
return upsert.run(
|
||||
opts,
|
||||
unlock_music=opts["unlock_music"],
|
||||
unlock_master=opts["unlock_master"],
|
||||
unlock_remaster=opts["unlock_remaster"],
|
||||
log_name="unlockmusic",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user