style: 统一 Ruff 代码格式

This commit is contained in:
yml2213
2026-08-30 21:04:52 +08:00
parent c891ac982e
commit 47e19ed7b2
90 changed files with 5574 additions and 2350 deletions
@@ -6,6 +6,7 @@
- mall.py: mall 侧高层 API(MallSession + generate_encrypt_msg)
- session.py: 会话态模型
"""
from .algorithm import (
build_plaintext,
decode_d,
File diff suppressed because it is too large Load Diff
+53 -13
View File
@@ -7,6 +7,7 @@
→ webSave(33 块变换)
→ encrypt_msg(1056 hex)
"""
from __future__ import annotations
import json
@@ -16,17 +17,39 @@ from typing import Any
from .algorithm import generate_encrypt_msg_offline
REPLAY = Path(__file__).resolve().parent.parent / "replay"
ORDER_FIELDS = ["token_id", "openid", "openkey", "session_id", "session_type", "zoneid",
"pay_method", "buy_quantity", "mb_pwd", "pay_id", "auth_key",
"card_value", "accounttype", "provide_uin", "extend", "ts",
"from_h5", "webversion"]
ORDER_FIELDS = [
"token_id",
"openid",
"openkey",
"session_id",
"session_type",
"zoneid",
"pay_method",
"buy_quantity",
"mb_pwd",
"pay_id",
"auth_key",
"card_value",
"accounttype",
"provide_uin",
"extend",
"ts",
"from_h5",
"webversion",
]
class GoodsSession:
"""goods 会话态:xMidasOps(59640,服务端生成)+ key16/key1 + args_template。"""
def __init__(self, xmidas_ops: list, key16: list, key1: list,
args_template_d: str = "", xmidas_token: str = ""):
def __init__(
self,
xmidas_ops: list,
key16: list,
key1: list,
args_template_d: str = "",
xmidas_token: str = "",
):
self.xmidas_ops = xmidas_ops
self.key16 = key16
self.key1 = key1
@@ -44,8 +67,13 @@ class GoodsSession:
def from_session_state(cls, path: str | Path) -> "GoodsSession":
"""从 scripts/capture-session.mjs 生成的 session-state.json 加载。"""
d = json.loads(Path(path).read_text(encoding="utf-8"))
return cls(d["xmidas_ops"], d["key16"], d["key1"],
d.get("args_template_d", ""), d.get("xmidas_token", ""))
return cls(
d["xmidas_ops"],
d["key16"],
d["key1"],
d.get("args_template_d", ""),
d.get("xmidas_token", ""),
)
def to_json(self) -> dict:
return {
@@ -58,17 +86,29 @@ class GoodsSession:
@classmethod
def from_json(cls, d: dict) -> "GoodsSession":
return cls(d["xmidas_ops"], d["key16"], d["key1"],
d.get("args_template_d", ""), d.get("xmidas_token", ""))
return cls(
d["xmidas_ops"],
d["key16"],
d["key1"],
d.get("args_template_d", ""),
d.get("xmidas_token", ""),
)
def generate_encrypt_msg(session: GoodsSession, order: dict) -> str:
"""用会话态 + 订单参数生成 goods encrypt_msg(1056 hex)。"""
from .algorithm import decode_d
params = {k: order.get(k, "") for k in ORDER_FIELDS}
args_tpl = decode_d(session.args_template_d) if session.args_template_d else None
return generate_encrypt_msg_offline(
params, order.get("fk_extend", ""), order.get("ts", ""), order.get("_rand", ""),
xmidas=session.xmidas_ops, xmidas_token=session.xmidas_token,
args_template=args_tpl, key16=session.key16, key1=session.key1,
params,
order.get("fk_extend", ""),
order.get("ts", ""),
order.get("_rand", ""),
xmidas=session.xmidas_ops,
xmidas_token=session.xmidas_token,
args_template=args_tpl,
key16=session.key16,
key1=session.key1,
)
@@ -1,4 +1,5 @@
"""YYB mall APIs require different session fields for WeChat and QQ OAuth."""
from __future__ import annotations
WECHAT_APPID = "wxd44977328b36e647"
+52 -10
View File
@@ -12,6 +12,7 @@ mall 加密链路(E3 已验证,U-2022 闭环):
注意:xMidasOps 是 mall 详情页页面级数据表(59620 长度,服务端生成),
必须从浏览器捕获(与 goods 的 59640 不同)。
"""
from __future__ import annotations
import copy
@@ -24,9 +25,28 @@ from .algorithm import UNDEF, JSObject, Window
from .pagedoo_vm import PagedooVM
REPLAY = Path(__file__).resolve().parent.parent / "replay" / "mall"
GLOBALS = [UNDEF, None, True, False, 4294967295, 3995986053, 2103143698, 1622111212,
4263108271, 3162892160, 1960464030, 2867129963, 3224029870, 3514649446,
1382846327, 1898428403, 1268470028, 1457769175, 1595352606, 1100935262]
GLOBALS = [
UNDEF,
None,
True,
False,
4294967295,
3995986053,
2103143698,
1622111212,
4263108271,
3162892160,
1960464030,
2867129963,
3224029870,
3514649446,
1382846327,
1898428403,
1268470028,
1457769175,
1595352606,
1100935262,
]
class MallSession:
@@ -45,9 +65,13 @@ class MallSession:
def validate(self) -> None:
if len(self.transform_input) != 18:
raise ValueError(f"transform_input 应为 18 槽,实际 {len(self.transform_input)}")
raise ValueError(
f"transform_input 应为 18 槽,实际 {len(self.transform_input)}"
)
if len(self.xmidas_ops) != 59620:
raise ValueError(f"mall xMidasOps 应为 59620(非 goods 59640),实际 {len(self.xmidas_ops)}")
raise ValueError(
f"mall xMidasOps 应为 59620(非 goods 59640),实际 {len(self.xmidas_ops)}"
)
mid = self.transform_input[10]
if isinstance(mid, list) and mid and isinstance(mid[0], list):
if len(mid[0]) != 624:
@@ -61,7 +85,11 @@ class MallSession:
- J|...|e377650|{JSON} e377650 创建参数(transform_input)
- XMIDAS_OPS|url|59620数组 mall 详情页 xMidasOps
"""
lines = Path(frames_jsonl).read_text(encoding="utf-8", errors="replace").splitlines()
lines = (
Path(frames_jsonl)
.read_text(encoding="utf-8", errors="replace")
.splitlines()
)
transform_input = None
xmidas = None
# xMidasOps:取 mall 详情页(z.iwan / pagedoo)那条
@@ -85,7 +113,9 @@ class MallSession:
transform_input = json.loads(l.split("|e377650|", 1)[1])
break
if transform_input is None:
raise ValueError("frames.jsonl 中未找到 e377650 J 转储(需 mall 详情页购买触发加密)")
raise ValueError(
"frames.jsonl 中未找到 e377650 J 转储(需 mall 详情页购买触发加密)"
)
if xmidas is None:
raise ValueError("frames.jsonl 中未找到 59620 长度 xMidasOps")
return cls(transform_input, xmidas)
@@ -112,8 +142,18 @@ def _mk_window(xmidas: list) -> Window:
w.set("sessionStorage", JSObject())
w.set("screen", JSObject())
w.set("history", JSObject())
w.set("XMLHttpRequest", type("XHR", (), {
"open": lambda *a: None, "send": lambda *a: None, "setRequestHeader": lambda *a: None}))
w.set(
"XMLHttpRequest",
type(
"XHR",
(),
{
"open": lambda *a: None,
"send": lambda *a: None,
"setRequestHeader": lambda *a: None,
},
),
)
w.set("fetch", lambda *a: None)
w.set("xMidasOps", xmidas)
return w
@@ -143,5 +183,7 @@ def generate_encrypt_msg(session: MallSession, random_seed: int = 1) -> str:
h9 = h[9][0] if isinstance(h[9], list) and h[9] else h[9]
if not isinstance(h9, list) or len(h9) != 624:
raise RuntimeError(f"e377650 输出异常: {type(h9).__name__} len={len(h9) if isinstance(h9, list) else '?'}")
raise RuntimeError(
f"e377650 输出异常: {type(h9).__name__} len={len(h9) if isinstance(h9, list) else '?'}"
)
return "".join(f"{x & 255:02x}" for x in h9)
@@ -1,4 +1,5 @@
"""Read YYB's official order list after a payment is completed."""
from __future__ import annotations
from typing import Any
@@ -70,7 +71,9 @@ def order_completion_states(document: dict[str, Any]) -> dict[str, bool]:
}
def find_completed_order(document: dict[str, Any], previous_states: dict[str, bool]) -> dict[str, Any] | None:
def find_completed_order(
document: dict[str, Any], previous_states: dict[str, bool]
) -> dict[str, Any] | None:
"""Find an order that appeared or transitioned to completed after the QR display."""
for item in document.get("list", []):
if not isinstance(item, dict):
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,5 @@
"""YYB 下单和付款失败响应的统一归类。"""
from __future__ import annotations
+29 -7
View File
@@ -2,6 +2,7 @@
这里仅收敛已由成功请求验证的固定值,不负责推断或修改协议字段。
"""
from __future__ import annotations
import hashlib
@@ -47,21 +48,38 @@ def file_fingerprint(path: Path) -> str:
return _sha256_prefix_bytes(path.read_bytes())
def validate_goods_materials(args_template: list, xmidas: list[int] | None = None) -> None:
def validate_goods_materials(
args_template: list, xmidas: list[int] | None = None
) -> None:
"""校验 goods webSave VM 所需静态表结构,发现升级时尽早失败。"""
if not isinstance(args_template, list) or len(args_template) != 18:
raise ValueError("goods args-template 必须是 18 槽数组")
for index in (0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16):
if not isinstance(args_template[index], list) or not args_template[index]:
raise ValueError(f"goods args-template 槽 {index} 缺失")
expected_lengths = {0: 16, 1: 256, 2: 256, 3: 256, 4: 256, 5: 256,
6: 16, 10: 528, 11: 1024, 12: 1024, 13: 256,
14: 256, 15: 256, 16: 256}
expected_lengths = {
0: 16,
1: 256,
2: 256,
3: 256,
4: 256,
5: 256,
6: 16,
10: 528,
11: 1024,
12: 1024,
13: 256,
14: 256,
15: 256,
16: 256,
}
for index, expected in expected_lengths.items():
value = args_template[index][0]
if not isinstance(value, list) or len(value) != expected:
actual = len(value) if isinstance(value, list) else "非数组"
raise ValueError(f"goods args-template 槽 {index} 长度异常: {actual} != {expected}")
raise ValueError(
f"goods args-template 槽 {index} 长度异常: {actual} != {expected}"
)
if xmidas is not None and len(xmidas) != 59640:
raise ValueError(f"goods xMidasOps 长度异常: {len(xmidas)} != 59640")
@@ -70,7 +88,9 @@ def validate_mall_materials(transform_fixed: dict[str, Any]) -> None:
"""校验 mall 固定槽模板;动态槽仍由当前会话的 GetPayToken 填充。"""
if not isinstance(transform_fixed, dict):
raise ValueError("mall transform-fixed 必须是对象")
required = {str(index) for index in (1, 2, 3, 4, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17)}
required = {
str(index) for index in (1, 2, 3, 4, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17)
}
missing = sorted(required - set(transform_fixed))
if missing:
raise ValueError(f"mall transform-fixed 缺少槽: {', '.join(missing)}")
@@ -79,7 +99,9 @@ def validate_mall_materials(transform_fixed: dict[str, Any]) -> None:
raise ValueError(f"mall transform-fixed 槽 {index} 不是数组")
def goods_material_diagnostics(root: Path, args_template: list, xmidas: list[int]) -> dict[str, Any]:
def goods_material_diagnostics(
root: Path, args_template: list, xmidas: list[int]
) -> dict[str, Any]:
"""构造脱敏协议指纹,便于区分页面升级和服务端业务拒绝。"""
validate_goods_materials(args_template, xmidas)
replay = root / "replay"
+4 -1
View File
@@ -6,6 +6,7 @@ F-2049/F-2051(E3):encrypt_msg 与当前会话绑定——
- key1: 诱饵密钥(点击级,捕获即可)
服务端能验证 key 派生状态(随机 key 变体 ret:1099),因此新订单必须先捕获会话态。
"""
from __future__ import annotations
import json
@@ -31,7 +32,9 @@ class SessionState:
key16: list[int] = field(default_factory=list)
key1: list[int] = field(default_factory=list)
xmidas_token: str = DEFAULT_XMIDAS_TOKEN
args_template_d: str = "" # webSave 18 参深拷贝(deepcap PC 85091 的 C[2] 原始 D 编码,会话绑定)
args_template_d: str = (
"" # webSave 18 参深拷贝(deepcap PC 85091 的 C[2] 原始 D 编码,会话绑定)
)
cookies: dict[str, str] = field(default_factory=dict)
openid: str = ""
openkey: str = ""