- u6.y.qq.com 协议底座: zzc/time33 与 LDP924/auto-checkin qmcommon.py 逐字节对拍 PASS(6向量) - 续期闭环: QQLogin 换新 key + openid/access_token 二级救活, 缓存存 /ql/data/qqmusic_cache(订阅git免疫) - 任务中心三态机: 签到/挂件/装扮/听书/节目, Y零请求/W直领/N动作, 10006写锁即停 - 福利站全量: 每日签到/礼券/旅程激励/月里程碑/月度好礼/臻享抽奖, 先探后做 - env: SH_QQMUSIC_KEY 多账号(uin#key / cookie串 / 混排), code 1000/2000 归一失效语义 - 播放上报(任务20完整+10)默认关闭, 定时 1 0,6 * * * 双轮
65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""签名对拍(文件交换版,规避 Windows 子进程控制台编码坑):
|
||
本文件 = LDP924/auto-checkin qmcommon.py 的 zzc/time33 参考实现,生成期望值 JSON;
|
||
qqmusic/test_sign_run.js = 被测 Node 模块,读取 JSON 逐条比对并给出 PASS/FAIL。
|
||
两个实现任何一处不一致都会 MISMATCH —— 这是协议调用可用性的硬门槛。"""
|
||
import json
|
||
import subprocess
|
||
import sys
|
||
import os
|
||
from base64 import b64encode
|
||
from hashlib import sha1
|
||
|
||
SCRAMBLE = (89, 39, 179, 150, 218, 82, 58, 252, 177, 52,
|
||
186, 123, 120, 64, 242, 133, 143, 161, 121, 179)
|
||
|
||
|
||
def time33(s):
|
||
h = 5381
|
||
for c in s:
|
||
h += (h << 5) + ord(c)
|
||
h &= 0xFFFFFFFF
|
||
return h & 0x7FFFFFFF
|
||
|
||
|
||
def zzc(payload):
|
||
h = sha1(payload.encode()).hexdigest().upper()
|
||
p1 = ''.join(h[i] for i in (23, 14, 6, 36, 16, 7, 19))
|
||
p2 = ''.join(h[i] for i in (16, 1, 32, 12, 19, 27, 8, 5))
|
||
p3 = bytes(v ^ int(h[i * 2:i * 2 + 2], 16) for i, v in enumerate(SCRAMBLE))
|
||
mid = b64encode(p3).decode().replace('\\', '').replace('/', '').replace('+', '').replace('=', '')
|
||
return ('zzc' + p1 + mid + p2).lower()
|
||
|
||
|
||
PAYLOADS = [
|
||
'{"comm":{"g_tk":962138355,"uin":123456789,"format":"json","inCharset":"utf-8","outCharset":"utf-8","notice":0,"platform":"h5","needNewCode":1,"ct":23,"cv":0},"req_0":{"module":"music.lvz.LevelConfigSvr","method":"GetState","param":{}}}',
|
||
'{"comm":{},"req_0":{"module":"m","method":"x","param":{}}}',
|
||
'x',
|
||
'a' * 10000,
|
||
'',
|
||
'unicode测试中文签名输入⏰',
|
||
]
|
||
|
||
|
||
def main():
|
||
expect = {
|
||
'time33': time33('Q_H_L_testkey1234567890'),
|
||
'cases': [{'payload': p, 'zzc': zzc(p), 'len': len(zzc(p))} for p in PAYLOADS],
|
||
}
|
||
here = os.path.dirname(os.path.abspath(__file__))
|
||
expect_path = os.path.join(here, 'test_sign_expect.json')
|
||
with open(expect_path, 'w', encoding='utf-8') as f:
|
||
json.dump(expect, f, ensure_ascii=False)
|
||
out = subprocess.run(['node', os.path.join(here, 'test_sign_run.js')],
|
||
capture_output=True, text=True, shell=False, cwd=here,
|
||
encoding='utf-8', errors='replace')
|
||
sys.stdout.write(out.stdout)
|
||
if out.stderr:
|
||
sys.stderr.write(out.stderr)
|
||
return out.returncode
|
||
|
||
|
||
if __name__ == '__main__':
|
||
sys.exit(main())
|