refactor(cmcc): 重放改两轮判别(轮1探活 + 轮2五发作死/活判别)

真机 2026-11-09 08:40 首跑暴露:60 发 + 两次 600s 静默 = 51 分钟,
最后才知道素材死了。三轮砸时间的假设(冷却自愈)在素材真死时纯浪费。

新策略:
- 轮1:15 发快节拍;全 88B → 疑似冷却,静默 600s
- 轮2:只打 5 发作判别 —— 仍全 88B 立即判死早停(stats.verdict=dead_seed);
        出现非 88B 形态说明服务在响应素材,继续跑满
- 结论:活的场景 12 分钟内出结果,死的场景也是 12 分钟明确,不再拖 51 分钟
This commit is contained in:
2026-11-09 13:04:19 +08:00
parent 449e592cdf
commit d6abdc59a3

View File

@@ -126,10 +126,8 @@ REPLAY_MAX = 30 # 单轮命中即停:实测中位 1~9 发
REPLAY_GAP = 5.0 # 3~5s 快节拍(慢节拍推向 236B 通道) REPLAY_GAP = 5.0 # 3~5s 快节拍(慢节拍推向 236B 通道)
WALL_LIMIT = 6 * 60 # 单轮 [A] 墙钟上限(含长静默) WALL_LIMIT = 6 * 60 # 单轮 [A] 墙钟上限(含长静默)
BACKOFF_AFTER = 15 # 连续 15 败 → 静默(防风暴冷却) BACKOFF_AFTER = 15 # 连续 15 败 → 静默(防风暴冷却)
COOLDOWN_ROUNDS = 2 # 轮1 探命中 + 轮2 死/活判别(详见 replay_auto_login 文档)
BACKOFF_SECS = 600 BACKOFF_SECS = 600
COOLDOWN_ROUNDS = 3 # ★ 冷却自愈:整轮未命中时,静默后重开一轮,最多 3 轮
# (素材在冷却期时 88B×15 + 600s 静默 ≈ 12min/轮;
# 实测静默 ≥10 分钟恢复命中,3 轮覆盖 ~36 分钟,足够穿出冷却期)
THROTTLE = 1.2 # 业务请求间隔 THROTTLE = 1.2 # 业务请求间隔
MASK = lambda s: (s[:3] + "****" + s[-4:]) if s and len(s) >= 7 else (s or "?") MASK = lambda s: (s[:3] + "****" + s[-4:]) if s and len(s) >= 7 else (s or "?")
@@ -316,14 +314,24 @@ def classify(resp):
def replay_auto_login(seed, stats): def replay_auto_login(seed, stats):
"""重放至 492 命中,命中即停。无状态 post(Session 的 jar 会污染素材 Cookie)。 """重放至 492 命中,命中即停。无状态 post(Session 的 jar 会污染素材 Cookie)。
★ 冷却自愈:单轮 15 败 → 静默 600s → 重开一轮(试数归零),最多 COOLDOWN_ROUNDS 轮。
素材在冷却期(88B)时靠这个穿出去;3 轮全 88B 才认输(诊断文案会说「稍后重跑」。)""" ★ 两轮判定(2026-11-09 真机重写,取代原三轮砸时间):
轮1:15 发快节拍(75s)。全 88B → 可能是冷却期,也可能是素材已死。
静默 600s(冷却期实测 ≥10 分钟释放)。
轮2:只打 5 发(25s)作判别——
仍全 88B → 静默后依然拒收 = **素材本身失效**,立即早停(不再烧时间)
出现任何非 88B(64/236/152/492)→ 说明服务在响应素材,继续跑满
这样「活了」的场景最多 12 分钟出结果,「死了」的场景也是 12 分钟就明确,
不再出现 11-09 那种 60 发 + 20 分钟静默后才说失败的情况。"""
DEAD_PROBE = 5 # 轮2 判别发数
total_tried = 0 total_tried = 0
for round_no in range(1, COOLDOWN_ROUNDS + 1): for round_no in (1, 2):
start = time.time() start = time.time()
tried = 0 tried = 0
backed = False backed = False
while tried < REPLAY_MAX and time.time() - start < WALL_LIMIT: cap = REPLAY_MAX if round_no == 1 else DEAD_PROBE
only_88 = True # 本轮是否「清一色 88B」
while tried < cap and time.time() - start < WALL_LIMIT:
tried += 1 tried += 1
total_tried += 1 total_tried += 1
try: try:
@@ -331,11 +339,14 @@ def replay_auto_login(seed, stats):
seed["headers"], timeout=30) seed["headers"], timeout=30)
except Exception as e: except Exception as e:
stats["net_error"] = stats.get("net_error", 0) + 1 stats["net_error"] = stats.get("net_error", 0) + 1
only_88 = False
log("[换会话] 第%d轮 #%d 网络错误 %r" % (round_no, tried, e)) log("[换会话] 第%d轮 #%d 网络错误 %r" % (round_no, tried, e))
time.sleep(REPLAY_GAP) time.sleep(REPLAY_GAP)
continue continue
form = classify(r) form = classify(r)
stats[form] = stats.get(form, 0) + 1 stats[form] = stats.get(form, 0) + 1
if form != "88_business_miss":
only_88 = False
if form == "492_hit": if form == "492_hit":
try: try:
raw_list = r.raw.headers.getlist("Set-Cookie") raw_list = r.raw.headers.getlist("Set-Cookie")
@@ -349,17 +360,24 @@ def replay_auto_login(seed, stats):
log("[换会话] 第%d轮 第%d发 %s" % (round_no, tried, form)) log("[换会话] 第%d轮 第%d发 %s" % (round_no, tried, form))
if tried >= BACKOFF_AFTER and not backed: if tried >= BACKOFF_AFTER and not backed:
backed = True backed = True
if round_no < COOLDOWN_ROUNDS: if round_no == 1:
log("[换会话] 连续 %d 败 → 疑似冷却期,静默 %ds 后自动重开一轮" % (tried, BACKOFF_SECS)) log("[换会话] 连续 %d 败 → 静默 %ds,之后只打 %d 发作死/活判别"
% (tried, BACKOFF_SECS, DEAD_PROBE))
time.sleep(BACKOFF_SECS) time.sleep(BACKOFF_SECS)
break # break 出 while → 下一轮(tried 归零) break # 进轮2
else: else:
log("[换会话] 末轮连续 %d 败,不再静默" % tried) log("[换会话] 判别轮仍全败,判素材失效")
else: else:
time.sleep(REPLAY_GAP) time.sleep(REPLAY_GAP)
# while 正常跑满 REPLAY_MAX 也进入下一轮(若还有轮次) # 轮2 结束(无论跑满还是 break)→ 收工
if round_no < COOLDOWN_ROUNDS: if round_no == 2:
log("[换会话] 第%d轮 %d 发未命中,重开新一轮" % (round_no, tried)) if only_88:
stats["verdict"] = "dead_seed"
log("[换会话] 静默 10 分钟后仍是 100%% 88B → 判定素材失效(早停,不烧时间)")
break
if only_88 and tried >= BACKOFF_AFTER:
continue # 上 break 已 sleep,进轮2
log("[换会话] 第1轮 %d 发未命中(有非 88B 形态),进判别轮" % tried)
return None, total_tried return None, total_tried