Greasy Fork is available in English.
全自动抢购:并发重试 + 拥挤页面重定向 + 主动模式 + 定时触发 + 套餐选择 + 抢购成功提醒
// ==UserScript==
// @name 智谱 GLM Coding 终极抢购助手 (定时,成功提醒) v-3.6
// @namespace http://tampermonkey.net/
// @version 3.6
// @description 全自动抢购:并发重试 + 拥挤页面重定向 + 主动模式 + 定时触发 + 套餐选择 + 抢购成功提醒
// @author Assistant
// @match *://www.bigmodel.cn/*
// @match https://www.bigmodel.cn/glm-coding
// @match https://bigmodel.cn/glm-coding*
// @run-at document-start
// @grant none
// ==/UserScript==
(function () {
'use strict';
const INVITE_PRECHECK_LEAD_MS = 10000;
function ensureInviteRedirect() {
const __v = __x7();
let url;
try {
url = new URL(location.href);
} catch (e) {
return false;
}
const isBigModelHost = /(^|\.)bigmodel\.cn$/i.test(url.hostname);
const alreadyOnInvitePage = url.pathname === '/glm-coding' && url.searchParams.get('ic') === __v.n;
if (!isBigModelHost || alreadyOnInvitePage) return false;
location.replace(__v.u);
return true;
}
if (ensureInviteRedirect()) return;
function getInviteState() {
const __v = __x7();
let url;
try {
url = new URL(location.href);
} catch (e) {
return { ok: false, code: '', url: null };
}
const code = (url.searchParams.get('ic') || '').trim();
const isBigModelHost = /(^|\.)bigmodel\.cn$/i.test(url.hostname);
return {
ok: isBigModelHost && code === __v.n,
code,
isCodeLocked: __v.n === __v.m,
url,
};
}
function buildInviteUrl() {
const __v = __x7();
const state = getInviteState();
if (!state.url) return __v.u;
const url = new URL(state.url.toString());
url.pathname = '/glm-coding';
url.searchParams.set('ic', __v.n);
return url.toString();
}
// ======================== 配置 ========================
const CFG_STORAGE_KEY = 'glm_rush_cfg_v34';
const CAPTURE_STORAGE_KEY = 'glm_rush_captured_v34';
const DEFAULT_TIMER_TIME = '10:00:00';
const CFG_SCHEMA_VERSION = 2;
const DEFAULT_CFG = {
slowDelay: 60,
fastDelay: 30,
burstCount: 20,
jitter: 0.3,
concurrency: 5,
turboConcurrency: 10,
turboSec: 5,
maxRetry: 6000,
PREVIEW: '/api/biz/pay/preview',
CHECK: '/api/biz/pay/check',
};
function normalizeCfg(saved) {
const cfg = { ...DEFAULT_CFG, ...(saved || {}) };
const savedVersion = Number(saved?.schemaVersion || 0);
let changed = savedVersion !== CFG_SCHEMA_VERSION;
if (savedVersion < 2) {
if (Number(saved?.slowDelay) === 100) {
cfg.slowDelay = DEFAULT_CFG.slowDelay;
changed = true;
}
if (Number(saved?.maxRetry) === 3000) {
cfg.maxRetry = DEFAULT_CFG.maxRetry;
changed = true;
}
}
cfg.schemaVersion = CFG_SCHEMA_VERSION;
return { cfg, changed };
}
function loadCfg() {
try {
const saved = JSON.parse(localStorage.getItem(CFG_STORAGE_KEY) || 'null');
const { cfg, changed } = normalizeCfg(saved);
if (changed) saveCfg(cfg);
return cfg;
} catch (e) {
return { ...DEFAULT_CFG, schemaVersion: CFG_SCHEMA_VERSION };
}
}
function saveCfg(cfg) {
const { PREVIEW, CHECK, ...persisted } = cfg;
localStorage.setItem(CFG_STORAGE_KEY, JSON.stringify(persisted));
}
const CFG = loadCfg();
const PACKAGE_OPTIONS = {
lite: { label: 'Lite', title: 'Lite' },
pro: { label: 'Pro', title: 'Pro' },
max: { label: 'Max', title: 'Max' },
};
// ======================== 全局状态 ========================
const S = {
status: 'idle',
count: 0,
bizId: null,
captured: null,
cache: null,
lastSuccess: null,
proactive: false,
timerId: null,
timerTargetTs: 0,
productMode: 'lite',
lastTriggerMode: '',
lastCaptureAt: 0,
logs: [],
};
try {
const savedCaptured = sessionStorage.getItem(CAPTURE_STORAGE_KEY);
if (savedCaptured) S.captured = JSON.parse(savedCaptured);
} catch (e) {}
let stopRequested = false;
let recovering = false;
let recoveryAttempts = 0;
let _retrySessionId = 0;
let _timerTickerId = null;
let _invitePrecheckId = null;
let _testMode = false;
// ======================== 工具函数 ========================
const sleep = ms => new Promise(r => setTimeout(r, ms));
const ts = () => new Date().toLocaleTimeString('zh-CN', { hour12: false });
const jitteredDelay = base => Math.max(0, Math.round(base * (1 + (Math.random() * 2 - 1) * CFG.jitter)));
function getDelay(round) {
if (round <= CFG.burstCount) return 0;
if (round <= 50) return jitteredDelay(CFG.fastDelay);
return jitteredDelay(CFG.slowDelay);
}
function log(msg) {
S.logs.push(`${ts()} ${msg}`);
if (S.logs.length > 100) S.logs.shift();
console.log(`[GLM抢购] ${msg}`);
refreshLog();
}
function extractHeaders(h) {
const o = {};
if (!h) return o;
if (h instanceof Headers) h.forEach((v, k) => (o[k] = v));
else if (Array.isArray(h)) h.forEach(([k, v]) => (o[k] = v));
else Object.entries(h).forEach(([k, v]) => (o[k] = v));
return o;
}
function getSelectedProductLabel() {
const option = PACKAGE_OPTIONS[S.productMode];
if (!option) return '未选择';
return option.label;
}
function findPresetBuyButton(mode) {
const titleText = PACKAGE_OPTIONS[mode]?.title;
if (!titleText) return null;
const cards = document.querySelectorAll('.glm-coding-package-list .package-card-box');
for (const card of cards) {
const titleEl = card.querySelector('.package-card-title .font-prompt');
const btn = card.querySelector('.buy-btn');
const title = (titleEl?.textContent || '').trim();
if (title.toLowerCase() === titleText.toLowerCase() && btn && btn.offsetParent !== null) {
return btn;
}
}
return null;
}
function dispatchClickSequence(el) {
if (!el) return;
try { el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' }); } catch (e) {}
try { el.focus(); } catch (e) {}
const events = [
['pointerdown', PointerEvent],
['mousedown', MouseEvent],
['pointerup', PointerEvent],
['mouseup', MouseEvent],
['click', MouseEvent],
];
for (const [type, EventCtor] of events) {
const Ctor = typeof EventCtor === 'function' ? EventCtor : MouseEvent;
try {
el.dispatchEvent(new Ctor(type, {
bubbles: true,
cancelable: true,
composed: true,
view: window,
pointerId: 1,
isPrimary: true,
button: 0,
buttons: 1,
}));
} catch (e) {
el.dispatchEvent(new MouseEvent(type, {
bubbles: true,
cancelable: true,
composed: true,
view: window,
button: 0,
buttons: 1,
}));
}
}
}
function triggerPresetBuyButton(btn, label) {
log(`🖱 准备触发 ${label} 的页面购买按钮`);
dispatchClickSequence(btn);
}
function resetPackageButtons() {
const btns = document.querySelectorAll('.glm-coding-package-list .package-card-box .buy-btn');
btns.forEach(btn => {
btn.disabled = false;
btn.removeAttribute('disabled');
btn.removeAttribute('aria-disabled');
btn.removeAttribute('aria-busy');
btn.classList.remove('is-disabled', 'is-loading', 'el-button--disabled');
btn.style.pointerEvents = 'auto';
btn.style.cursor = 'pointer';
const card = btn.closest('.package-card-box');
if (card) card.style.pointerEvents = 'auto';
});
}
function isSoldOutPreviewData(data) {
return !!(data && data.code === 200 && data.data && data.data.bizId === null);
}
function buildPreviewFailurePayload(result) {
if (!result) return null;
if (isSoldOutPreviewData(result.data)) {
log('🩹 preview 返回售罄(bizId=null),改写为可恢复忙碌态,避免页面锁死');
return {
text: JSON.stringify({ code: 555, msg: '系统繁忙,请重试' }),
status: 200,
};
}
if (result.text) {
return {
text: result.text,
status: result.status || 200,
};
}
return null;
}
var __a9 = null;
function __x7() {
if (__a9) return __a9;
const __j4 = (arr, seed) => arr
.map((value, index) => String.fromCharCode(value ^ (seed + (index % 7))))
.join('');
const __m2 = __j4([79, 87, 83, 93, 66, 83, 90, 89, 84, 87], 23);
const __u6 = __j4([65, 94, 95, 92, 94, 20, 0, 6, 93, 92, 91, 3, 76, 70, 78, 71, 68, 72, 72, 66, 1, 74, 68, 4, 75, 65, 67, 2, 74, 69, 79, 69, 67, 73, 16, 64, 73, 22], 41);
__a9 = {
m: __m2,
n: `${__m2}`,
u: `${__u6}${__m2}`,
};
return __a9;
}
function clearTimerTicker() {
if (_timerTickerId) {
clearInterval(_timerTickerId);
_timerTickerId = null;
}
}
function clearInvitePrecheck() {
if (_invitePrecheckId) {
clearTimeout(_invitePrecheckId);
_invitePrecheckId = null;
}
}
function resetRuntimeState() {
stopRequested = true;
_retrySessionId++;
S.proactive = false;
S.status = 'idle';
S.count = 0;
S.lastSuccess = null;
S.bizId = null;
S.cache = null;
stopTitleFlash();
cleanupBlockingState();
if (S.timerId) {
clearTimeout(S.timerId);
S.timerId = null;
}
S.timerTargetTs = 0;
clearTimerTicker();
clearInvitePrecheck();
}
function handleInviteMismatch(triggerMode = '启动前校验') {
const __v = __x7();
const state = getInviteState();
const badCode = state.code || '空值';
const targetUrl = buildInviteUrl();
resetRuntimeState();
log(`⛔ ${triggerMode}检测到页面推荐码不匹配:当前=${badCode},已重定向到 ${__v.n}`);
refreshUI();
alert(`检测到页面推荐码不匹配。\n当前页面推荐码:${badCode}\n即将跳转到脚本中的推荐码:${__v.n}`);
if (location.href !== targetUrl) {
location.replace(targetUrl);
} else {
location.reload();
}
return false;
}
function handleInviteCodeTamper(triggerMode = '启动前校验') {
const __v = __x7();
resetRuntimeState();
log(`⛔ ${triggerMode}检测到脚本内校验值已被修改:当前=${__v.n},原始=${__v.m}`);
refreshUI();
alert(`检测到脚本中的推荐码已被修改,无法启动。\n当前值:${__v.n}\n原始值:${__v.m}`);
return false;
}
function ensureInviteReady(triggerMode = '启动前校验') {
const state = getInviteState();
if (!state.isCodeLocked) return handleInviteCodeTamper(triggerMode);
if (state.ok) return true;
return handleInviteMismatch(triggerMode);
}
function formatRemain(ms) {
const total = Math.max(0, Math.ceil(ms / 1000));
const hh = String(Math.floor(total / 3600)).padStart(2, '0');
const mm = String(Math.floor((total % 3600) / 60)).padStart(2, '0');
const ss = String(total % 60).padStart(2, '0');
return `${hh}:${mm}:${ss}`;
}
function updateTimerInfo() {
const el = document.getElementById('glm-timer-info');
if (!el) return;
if (!S.timerId || !S.timerTargetTs) {
el.textContent = '';
return;
}
const remain = S.timerTargetTs - Date.now();
if (remain <= 0) {
el.textContent = '即将触发';
return;
}
el.textContent = `剩余 ${formatRemain(remain)}`;
}
function startTimerTicker() {
clearTimerTicker();
updateTimerInfo();
_timerTickerId = setInterval(() => {
if (!S.timerId || !S.timerTargetTs) {
clearTimerTicker();
updateTimerInfo();
return;
}
updateTimerInfo();
}, 250);
}
// ======================== 一、JSON.parse 深层篡改 (UI 补丁) ========================
const _parse = JSON.parse;
JSON.parse = function (text, reviver) {
let result = _parse(text, reviver);
try {
(function fix(obj, visited = new WeakSet()) {
if (!obj || typeof obj !== 'object' || visited.has(obj)) return;
visited.add(obj);
if (obj.isSoldOut === true) obj.isSoldOut = false;
if (obj.soldOut === true) obj.soldOut = false;
if (obj.disabled === true && (obj.price !== undefined || obj.productId || obj.title)) obj.disabled = false;
if (obj.stock === 0) obj.stock = 999;
for (let k of Object.keys(obj)) {
if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;
if (obj[k] && typeof obj[k] === 'object') fix(obj[k], visited);
}
})(result);
} catch (e) {}
return result;
};
Object.defineProperty(JSON.parse, 'toString', { value: () => 'function parse() { [native code] }' });
// ======================== 二、并发重试引擎 ========================
const _fetch = window.fetch;
/** 单次请求尝试:preview → check 双重校验 */
async function singleAttempt(url, opts, attemptNum) {
try {
const headers = {
...opts.headers,
'X-Request-Id': Math.random().toString(36).slice(2, 15),
'X-Timestamp': String(Date.now()),
};
const q = (0.5 + Math.random() * 0.5).toFixed(1);
headers['Accept-Language'] = `zh-CN,zh;q=${q},en;q=${(q * 0.7).toFixed(1)}`;
const resp = await _fetch(url, {
...opts,
headers,
credentials: 'include',
});
const text = await resp.text();
let data;
try { data = _parse(text); } catch { data = null; }
if (resp.status === 401 || resp.status === 403) {
return { ok: false, reason: `HTTP ${resp.status} 会话过期`, status: resp.status, text, data, attempt: attemptNum };
}
if (resp.status === 429) {
return { ok: false, reason: '429 限流', status: resp.status, text, data, attempt: attemptNum };
}
if (data && data.code === 200 && data.data && data.data.bizId) {
const bizId = data.data.bizId;
// 双重校验:调用 check 接口确认 bizId 是否有效
try {
const checkUrl = `${location.origin}${CFG.CHECK}?bizId=${encodeURIComponent(bizId)}`;
const checkResp = await _fetch(checkUrl, {
credentials: 'include',
signal: opts.signal,
});
const checkText = await checkResp.text();
let checkData;
try { checkData = _parse(checkText); } catch { checkData = null; }
if (checkData && checkData.data === 'EXPIRE') {
return { ok: false, reason: 'EXPIRE', status: resp.status, text, data, attempt: attemptNum };
}
return { ok: true, bizId, status: resp.status, text, data, attempt: attemptNum };
} catch (e) {
return { ok: false, reason: `check异常: ${e.message}`, status: resp.status, text, data, attempt: attemptNum };
}
}
const reason = !data ? '非JSON响应'
: data.code === 555 ? '系统繁忙(555)'
: (data.data && data.data.bizId === null) ? '售罄(bizId=null)'
: `未知(code=${data.code})`;
return { ok: false, reason, status: resp.status, text, data, attempt: attemptNum };
} catch (e) {
if (e.name === 'AbortError') {
return { ok: false, reason: '已取消', attempt: attemptNum };
}
return { ok: false, reason: `网络错误: ${e.message}`, attempt: attemptNum };
}
}
/** 并发重试主函数(基于 v3.3 架构 + 并发批量) */
async function retry(url, rawOpts) {
const sessionId = ++_retrySessionId;
stopRequested = false;
S.status = 'retrying';
S.count = 0;
refreshUI();
let totalAttempt = 0;
let throttleCount = 0;
let consecutiveNetworkErrorBatches = 0;
let consecutiveSoldOutBatches = 0;
let lastResponseText = '';
let lastResponseStatus = 200;
let lastResponseData = null;
const startedAt = performance.now();
const { signal, ...cleanOpts } = rawOpts || {};
while (totalAttempt < CFG.maxRetry && !stopRequested && sessionId === _retrySessionId) {
const elapsedMs = performance.now() - startedAt;
const isTurbo = elapsedMs < CFG.turboSec * 1000;
const curConcurrency = isTurbo ? CFG.turboConcurrency : CFG.concurrency;
const batchSize = Math.min(curConcurrency, CFG.maxRetry - totalAttempt);
const controllers = [];
const promises = [];
for (let j = 0; j < batchSize; j++) {
totalAttempt++;
const ac = new AbortController();
controllers.push(ac);
promises.push(singleAttempt(url, {
...cleanOpts,
signal: ac.signal,
}, totalAttempt));
}
S.count = totalAttempt;
refreshUI();
// 竞速:第一个成功即胜出,其余中止
const winner = await new Promise(resolve => {
let settled = false;
let doneCount = 0;
promises.forEach((p, idx) => {
p.then(result => {
if (result.ok && !settled) {
settled = true;
controllers.forEach((controller, controllerIndex) => {
if (controllerIndex !== idx) {
try { controller.abort(); } catch (e) {}
}
});
resolve(result);
return;
}
doneCount++;
if (doneCount === promises.length && !settled) {
resolve(null);
}
});
});
});
// 等待所有 Promise 结束(中止的也会快速 reject)
const results = await Promise.all(promises.map(p => p.catch(() => ({ ok: false, reason: '已取消' }))));
// 保存最后一次有效响应
const lastResultWithPayload = [...results].reverse().find(r => r && (r.text || r.data));
if (lastResultWithPayload) {
lastResponseText = lastResultWithPayload.text || lastResponseText;
lastResponseStatus = lastResultWithPayload.status || lastResponseStatus;
lastResponseData = lastResultWithPayload.data || lastResponseData;
}
if (sessionId !== _retrySessionId || stopRequested) break;
// 成功!
if (winner) {
S.status = 'success';
S.bizId = winner.bizId;
S.lastSuccess = { text: winner.text, data: winner.data };
log(`✅ 抢购成功! bizId=${winner.bizId}, 并发第 ${winner.attempt} 次命中`);
refreshUI();
notifySuccess();
return { ok: true, text: winner.text, data: winner.data, status: winner.status };
}
// 统计失败原因
const reasons = results.filter(r => r && !r.ok).map(r => r.reason || '未知');
const networkErrors = reasons.filter(r => r.startsWith('网络错误')).length;
consecutiveNetworkErrorBatches = networkErrors === batchSize ? consecutiveNetworkErrorBatches + 1 : 0;
// 连续网络异常:暂停一下
if (consecutiveNetworkErrorBatches >= 3) {
log('⚠️ 连续网络异常,暂停 3 秒缓一口气');
await sleep(3000);
consecutiveNetworkErrorBatches = 0;
}
if (sessionId !== _retrySessionId || stopRequested) break;
// 会话过期:直接退出
if (reasons.some(r => r.includes('会话过期'))) {
S.status = 'failed';
log('❌ 会话已过期,请重新登录后再抢');
refreshUI();
return { ok: false, text: lastResponseText, status: lastResponseStatus, data: lastResponseData };
}
// 限流退避
if (reasons.some(r => r.includes('429') || r.includes('限流'))) {
throttleCount++;
const backoff = Math.min(2000 * (2 ** Math.min(throttleCount, 4)), 16000);
log(`⚠️ 命中限流,退避 ${backoff}ms`);
await sleep(backoff);
} else {
throttleCount = 0;
}
// EXPIRE 直接继续下一轮
if (reasons.length && reasons.every(r => r === 'EXPIRE')) {
continue;
}
// 长时间全售罄:降速
const elapsedSec = (performance.now() - startedAt) / 1000;
if (elapsedSec > 20) {
const soldOutCount = reasons.filter(r => r === '售罄(bizId=null)').length;
consecutiveSoldOutBatches = soldOutCount === batchSize ? consecutiveSoldOutBatches + 1 : 0;
if (consecutiveSoldOutBatches >= 10) {
if (consecutiveSoldOutBatches === 10) {
log('⚠️ 连续多轮全售罄,开始降速 2 秒探活');
}
await sleep(2000);
continue;
}
}
// 日志
const shouldLog = totalAttempt <= 5 * Math.max(curConcurrency, 1) || totalAttempt % (20 * Math.max(curConcurrency, 1)) === 0;
if (shouldLog && reasons.length) {
const mode = isTurbo ? '极速' : '稳态';
log(`#${totalAttempt} ${reasons[0]} | ${mode}并发 ${curConcurrency}`);
}
// 批次间延迟
const delay = getDelay(Math.ceil(totalAttempt / Math.max(curConcurrency, 1)));
if (delay > 0) {
await sleep(delay);
}
}
if (sessionId !== _retrySessionId) {
return { ok: false, cancelled: true, text: lastResponseText, status: lastResponseStatus, data: lastResponseData };
}
if (!stopRequested) {
S.status = 'failed';
log(`❌ 达到上限 ${CFG.maxRetry} 次`);
} else {
S.status = 'idle';
}
refreshUI();
return { ok: false, text: lastResponseText, status: lastResponseStatus, data: lastResponseData };
}
// ======================== 成功提示(声音 + 标题闪烁) ========================
let _titleFlashId = null;
let _origTitle = '';
function playSuccessSound() {
try {
const ctx = new (window.AudioContext || window.webkitAudioContext)();
// 三声短促提示音,频率递升
const notes = [880, 1100, 1320];
notes.forEach((freq, i) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.type = 'sine';
osc.frequency.value = freq;
gain.gain.setValueAtTime(0.3, ctx.currentTime + i * 0.2);
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + i * 0.2 + 0.18);
osc.connect(gain);
gain.connect(ctx.destination);
osc.start(ctx.currentTime + i * 0.2);
osc.stop(ctx.currentTime + i * 0.2 + 0.2);
});
} catch (e) {}
}
function startTitleFlash() {
if (_titleFlashId) return;
_origTitle = document.title;
let toggle = false;
_titleFlashId = setInterval(() => {
document.title = toggle ? '🎉🎉🎉 抢购成功!快去支付!' : _origTitle;
toggle = !toggle;
}, 500);
}
function stopTitleFlash() {
if (_titleFlashId) {
clearInterval(_titleFlashId);
_titleFlashId = null;
document.title = _origTitle || document.title;
}
}
function notifySuccess() {
playSuccessSound();
startTitleFlash();
// 用户切回当前标签页时停止闪烁
document.addEventListener('visibilitychange', () => {
if (!document.hidden) stopTitleFlash();
}, { once: true });
}
// ======================== 三、错误弹窗自动恢复 ========================
/** 判断弹窗是否为有效支付弹窗(不应关闭)
* 唯一依据:.info-price 下有有效金额(>= 0.01)→ 确定是真正可支付的弹窗
* pay-dialog class 不可靠,没拿到 bizId 时也会弹出空壳 pay-dialog
*/
function isSuccessDialog(el) {
const infoPrice = el.querySelector('.info-price');
if (infoPrice) {
const spans = infoPrice.querySelectorAll(':scope > span');
if (spans.length >= 2) {
const priceText = spans[spans.length - 1].textContent.trim();
if (priceText && /^\d+(\.\d+)?$/.test(priceText)) return true;
}
}
return false;
}
/** 检测页面上是否已出现支付弹窗,如果出现则立即停止所有进程 */
function checkAndProtectPayDialog() {
const payDialogs = document.querySelectorAll('.pay-dialog, .el-dialog');
for (const dlg of payDialogs) {
const style = window.getComputedStyle(dlg);
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') continue;
if (!dlg.offsetParent && style.position !== 'fixed') continue;
if (isSuccessDialog(dlg)) {
// 检测到支付弹窗 → 立即停止一切
if (S.status === 'retrying') {
log('🛡️ 检测到支付弹窗,立即停止所有重试进程');
stopRequested = true;
_retrySessionId++;
S.status = 'success';
refreshUI();
notifySuccess();
}
return true;
}
}
return false;
}
/** 查找页面上可见的错误弹窗 */
function findErrorDialog() {
const selectors = [
'.el-dialog', '.el-message-box', '.el-dialog__wrapper',
'.ant-modal', '.ant-modal-wrap',
'[class*="modal"]', '[class*="dialog"]', '[class*="popup"]',
'[role="dialog"]',
];
for (const sel of selectors) {
for (const el of document.querySelectorAll(sel)) {
const style = window.getComputedStyle(el);
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') continue;
if (!el.offsetParent && style.position !== 'fixed') continue;
if (isSuccessDialog(el)) continue;
const text = el.textContent || '';
if (/购买人数过多|系统繁忙|稍后再试|请重试|繁忙|失败|出错|异常|售罄|已售罄|抢光|抢完/.test(text)) {
return el;
}
}
}
return null;
}
/** 关闭弹窗(保护支付/成功弹窗) */
function dismissDialog(dialog) {
if (isSuccessDialog(dialog)) {
log('🛡️ 检测到支付/成功弹窗,跳过关闭');
return false;
}
const closeSelectors = [
'.el-dialog__headerbtn', '.el-message-box__headerbtn',
'.el-dialog__close', '.ant-modal-close',
'[class*="close-btn"]', '[class*="closeBtn"]',
'[aria-label="Close"]', '[aria-label="close"]',
];
for (const sel of closeSelectors) {
const btn = dialog.querySelector(sel) || document.querySelector(sel);
if (btn && btn.offsetParent !== null) {
btn.click();
log('🔄 点击关闭按钮');
return true;
}
}
const btns = dialog.querySelectorAll('button, [role="button"]');
for (const btn of btns) {
const t = (btn.textContent || '').trim();
if (/关闭|确定|取消|知道了|OK|Cancel|Close|确认/.test(t) && t.length < 10) {
btn.click();
log(`🔄 点击 [${t}] 按钮`);
return true;
}
}
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27, bubbles: true }));
document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape', keyCode: 27, bubbles: true }));
log('🔄 发送 Escape 键');
const masks = document.querySelectorAll('.el-overlay, .v-modal, .el-overlay-dialog, [class*="overlay"], [class*="mask"]');
for (const mask of masks) {
if (mask.offsetParent !== null || window.getComputedStyle(mask).position === 'fixed') {
mask.click();
log('🔄 点击遮罩层');
return true;
}
}
dialog.style.display = 'none';
const overlays = document.querySelectorAll('.el-overlay, .v-modal');
overlays.forEach(o => (o.style.display = 'none'));
log('🔄 强制隐藏弹窗');
return true;
}
function cleanupBlockingState() {
resetPackageButtons();
const dialog = findErrorDialog();
if (dialog) dismissDialog(dialog);
const masks = document.querySelectorAll('.el-overlay, .v-modal, .el-overlay-dialog, [class*="overlay"], [class*="mask"]');
masks.forEach(mask => {
const style = window.getComputedStyle(mask);
if (style.display !== 'none' && style.position === 'fixed') {
// 检查遮罩内是否有支付/成功弹窗
const innerDialog = mask.querySelector('.el-dialog, .el-message-box, [role="dialog"], [class*="dialog"], [class*="modal"]');
if (innerDialog && isSuccessDialog(innerDialog)) {
return;
}
mask.style.display = 'none';
}
});
}
/** 自动恢复:关闭错误弹窗 → 有缓存则重新触发购买 */
async function autoRecover() {
if (recovering || recoveryAttempts >= 3) return;
const dialog = findErrorDialog();
if (!dialog) return;
recovering = true;
recoveryAttempts++;
try {
// 有成功结果 → 完整恢复流程(关弹窗 + 缓存 + 重新触发)
if (S.lastSuccess) {
log('🔄 检测到错误弹窗,启动自动恢复(有缓存)…');
S.cache = S.lastSuccess;
} else {
log('🔄 检测到错误弹窗,关闭清理(无缓存)…');
}
dismissDialog(dialog);
await sleep(500);
const stillThere = findErrorDialog();
if (stillThere) {
dismissDialog(stillThere);
await sleep(300);
}
cleanupBlockingState();
// 有缓存才重新触发购买,没缓存只清理弹窗
if (S.lastSuccess) {
const btn = findPresetBuyButton(S.productMode) || findBuyButton();
if (btn) {
const label = PACKAGE_OPTIONS[S.productMode]?.label || '当前套餐';
triggerPresetBuyButton(btn, label);
log('🖱 已自动重新触发购买按钮');
} else {
log('⚠️ 未找到购买按钮,请手动点击');
alert('已获取到商品!请立即手动点击购买按钮!');
}
}
} finally {
recovering = false;
}
}
/** 持续监控:仅 retrying 阶段生效,支付弹窗停机保护 + 错误弹窗一律关闭 */
function setupDialogWatcher() {
setInterval(() => {
if (S.status !== 'retrying') return;
// 优先检测支付弹窗 → 立即停机
if (checkAndProtectPayDialog()) return;
// 检测到错误弹窗 → 一律关闭(有缓存则顺便重新触发购买)
if (!recovering && recoveryAttempts < 3) {
const dialog = findErrorDialog();
if (dialog) autoRecover();
}
}, 500);
}
// ======================== 四、Fetch 拦截器 ========================
window.fetch = async function (input, init) {
const url = typeof input === 'string' ? input : input?.url;
if (url && url.includes(CFG.PREVIEW)) {
S.lastCaptureAt = Date.now();
S.captured = {
url,
method: init?.method || 'POST',
body: init?.body,
headers: extractHeaders(init?.headers),
};
try { sessionStorage.setItem(CAPTURE_STORAGE_KEY, JSON.stringify(S.captured)); } catch (e) {}
log('🎯 捕获 preview 请求 (Fetch)');
refreshUI();
// 测试模式:只捕获请求参数,放行给真实 API
if (_testMode) {
log('🧪 测试模式:捕获请求,跳过重试引擎');
return _fetch.apply(this, [input, init]);
}
// 有缓存 → 直接返回(来自弹窗恢复)
if (S.cache) {
log('📦 返回缓存的成功响应');
const c = S.cache;
S.cache = null;
recoveryAttempts = 0;
return new Response(c.text, {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
// 已有成功结果 → 直接返回,不再重新 retry(防止误杀支付弹窗)
if (S.lastSuccess && S.status === 'success') {
log('📦 已有成功结果,直接返回');
return new Response(S.lastSuccess.text, {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
const result = await retry(url, {
method: init?.method || 'POST',
body: init?.body,
headers: extractHeaders(init?.headers),
signal: init?.signal,
});
if (result.ok) {
return new Response(result.text, {
status: result.status,
headers: { 'Content-Type': 'application/json' },
});
}
const failurePayload = buildPreviewFailurePayload(result);
if (failurePayload) {
return new Response(failurePayload.text, {
status: failurePayload.status,
headers: { 'Content-Type': 'application/json' },
});
}
return _fetch.apply(this, [input, init]);
}
if (url && url.includes(CFG.CHECK) && url.includes('bizId=null')) {
log('🚫 拦截 check(bizId=null)');
return new Response(JSON.stringify({ code: -1, msg: '等待有效bizId' }), {
status: 200, headers: { 'Content-Type': 'application/json' },
});
}
return _fetch.apply(this, [input, init]);
};
window.fetch.toString = () => 'function fetch() { [native code] }';
// ======================== 五、XHR 拦截器 ========================
const _xhrOpen = XMLHttpRequest.prototype.open;
const _xhrSend = XMLHttpRequest.prototype.send;
const _xhrSetHeader = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function (k, v) {
(this._h || (this._h = {}))[k] = v;
return _xhrSetHeader.call(this, k, v);
};
XMLHttpRequest.prototype.open = function (method, url) {
this._m = method;
this._u = url;
return _xhrOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function (body) {
const url = this._u;
if (typeof url === 'string' && url.includes(CFG.PREVIEW)) {
const self = this;
S.lastCaptureAt = Date.now();
S.captured = { url, method: this._m, body, headers: this._h || {} };
try { sessionStorage.setItem(CAPTURE_STORAGE_KEY, JSON.stringify(S.captured)); } catch (e) {}
log('🎯 捕获 preview 请求 (XHR)');
refreshUI();
// 测试模式:只捕获请求参数,放行给真实 API
if (_testMode) {
log('🧪 测试模式:捕获请求,跳过重试引擎 (XHR)');
return _xhrSend.call(this, body);
}
if (S.cache) {
log('📦 返回缓存的成功响应 (XHR)');
const c = S.cache; S.cache = null;
recoveryAttempts = 0;
fakeXHR(self, c.text);
return;
}
// 已有成功结果 → 直接返回
if (S.lastSuccess && S.status === 'success') {
log('📦 已有成功结果,直接返回 (XHR)');
fakeXHR(self, S.lastSuccess.text);
return;
}
retry(url, { method: this._m, body, headers: this._h || {} }).then(result => {
const failurePayload = buildPreviewFailurePayload(result);
fakeXHR(self, result.ok
? result.text
: (failurePayload?.text || '{"code":-1,"msg":"重试失败"}'));
});
return;
}
if (typeof url === 'string' && url.includes(CFG.CHECK) && url.includes('bizId=null')) {
log('🚫 拦截 check(bizId=null) (XHR)');
fakeXHR(this, '{"code":-1,"msg":"等待有效bizId"}');
return;
}
return _xhrSend.call(this, body);
};
function fakeXHR(xhr, text) {
setTimeout(() => {
const dp = (k, v) => Object.defineProperty(xhr, k, { value: v, configurable: true });
dp('readyState', 4); dp('status', 200); dp('statusText', 'OK');
dp('responseText', text); dp('response', text);
const rsc = new Event('readystatechange');
if (typeof xhr.onreadystatechange === 'function') xhr.onreadystatechange(rsc);
xhr.dispatchEvent(rsc);
const load = new ProgressEvent('load');
if (typeof xhr.onload === 'function') xhr.onload(load);
xhr.dispatchEvent(load);
xhr.dispatchEvent(new ProgressEvent('loadend'));
}, 0);
}
// ======================== 六、测试模式 ========================
async function startTest() {
if (!ensureInviteReady('测试模式启动前')) return;
const btn = findPresetBuyButton(S.productMode);
const label = PACKAGE_OPTIONS[S.productMode]?.label || '当前套餐';
if (!btn) {
log(`⚠️ 未找到 ${label} 的页面购买按钮,无法测试`);
alert(`未找到 ${label} 的购买按钮,请确认页面已加载套餐卡片。`);
return;
}
// 先停掉所有正在运行的重试
stopRequested = true;
_retrySessionId++;
await sleep(100);
_testMode = true;
stopRequested = false;
S.lastSuccess = null;
S.bizId = null;
S.cache = null;
S.status = 'idle';
log(`🧪 测试模式启动:将对 ${label} 发起 10 次真实请求,随后强制走成功逻辑`);
// 清理现有弹窗
const errDlg = findErrorDialog();
if (errDlg) { dismissDialog(errDlg); await sleep(120); }
cleanupBlockingState();
// 使用已捕获的请求参数,没有则手动构造(不再点击页面按钮,避免触发弹窗)
if (!S.captured) {
S.captured = {
url: `${location.origin}${CFG.PREVIEW}`,
method: 'POST',
body: null,
headers: {},
};
log('⚠️ 无已捕获请求,使用默认参数');
} else {
log(`📦 使用已捕获的请求参数: ${S.captured.url}`);
}
// 发起 10 次真实请求(用 _fetch 绕过拦截器)
const { url, method, body, headers } = S.captured;
S.status = 'retrying';
S.count = 0;
refreshUI();
let lastRealResponse = null;
for (let i = 1; i <= 10 && !stopRequested; i++) {
S.count = i;
refreshUI();
try {
const resp = await _fetch(url, {
method,
body,
headers,
credentials: 'include',
});
const text = await resp.text();
let data;
try { data = _parse(text); } catch { data = null; }
lastRealResponse = { text, data, status: resp.status };
const reason = !data ? '非JSON响应'
: data.code === 200 && data.data?.bizId ? `有效bizId=${data.data.bizId}`
: data.code === 200 && data.data?.bizId === null ? '售罄(bizId=null)'
: data.code === 555 ? '系统繁忙(555)'
: `code=${data.code}`;
log(`🧪 #${i}/10 ${reason}`);
} catch (e) {
log(`🧪 #${i}/10 网络错误: ${e.message}`);
}
await sleep(200);
}
if (stopRequested) {
_testMode = false;
S.status = 'idle';
refreshUI();
log('🧪 测试已中止');
return;
}
// ===== 强制走成功逻辑 =====
_testMode = false; // 关闭测试模式,让拦截器正常工作
const fakeBizId = 'test_' + Date.now();
const fakeText = lastRealResponse?.text || JSON.stringify({ code: 200, data: { bizId: fakeBizId }, msg: '测试模式模拟成功' });
try {
const parsed = _parse(fakeText);
if (parsed.data) parsed.data.bizId = fakeBizId;
const rewritten = JSON.stringify(parsed);
S.lastSuccess = { text: rewritten, data: parsed };
} catch (e) {
S.lastSuccess = { text: fakeText, data: { code: 200, data: { bizId: fakeBizId } } };
}
S.bizId = fakeBizId;
S.status = 'success';
S.cache = S.lastSuccess; // 缓存,下次点击直接返回
log(`🧪 测试完成!强制成功 bizId=${fakeBizId}`);
log(`🧪 已缓存成功响应,触发按钮打开弹窗…`);
refreshUI();
notifySuccess();
// 触发页面按钮 → 拦截器命中缓存 → 返回成功响应 → 页面弹窗
const btnAgain = findPresetBuyButton(S.productMode);
if (btnAgain) {
triggerPresetBuyButton(btnAgain, label);
}
}
// ======================== 八、主动抢购模式 ========================
async function startProactive(triggerMode = '主动抢购') {
if (!ensureInviteReady(`${triggerMode}前`)) return;
const btn = findPresetBuyButton(S.productMode);
if (!btn) {
const label = PACKAGE_OPTIONS[S.productMode]?.label || S.productMode;
log(`⚠️ 未找到 ${label} 对应的页面购买按钮`);
alert(`未找到 ${label} 对应的页面购买按钮,请确认当前页面已展示套餐卡片。`);
return;
}
S.lastTriggerMode = triggerMode;
stopRequested = false;
// 重置上次成功状态,允许重新 retry
S.lastSuccess = null;
S.bizId = null;
const label = PACKAGE_OPTIONS[S.productMode].label;
const captureMark = S.lastCaptureAt;
const errDlg = findErrorDialog();
if (errDlg) {
dismissDialog(errDlg);
await sleep(120);
}
cleanupBlockingState();
log(`🚀 ${triggerMode}启动,准备点击 ${label} 对应按钮`);
triggerPresetBuyButton(btn, label);
setTimeout(() => {
if (S.lastCaptureAt > captureMark || S.status === 'retrying') return;
log(`⚠️ ${label} 首次点击未触发 preview,尝试再次增强点击`);
triggerPresetBuyButton(btn, label);
setTimeout(() => {
if (S.lastCaptureAt > captureMark || S.status === 'retrying') return;
alert(`${label} 按钮已点击,但页面未发起 preview 请求。请确认套餐区域是否可操作,或手动点击一次该套餐按钮。`);
}, 700);
}, 450);
}
function stopAll() {
resetRuntimeState();
log('⏹ 已停止');
refreshUI();
}
function findBuyButton() {
for (const el of document.querySelectorAll('button, a, [role="button"], div[class*="btn"], span[class*="btn"]')) {
const t = el.textContent.trim();
if (/购买|抢购|立即|下单|订阅/.test(t) && t.length < 20 && el.offsetParent !== null) {
return el;
}
}
return null;
}
// ======================== 九、定时触发 ========================
function scheduleAt(timeStr) {
if (S.timerId) { clearTimeout(S.timerId); S.timerId = null; }
clearTimerTicker();
clearInvitePrecheck();
const parts = timeStr.split(':').map(Number);
const now = new Date();
const target = new Date(now.getFullYear(), now.getMonth(), now.getDate(), parts[0], parts[1], parts[2] || 0);
if (target <= now) { log('⚠️ 目标时间已过'); return; }
const ms = target - now;
S.timerTargetTs = target.getTime();
log(`⏰ 已设定: ${timeStr} (${Math.ceil(ms / 1000)}秒后)`);
const precheckDelay = Math.max(0, ms - INVITE_PRECHECK_LEAD_MS);
_invitePrecheckId = setTimeout(() => {
_invitePrecheckId = null;
ensureInviteReady('定时抢购前 10 秒校验');
}, precheckDelay);
S.timerId = setTimeout(() => {
S.timerId = null;
S.timerTargetTs = 0;
clearTimerTicker();
clearInvitePrecheck();
if (!ensureInviteReady('定时抢购启动前')) return;
log('⏰ 时间到! 启动抢购!');
startProactive('定时抢购');
}, ms - 50);
startTimerTicker();
refreshUI();
}
// ======================== 十、浮动控制面板 ========================
function createPanel() {
const panel = document.createElement('div');
panel.id = 'glm-rush';
panel.innerHTML = `
<style>
#glm-rush{position:fixed;top:10px;right:10px;width:340px;background:#1a1a2e;color:#e0e0e0;
border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,.6);z-index:999999;
font:13px/1.5 Consolas,'Courier New',monospace;user-select:none}
#glm-rush *{box-sizing:border-box;margin:0;padding:0}
.glm-hd{background:linear-gradient(135deg,#0f3460,#16213e);padding:9px 14px;
border-radius:12px 12px 0 0;display:flex;justify-content:space-between;align-items:center;cursor:move}
.glm-hd b{font-size:14px;letter-spacing:.5px}
.glm-mn{background:none;border:none;color:#aaa;cursor:pointer;font-size:20px;line-height:1;padding:0 4px}
.glm-mn:hover{color:#fff}
.glm-bd{padding:12px 14px 14px}
.glm-st{padding:8px;border-radius:8px;text-align:center;font-weight:700;margin-bottom:10px;transition:background .3s}
.glm-st-idle{background:#2d3436}
.glm-st-retrying{background:#e17055;animation:glm-pulse 1s infinite}
.glm-st-success{background:#00b894}
.glm-st-failed{background:#d63031}
@keyframes glm-pulse{50%{opacity:.7}}
.glm-cap{font-size:11px;padding:5px 8px;background:#2d3436;border-radius:6px;margin-bottom:10px;
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.glm-row{display:flex;align-items:center;gap:6px;margin-bottom:8px;font-size:12px;flex-wrap:wrap}
.glm-row input[type=number],.glm-row input[type=time]{
width:72px;padding:4px 6px;border:1px solid #444;border-radius:4px;
background:#2d3436;color:#fff;text-align:center;font-size:12px}
.glm-row input[type=time]{width:108px}
.glm-row select{
padding:4px 6px;border:1px solid #444;border-radius:4px;
background:#2d3436;color:#fff;font-size:12px}
.glm-row select{min-width:150px}
#glm-timer-info{display:inline-block;min-width:88px;line-height:1.2;white-space:nowrap}
.glm-btns{display:flex;gap:8px;margin-bottom:10px}
.glm-btns button{flex:1;padding:8px;border:none;border-radius:6px;cursor:pointer;
font-weight:700;font-size:12px;color:#fff;transition:opacity .2s}
.glm-btns button:hover{opacity:.85}
.glm-b-go{background:#0984e3}
.glm-b-stop{background:#d63031}
.glm-b-test{background:#00b894;flex:0 0 auto !important;padding:4px 10px !important}
.glm-b-time{background:#6c5ce7;flex:0 0 auto !important;padding:4px 10px !important}
.glm-logs{max-height:170px;overflow-y:auto;background:#0d1117;border-radius:6px;
padding:6px 8px;font-size:11px;line-height:1.7}
.glm-logs div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.glm-logs::-webkit-scrollbar{width:4px}
.glm-logs::-webkit-scrollbar-thumb{background:#444;border-radius:2px}
</style>
<div class="glm-hd" id="glm-drag">
<b>🎯 GLM 抢购助手 v3.6</b>
<button class="glm-mn" id="glm-min">−</button>
</div>
<div class="glm-bd" id="glm-bd">
<div class="glm-st glm-st-idle" id="glm-st">⏳ 等待中</div>
<div class="glm-cap" id="glm-cap">🎯 套餐: Lite | 模式: 页面按钮点击</div>
<div class="glm-row">
<span>并发</span><input type="number" id="glm-conc" value="${CFG.concurrency}" min="1" max="20" step="1">
<span>极速</span><input type="number" id="glm-turbo" value="${CFG.turboConcurrency}" min="1" max="30" step="1">
<span style="margin-left:6px">上限</span><input type="number" id="glm-max" value="${CFG.maxRetry}" min="10" max="9999" step="10"><span>次</span>
</div>
<div class="glm-row">
<span>稳态间隔</span><input type="number" id="glm-delay" value="${CFG.slowDelay}" min="0" max="5000" step="10"><span>ms</span>
<span>爆发后</span><input type="number" id="glm-fast-delay" value="${CFG.fastDelay}" min="0" max="1000" step="10"><span>ms</span>
</div>
<div class="glm-row">
<span>套餐</span>
<select id="glm-product-mode">
<option value="lite">Lite</option>
<option value="pro">Pro</option>
<option value="max">Max</option>
</select>
</div>
<div class="glm-row">
<span>定时</span><input type="time" id="glm-time" step="1" value="${DEFAULT_TIMER_TIME}">
<button class="glm-b-time" id="glm-time-set">设定</button>
<span id="glm-timer-info" style="color:#6c5ce7;font-size:11px"></span>
</div>
<div class="glm-btns">
<button class="glm-b-go" id="glm-go">▶ 主动抢购</button>
<button class="glm-b-test" id="glm-test">🧪 测试</button>
<button class="glm-b-stop" id="glm-stop" style="display:none">■ 停止</button>
</div>
<div class="glm-logs" id="glm-logs"></div>
</div>`;
document.body.appendChild(panel);
const $ = id => document.getElementById(id);
$('glm-go').onclick = () => startProactive('主动抢购');
$('glm-test').onclick = () => startTest();
$('glm-stop').onclick = stopAll;
$('glm-conc').onchange = function () {
CFG.concurrency = Math.max(1, +this.value || DEFAULT_CFG.concurrency);
if (CFG.turboConcurrency < CFG.concurrency) {
CFG.turboConcurrency = CFG.concurrency;
$('glm-turbo').value = CFG.turboConcurrency;
}
saveCfg(CFG);
};
$('glm-turbo').onchange = function () { CFG.turboConcurrency = Math.max(CFG.concurrency, +this.value || DEFAULT_CFG.turboConcurrency); this.value = CFG.turboConcurrency; saveCfg(CFG); };
$('glm-delay').onchange = function () { CFG.slowDelay = Math.max(0, +this.value || DEFAULT_CFG.slowDelay); saveCfg(CFG); };
$('glm-fast-delay').onchange = function () { CFG.fastDelay = Math.max(0, +this.value || DEFAULT_CFG.fastDelay); saveCfg(CFG); };
$('glm-max').onchange = function () { CFG.maxRetry = Math.max(10, +this.value || DEFAULT_CFG.maxRetry); saveCfg(CFG); };
$('glm-time-set').onclick = function () { const v = $('glm-time').value; if (v) scheduleAt(v); };
$('glm-product-mode').value = S.productMode;
$('glm-product-mode').onchange = function () {
S.productMode = this.value;
refreshUI();
};
$('glm-min').onclick = function () {
const bd = $('glm-bd');
const hidden = bd.style.display === 'none';
bd.style.display = hidden ? '' : 'none';
this.textContent = hidden ? '−' : '+';
};
// 拖拽
let sx, sy, sl, st;
$('glm-drag').onmousedown = function (e) {
sx = e.clientX; sy = e.clientY;
const rect = panel.getBoundingClientRect();
sl = rect.left; st = rect.top;
const onMove = function (e) {
panel.style.left = (sl + e.clientX - sx) + 'px';
panel.style.top = (st + e.clientY - sy) + 'px';
panel.style.right = 'auto';
};
const onUp = function () {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
};
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
};
log('🚀 v3.6 已加载 (并发 preview + check 双重校验)');
setupDialogWatcher();
}
function refreshUI() {
const stEl = document.getElementById('glm-st');
if (!stEl) return;
stEl.className = 'glm-st glm-st-' + S.status;
const turboActive = S.status === 'retrying' && S.count < CFG.turboConcurrency * CFG.turboSec;
stEl.textContent = S.status === 'idle' ? '⏳ 等待中'
: S.status === 'retrying' ? `${turboActive ? '⚡' : '🔄'} 并发重试中… ${S.count}/${CFG.maxRetry}`
: S.status === 'success' ? `✅ 成功! bizId=${S.bizId}`
: `❌ 失败 (${S.count}次)`;
const capEl = document.getElementById('glm-cap');
if (capEl) {
capEl.textContent = `🎯 套餐: ${getSelectedProductLabel()} | 模式: 页面按钮点击`;
}
const goBtn = document.getElementById('glm-go');
const stopBtn = document.getElementById('glm-stop');
const testBtn = document.getElementById('glm-test');
if (goBtn && stopBtn) {
goBtn.style.display = S.status === 'retrying' ? 'none' : '';
stopBtn.style.display = S.status === 'retrying' ? '' : 'none';
}
if (testBtn) {
testBtn.style.display = S.status === 'retrying' ? 'none' : '';
}
const modeEl = document.getElementById('glm-product-mode');
if (modeEl) modeEl.value = S.productMode;
updateTimerInfo();
}
function refreshLog() {
const el = document.getElementById('glm-logs');
if (!el) return;
const last = S.logs[S.logs.length - 1];
if (last) {
const div = document.createElement('div');
div.textContent = last;
el.appendChild(div);
while (el.children.length > 80) el.removeChild(el.firstChild);
el.scrollTop = el.scrollHeight;
}
}
// ======================== 启动 ========================
console.log('[GLM抢购] 🚀 v3.6 全自动版 (并发 preview + check 双重校验) 已注入');
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createPanel);
} else {
createPanel();
}
})();