revert(userscript): 回退到 v8.19 (e07ec9e)

v8.20 / v8.21 / v8.21.x 在用户本地确认主循环失效
(不会自动购买 / 不会自动关弹窗),回退到 PR #17 之前
的 v8.19 黄金时间延长版,这是用户最后确认正常的版本。

高级模式 / ±20% 抖动 / incognito 提示全部撤销(它们是
引入 CFG undefined 死循环的根源,且不再单独发布)。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
OLmatter
2026-06-15 23:43:30 +08:00
co-authored by Claude Opus 4.7
parent 03dd2f1e0e
commit 6f00ad245f
2 changed files with 58 additions and 266 deletions
+29 -133
View File
@@ -1,7 +1,7 @@
// ==UserScript==
// @name 智谱 GLM Coding Plan 抢购助手 + 本地 OCR 自动验证码
// @namespace http://tampermonkey.net/
// @version 8.21.3
// @version 8.19
// @description GLM Coding Rush / 智谱 GLM Coding Plan 抢购助手,一键抢购油猴脚本 / Tampermonkey userscript,配合本地 CPU/GPU OCR 自动识别中文点选验证码并点击,支持多窗口并发、限流重试和支付页安全保护
// @author mumumi
// @include https://*bigmodel.cn/glm-coding*
@@ -19,9 +19,7 @@
// @grant GM_xmlhttpRequest
// @connect localhost
// @connect localhost:8888
// @connect localhost
// @connect 127.0.0.1:8888
// @connect 127.0.0.1
// @connect gtimg.com
// @connect *.gtimg.com
// @connect captcha.qcloud.com
@@ -510,14 +508,6 @@
AUTO_CLICK_SUB : true,
AUTO_CAPTCHA_CLICK : true,
AUTO_CAPTCHA_CONFIRM: false,
RUSH_ENABLED : false,
RUSH_TARGET_HOUR : 9,
RUSH_TARGET_MIN : 59,
RUSH_TARGET_SEC : 58,
// v8.21: 高级模式(默认关闭;开启后可调点击/限流重试间隔,加 ±20% 随机抖动防风控)
ADVANCED_MODE : false,
CAPTCHA_CLICK_DELAY : 220, // 验证码三个字之间的间隔(ms)
RL_RETRY_DELAY : 1000, // 限流弹窗关闭后多久再点购买(ms)
};
function loadCfg() { try { const s = GM_getValue(STORAGE_KEY, null); return s ? { ...DEF, ...JSON.parse(s) } : { ...DEF }; } catch { return { ...DEF }; } }
function saveCfg(c) { GM_setValue(STORAGE_KEY, JSON.stringify(c)); }
@@ -632,7 +622,7 @@
return time >= start && time <= end;
}
// ── DOM 访问 ──────────────────────────────────────────────────────────────
const tabEl = n => document.querySelectorAll('#switchTabBox .switch-tab-item')[n - 1];
const tabEl = n => document.querySelectorAll('#switchTabBox .switch-tab-item')[n];
const btnEl = n => document.querySelector(`.glm-coding-package-list > div:nth-child(${n}) > div > .package-card-btn-box > button`);
const canBuy = b => b && !b.disabled && !b.classList.contains('is-disabled') && !b.classList.contains('disabled') && !/售罄|补货|暂时/.test(b.innerText || '');
const isSoldOut = b => /售罄|补货|暂时/.test(b?.innerText || '');
@@ -705,13 +695,8 @@
function checkPayDialog() {
const dlg = getPayDialog();
if (!dlg) return 'keep';
// 抢购模式锁定:抢购确认后永不自动关闭支付弹窗
if (window.__glmRushConfirmed) {
window.__glmRushDialogSeen = 1;
return 'keep';
}
// 绝对安全锁:成功过且当前 bizId 有效才不关
if (everSucceeded && PS.bizId) return 'keep';
// 绝对安全锁:本次会话中只要成功过一次,永不关闭
if (everSucceeded) return 'keep';
// 接口还没返回
if (PS.inProgress) return 'keep';
// ── 情况 A:接口 555 系统繁忙 → 关弹窗试下一个
@@ -866,37 +851,6 @@
<span title="默认关闭。开启后点完验证码文字会自动点确定;关闭后需要你手动点确定。" style="margin-left:6px;cursor:help;color:#999;font-size:14px;border:1px solid #ccc;border-radius:50%;width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center;line-height:1">?</span>
</label>
</div>
<div style="border-top:1px dashed #eee;padding-top:12px;margin-top:4px"></div>
<label style="display:flex;align-items:center;cursor:pointer">
<input type="checkbox" id="glm-re" ${CFG.RUSH_ENABLED ? 'checked' : ''} style="margin-right:8px">
<span style="font-size:14px;color:#555">启用抢购模式(验证码卡点确认)</span>
</label>
<div style="display:flex;align-items:center;gap:6px;margin-top:4px;padding-left:26px">
<span style="font-size:13px;color:#888">目标时间:</span>
<input type="number" id="glm-rh" value="${CFG.RUSH_TARGET_HOUR}" min="0" max="23" style="width:52px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
<span style="font-size:14px;color:#888">:</span>
<input type="number" id="glm-rm" value="${CFG.RUSH_TARGET_MIN}" min="0" max="59" style="width:52px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
<span style="font-size:14px;color:#888">:</span>
<input type="number" id="glm-rs" value="${CFG.RUSH_TARGET_SEC}" min="0" max="59" style="width:52px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
</div>
<div style="border-top:1px dashed #eee;padding-top:12px;margin-top:4px"></div>
<label style="display:flex;align-items:center;cursor:pointer">
<input type="checkbox" id="glm-advanced" ${CFG.ADVANCED_MODE ? 'checked' : ''} style="margin-right:8px">
<span style="font-size:14px;color:#555">高级模式(自定义点击/重试速度)</span>
<span title="经典模式已自带 ±20% 随机抖动防风控。开启高级模式可调验证码点击间隔、限流重试间隔,抖动仍然保留。" style="margin-left:6px;cursor:help;color:#999;font-size:14px;border:1px solid #ccc;border-radius:50%;width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center;line-height:1">?</span>
</label>
<div id="glm-advanced-panel" style="margin-top:8px;padding:10px 12px 10px 26px;background:#fafafa;border-radius:6px;display:${CFG.ADVANCED_MODE ? 'block' : 'none'}">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
<span style="font-size:13px;color:#666;min-width:140px">验证码点击间隔 (ms)</span>
<input type="number" id="glm-ccd" value="${CFG.CAPTCHA_CLICK_DELAY}" min="50" max="2000" step="10" style="width:80px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
<span style="font-size:12px;color:#999">默认 220,±20% 抖动</span>
</div>
<div style="display:flex;align-items:center;gap:8px">
<span style="font-size:13px;color:#666;min-width:140px">限流重试间隔 (ms)</span>
<input type="number" id="glm-rld" value="${CFG.RL_RETRY_DELAY}" min="100" max="10000" step="50" style="width:80px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
<span style="font-size:12px;color:#999">默认 1000,±20% 抖动</span>
</div>
</div>
<div style="display:flex;justify-content:space-between;gap:10px">
<button id="glm-multi" style="padding:8px 16px;border:1px solid #52c41a;background:#f6ffed;color:#52c41a;border-radius:6px;cursor:pointer;font-weight:600">🚀 一键多开</button>
<div style="display:flex;gap:10px">
@@ -909,9 +863,6 @@
const getPkgs = buildTransferBox(document.getElementById('glm-wp'), PKGS_MAP, CFG.PACKAGES_PRIORITY, '套餐优先级');
const getTabs = buildTransferBox(document.getElementById('glm-wt'), TABS_MAP, CFG.TABS_PRIORITY, '订阅周期优先级');
panel.querySelector('#glm-cc').onclick = () => ov.remove();
panel.querySelector('#glm-advanced').onchange = (e) => {
panel.querySelector('#glm-advanced-panel').style.display = e.target.checked ? 'block' : 'none';
};
panel.querySelector('#glm-multi').onclick = () => { openMultipleWindows(); };
panel.querySelector('#glm-cs').onclick = () => {
const p = getPkgs(), t = getTabs();
@@ -925,13 +876,6 @@
AUTO_CLICK_SUB : panel.querySelector('#glm-acs').checked,
AUTO_CAPTCHA_CLICK: panel.querySelector('#glm-acc').checked,
AUTO_CAPTCHA_CONFIRM: panel.querySelector('#glm-acf').checked,
RUSH_ENABLED: panel.querySelector('#glm-re').checked,
RUSH_TARGET_HOUR: parseInt(panel.querySelector('#glm-rh').value, 10),
RUSH_TARGET_MIN: parseInt(panel.querySelector('#glm-rm').value, 10),
RUSH_TARGET_SEC: parseInt(panel.querySelector('#glm-rs').value, 10),
ADVANCED_MODE: panel.querySelector('#glm-advanced').checked,
CAPTCHA_CLICK_DELAY: Math.max(50, parseInt(panel.querySelector('#glm-ccd').value, 10) || 220),
RL_RETRY_DELAY: Math.max(100, parseInt(panel.querySelector('#glm-rld').value, 10) || 1000),
SAFE_DEFAULTS_VERSION,
});
ov.remove(); alert('已保存,即将刷新。'); location.reload();
@@ -943,16 +887,7 @@
// ═══════════════════════════════════════════════════════════════════════════
function tick() {
if (state === 'DONE') return;
if (window.__glmRushConfirmed && window.__glmRushDialogSeen && !getPayDialog()) {
window.__glmRushConfirmed = 0; window.__glmRushDialogSeen = 0;
console.log('[GLM] rush lock cleared');
}
if (ensureDiscountEntry()) return;
// v8.21: 黄金时间内每天首条提示建议试试无痕模式(sessionStorage 去重)
if (isGoldenTime() && !sessionStorage.getItem('glm_nudge_incognito_v1')) {
sessionStorage.setItem('glm_nudge_incognito_v1', '1');
setBar('🕶️ 抢不到?试试无痕窗口(Ctrl+Shift+N)!没有历史 Cookie/缓存/Service Worker,可能消除隐形的风控标记。Tampermonkey 需在扩展详情页允许在无痕中启用。', '#722ed1');
}
if (state === 'SLEEPING') {
const rem = sleepUntil - Date.now();
if (rem <= 0) {
@@ -1107,14 +1042,7 @@
return;
}
setBar(`⚠️ 限流 ${taskRLCount}/${MAX_RL},自动关闭后重试...`, '#d46b08');
// v8.21: 高级模式可调限流重试间隔(带 ±20% 抖动防风控)
// 期间用 WAITING_RL 状态占位,避免下一轮 tick 重复关弹窗
var _rld = Number(CFG.RL_RETRY_DELAY) || 1000;
var _rldDelta = _rld * 0.2;
var _rlDelay = Math.max(1, Math.round(_rld + (Math.random() * 2 - 1) * _rldDelta));
taskPhase = 'WAITING_RL';
setTimeout(() => { taskPhase = 'IDLE'; }, _rlDelay);
return;
taskPhase = 'IDLE'; return;
}
if (isPayDialog()) {
const verdict = checkPayDialog();
@@ -1142,9 +1070,9 @@
return;
}
const prices = readDialogPrices();
if (everSucceeded && PS.bizId || prices?.any) {
if (everSucceeded || prices?.any) {
state = 'DONE';
if (everSucceeded && PS.bizId) showPayAlarm();
if (everSucceeded) showPayAlarm();
setBar('💳 <b>支付弹窗已出现!请立即扫码支付!</b> 脚本已停止。', '#16a34a');
} else {
setBar(`🔄 ${TABS_MAP[tab]}·${PKGS_MAP[pkg]} 弹窗等待确认...`, '#1677ff');
@@ -1221,24 +1149,15 @@
'use strict';
if (window.__glmCaptchaPromptBridge === 1) return;
window.__glmCaptchaPromptBridge = 1;
const RUSH_CFG = (() => {
try {
const raw = GM_getValue('glm_coding_config_v5', '{}');
const cfg = JSON.parse(raw || '{}');
const ph = parseInt(cfg.RUSH_TARGET_HOUR, 10); const pm = parseInt(cfg.RUSH_TARGET_MIN, 10); const ps = parseInt(cfg.RUSH_TARGET_SEC, 10);
return {
enabled: cfg.RUSH_ENABLED === true,
targetHour: Number.isFinite(ph) ? ph : 9,
targetMin: Number.isFinite(pm) ? pm : 59,
targetSec: Number.isFinite(ps) ? ps : 58,
staggerMs: 2000,
pollInterval: 50,
pollTimeout: 20000,
};
} catch {
return { enabled: false, targetHour: 9, targetMin: 59, targetSec: 58, staggerMs: 2000, pollInterval: 50, pollTimeout: 20000 };
}
})();
const RUSH_CONFIG = {
enabled: true,
targetHour: 10,
targetMin: 0,
targetSec: 0,
staggerMs: 2000,
pollInterval: 50,
pollTimeout: 20000,
};
const CAPTCHA_CFG = (() => {
try {
const raw = GM_getValue('glm_coding_config_v5', '{}');
@@ -1247,13 +1166,6 @@
return { AUTO_CAPTCHA_CLICK: true, AUTO_CAPTCHA_CONFIRM: false };
}
})();
// v8.21.3: captcha IIFE 没有 CFG 闭包,必须走 CAPTCHA_CFG 拿点击/重试间隔
const _ccd = Number(CAPTCHA_CFG.CAPTCHA_CLICK_DELAY) || 220;
const _cdDelta = _ccd * 0.2;
const _clickDelay = Math.max(1, Math.round(_ccd + (Math.random() * 2 - 1) * _cdDelta));
const _rld = Number(CAPTCHA_CFG.RL_RETRY_DELAY) || 1000;
const _rldDelta = _rld * 0.2;
const _rlDelay = Math.max(1, Math.round(_rld + (Math.random() * 2 - 1) * _rldDelta));
function getWindowIndex() {
const params = new URLSearchParams(location.search);
return parseInt(params.get('wi') || '0', 10);
@@ -1261,8 +1173,8 @@
function getTargetTimestamp() {
const now = new Date();
const target = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
RUSH_CFG.targetHour, RUSH_CFG.targetMin, RUSH_CFG.targetSec, 0);
const offset = getWindowIndex() * RUSH_CFG.staggerMs;
RUSH_CONFIG.targetHour, RUSH_CONFIG.targetMin, RUSH_CONFIG.targetSec, 0);
const offset = getWindowIndex() * RUSH_CONFIG.staggerMs;
return target.getTime() + offset;
}
let lastCaptchaText = '';
@@ -1301,14 +1213,14 @@
return new Promise((resolve, reject) => {
const start = Date.now();
function poll() {
if (Date.now() - start > RUSH_CFG.pollTimeout) {
if (Date.now() - start > RUSH_CONFIG.pollTimeout) {
reject(new Error('poll timeout'));
return;
}
serverRequest('POST', '/result', { ts: ts }).then(d => {
if (d.has_result) resolve(d.result);
else setTimeout(poll, RUSH_CFG.pollInterval);
}).catch(() => setTimeout(poll, RUSH_CFG.pollInterval));
else setTimeout(poll, RUSH_CONFIG.pollInterval);
}).catch(() => setTimeout(poll, RUSH_CONFIG.pollInterval));
}
poll();
});
@@ -1331,6 +1243,8 @@
var selectors = [
'.tencent-captcha-dy__btn-confirm',
'.tencent-captcha-dy__footer .btn',
'.pay-dialog button.el-button--primary',
'.el-dialog__wrapper:not([style*="display: none"]) .el-button--primary',
'[class*="captcha"] [class*="confirm"]',
'[class*="captcha"] [class*="submit"]',
];
@@ -1805,8 +1719,6 @@
if (!result || !result.success || !Array.isArray(result.click_coords)) {
throw new Error('bad direct result: ' + JSON.stringify(resp).substring(0, 180));
}
// v8.21.2: 成功后锁定 30 秒,期间同一张图不再重发(即使 catch 被异常触发)
window.__glmSentLockUntil = Date.now() + 30000;
var rect = bgEl.getBoundingClientRect();
console.log('[captcha-direct-page] click result:', JSON.stringify(result).substring(0, 260));
for (var i = 0; i < result.click_coords.length; i++) {
@@ -1817,40 +1729,24 @@
if (!Number.isFinite(ny) && Number.isFinite(Number(c.rel_y))) ny = Number(c.rel_y) / rect.height;
if (!Number.isFinite(nx) || !Number.isFinite(ny)) continue;
dispatchClickAt(bgEl, nx * rect.width, ny * rect.height, c.char || String(i + 1));
await new Promise(function(r) { setTimeout(r, _clickDelay); });
await new Promise(function(r) { setTimeout(r, 220); });
}
await new Promise(function(r) { setTimeout(r, 350); });
rushState = 'idle';
if (RUSH_CFG.enabled) {
var targetTs = getTargetTimestamp();
if (Date.now() < targetTs) {
console.log('[captcha-rush] waiting ' + ((targetTs - Date.now())/1000).toFixed(1) + 's until target...');
await waitForTargetTime();
console.log('[captcha-rush] target time reached, confirming...');
var clicked = findAndClickConfirm();
if (clicked) { window.__glmRushConfirmed = Date.now(); }
} else {
var clicked = findAndClickConfirm();
if (clicked) { window.__glmRushConfirmed = Date.now(); }
}
} else if (CAPTCHA_CFG.AUTO_CAPTCHA_CONFIRM) {
if (CAPTCHA_CFG.AUTO_CAPTCHA_CONFIRM) {
findAndClickConfirm();
} else {
console.log('[captcha-direct-page] captcha confirm is disabled; waiting for manual confirm');
}
} catch (e) {
console.error('[captcha-direct-page] error:', e);
// v8.21.3: 出错时不再立即重置 captchaSent,避免 50ms tick 死循环。
// 改为加 30s 冷却:期间相同 prompt 不再重发;prompt 变化时 checkCaptchaPrompt 自然会重置。
window.__glmCaptchaCooldownUntil = Date.now() + 30000;
captchaSent = false;
lastCaptchaText = '';
} finally {
rushState = 'idle';
}
}
async function checkCaptchaPrompt() {
if (rushState === 'solving') return;
// v8.21.3: 出错后 30s 冷却,期间不再触发 handleCaptchaDirectInPage
if (window.__glmCaptchaCooldownUntil && Date.now() < window.__glmCaptchaCooldownUntil) return;
var found = findCaptchaPromptElement();
if (!found) { captchaSent = false; return; }
var payloadText = found.chars.join('');
@@ -1867,7 +1763,7 @@
console.log('[captcha] page direct solver:', payloadText);
handleCaptchaDirectInPage(found.chars).catch(function(e) { console.error('[captcha-direct-page] unhandled:', e); });
return;
if (RUSH_CFG.enabled) {
if (RUSH_CONFIG.enabled) {
handleCaptchaRush(found.chars).catch(function(e) { console.error('[captcha-rush] unhandled:', e); });
} else {
serverRequest('POST', '/captcha', { text: payloadText, ts: Date.now() })
@@ -1877,5 +1773,5 @@
}
}
setInterval(checkCaptchaPrompt, 50);
console.log('[captcha] bridge v2 started | rush=' + RUSH_CFG.enabled + ' | wi=' + getWindowIndex() + ' | target=10:' + String(RUSH_CFG.targetSec).padStart(2,'0') + '+' + (getWindowIndex() * RUSH_CFG.staggerMs / 1000) + 's');
console.log('[captcha] bridge v2 started | rush=' + RUSH_CONFIG.enabled + ' | wi=' + getWindowIndex() + ' | target=10:' + String(RUSH_CONFIG.targetSec).padStart(2,'0') + '+' + (getWindowIndex() * RUSH_CONFIG.staggerMs / 1000) + 's');
})();
+29 -133
View File
@@ -1,7 +1,7 @@
// ==UserScript==
// @name 智谱 GLM Coding Plan 抢购助手 + 本地 OCR 自动验证码
// @namespace http://tampermonkey.net/
// @version 8.21.3
// @version 8.19
// @description GLM Coding Rush / 智谱 GLM Coding Plan 抢购助手,一键抢购油猴脚本 / Tampermonkey userscript,配合本地 CPU/GPU OCR 自动识别中文点选验证码并点击,支持多窗口并发、限流重试和支付页安全保护
// @author mumumi
// @include https://*bigmodel.cn/glm-coding*
@@ -19,9 +19,7 @@
// @grant GM_xmlhttpRequest
// @connect localhost
// @connect localhost:8888
// @connect localhost
// @connect 127.0.0.1:8888
// @connect 127.0.0.1
// @connect gtimg.com
// @connect *.gtimg.com
// @connect captcha.qcloud.com
@@ -510,14 +508,6 @@
AUTO_CLICK_SUB : true,
AUTO_CAPTCHA_CLICK : true,
AUTO_CAPTCHA_CONFIRM: false,
RUSH_ENABLED : false,
RUSH_TARGET_HOUR : 9,
RUSH_TARGET_MIN : 59,
RUSH_TARGET_SEC : 58,
// v8.21: 高级模式(默认关闭;开启后可调点击/限流重试间隔,加 ±20% 随机抖动防风控)
ADVANCED_MODE : false,
CAPTCHA_CLICK_DELAY : 220, // 验证码三个字之间的间隔(ms)
RL_RETRY_DELAY : 1000, // 限流弹窗关闭后多久再点购买(ms)
};
function loadCfg() { try { const s = GM_getValue(STORAGE_KEY, null); return s ? { ...DEF, ...JSON.parse(s) } : { ...DEF }; } catch { return { ...DEF }; } }
function saveCfg(c) { GM_setValue(STORAGE_KEY, JSON.stringify(c)); }
@@ -632,7 +622,7 @@
return time >= start && time <= end;
}
// ── DOM 访问 ──────────────────────────────────────────────────────────────
const tabEl = n => document.querySelectorAll('#switchTabBox .switch-tab-item')[n - 1];
const tabEl = n => document.querySelectorAll('#switchTabBox .switch-tab-item')[n];
const btnEl = n => document.querySelector(`.glm-coding-package-list > div:nth-child(${n}) > div > .package-card-btn-box > button`);
const canBuy = b => b && !b.disabled && !b.classList.contains('is-disabled') && !b.classList.contains('disabled') && !/售罄|补货|暂时/.test(b.innerText || '');
const isSoldOut = b => /售罄|补货|暂时/.test(b?.innerText || '');
@@ -705,13 +695,8 @@
function checkPayDialog() {
const dlg = getPayDialog();
if (!dlg) return 'keep';
// 抢购模式锁定:抢购确认后永不自动关闭支付弹窗
if (window.__glmRushConfirmed) {
window.__glmRushDialogSeen = 1;
return 'keep';
}
// 绝对安全锁:成功过且当前 bizId 有效才不关
if (everSucceeded && PS.bizId) return 'keep';
// 绝对安全锁:本次会话中只要成功过一次,永不关闭
if (everSucceeded) return 'keep';
// 接口还没返回
if (PS.inProgress) return 'keep';
// ── 情况 A:接口 555 系统繁忙 → 关弹窗试下一个
@@ -866,37 +851,6 @@
<span title="默认关闭。开启后点完验证码文字会自动点确定;关闭后需要你手动点确定。" style="margin-left:6px;cursor:help;color:#999;font-size:14px;border:1px solid #ccc;border-radius:50%;width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center;line-height:1">?</span>
</label>
</div>
<div style="border-top:1px dashed #eee;padding-top:12px;margin-top:4px"></div>
<label style="display:flex;align-items:center;cursor:pointer">
<input type="checkbox" id="glm-re" ${CFG.RUSH_ENABLED ? 'checked' : ''} style="margin-right:8px">
<span style="font-size:14px;color:#555">启用抢购模式(验证码卡点确认)</span>
</label>
<div style="display:flex;align-items:center;gap:6px;margin-top:4px;padding-left:26px">
<span style="font-size:13px;color:#888">目标时间:</span>
<input type="number" id="glm-rh" value="${CFG.RUSH_TARGET_HOUR}" min="0" max="23" style="width:52px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
<span style="font-size:14px;color:#888">:</span>
<input type="number" id="glm-rm" value="${CFG.RUSH_TARGET_MIN}" min="0" max="59" style="width:52px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
<span style="font-size:14px;color:#888">:</span>
<input type="number" id="glm-rs" value="${CFG.RUSH_TARGET_SEC}" min="0" max="59" style="width:52px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
</div>
<div style="border-top:1px dashed #eee;padding-top:12px;margin-top:4px"></div>
<label style="display:flex;align-items:center;cursor:pointer">
<input type="checkbox" id="glm-advanced" ${CFG.ADVANCED_MODE ? 'checked' : ''} style="margin-right:8px">
<span style="font-size:14px;color:#555">高级模式(自定义点击/重试速度)</span>
<span title="经典模式已自带 ±20% 随机抖动防风控。开启高级模式可调验证码点击间隔、限流重试间隔,抖动仍然保留。" style="margin-left:6px;cursor:help;color:#999;font-size:14px;border:1px solid #ccc;border-radius:50%;width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center;line-height:1">?</span>
</label>
<div id="glm-advanced-panel" style="margin-top:8px;padding:10px 12px 10px 26px;background:#fafafa;border-radius:6px;display:${CFG.ADVANCED_MODE ? 'block' : 'none'}">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
<span style="font-size:13px;color:#666;min-width:140px">验证码点击间隔 (ms)</span>
<input type="number" id="glm-ccd" value="${CFG.CAPTCHA_CLICK_DELAY}" min="50" max="2000" step="10" style="width:80px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
<span style="font-size:12px;color:#999">默认 220,±20% 抖动</span>
</div>
<div style="display:flex;align-items:center;gap:8px">
<span style="font-size:13px;color:#666;min-width:140px">限流重试间隔 (ms)</span>
<input type="number" id="glm-rld" value="${CFG.RL_RETRY_DELAY}" min="100" max="10000" step="50" style="width:80px;padding:3px 6px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;text-align:center">
<span style="font-size:12px;color:#999">默认 1000,±20% 抖动</span>
</div>
</div>
<div style="display:flex;justify-content:space-between;gap:10px">
<button id="glm-multi" style="padding:8px 16px;border:1px solid #52c41a;background:#f6ffed;color:#52c41a;border-radius:6px;cursor:pointer;font-weight:600">🚀 一键多开</button>
<div style="display:flex;gap:10px">
@@ -909,9 +863,6 @@
const getPkgs = buildTransferBox(document.getElementById('glm-wp'), PKGS_MAP, CFG.PACKAGES_PRIORITY, '套餐优先级');
const getTabs = buildTransferBox(document.getElementById('glm-wt'), TABS_MAP, CFG.TABS_PRIORITY, '订阅周期优先级');
panel.querySelector('#glm-cc').onclick = () => ov.remove();
panel.querySelector('#glm-advanced').onchange = (e) => {
panel.querySelector('#glm-advanced-panel').style.display = e.target.checked ? 'block' : 'none';
};
panel.querySelector('#glm-multi').onclick = () => { openMultipleWindows(); };
panel.querySelector('#glm-cs').onclick = () => {
const p = getPkgs(), t = getTabs();
@@ -925,13 +876,6 @@
AUTO_CLICK_SUB : panel.querySelector('#glm-acs').checked,
AUTO_CAPTCHA_CLICK: panel.querySelector('#glm-acc').checked,
AUTO_CAPTCHA_CONFIRM: panel.querySelector('#glm-acf').checked,
RUSH_ENABLED: panel.querySelector('#glm-re').checked,
RUSH_TARGET_HOUR: parseInt(panel.querySelector('#glm-rh').value, 10),
RUSH_TARGET_MIN: parseInt(panel.querySelector('#glm-rm').value, 10),
RUSH_TARGET_SEC: parseInt(panel.querySelector('#glm-rs').value, 10),
ADVANCED_MODE: panel.querySelector('#glm-advanced').checked,
CAPTCHA_CLICK_DELAY: Math.max(50, parseInt(panel.querySelector('#glm-ccd').value, 10) || 220),
RL_RETRY_DELAY: Math.max(100, parseInt(panel.querySelector('#glm-rld').value, 10) || 1000),
SAFE_DEFAULTS_VERSION,
});
ov.remove(); alert('已保存,即将刷新。'); location.reload();
@@ -943,16 +887,7 @@
// ═══════════════════════════════════════════════════════════════════════════
function tick() {
if (state === 'DONE') return;
if (window.__glmRushConfirmed && window.__glmRushDialogSeen && !getPayDialog()) {
window.__glmRushConfirmed = 0; window.__glmRushDialogSeen = 0;
console.log('[GLM] rush lock cleared');
}
if (ensureDiscountEntry()) return;
// v8.21: 黄金时间内每天首条提示建议试试无痕模式(sessionStorage 去重)
if (isGoldenTime() && !sessionStorage.getItem('glm_nudge_incognito_v1')) {
sessionStorage.setItem('glm_nudge_incognito_v1', '1');
setBar('🕶️ 抢不到?试试无痕窗口(Ctrl+Shift+N)!没有历史 Cookie/缓存/Service Worker,可能消除隐形的风控标记。Tampermonkey 需在扩展详情页允许在无痕中启用。', '#722ed1');
}
if (state === 'SLEEPING') {
const rem = sleepUntil - Date.now();
if (rem <= 0) {
@@ -1107,14 +1042,7 @@
return;
}
setBar(`⚠️ 限流 ${taskRLCount}/${MAX_RL},自动关闭后重试...`, '#d46b08');
// v8.21: 高级模式可调限流重试间隔(带 ±20% 抖动防风控)
// 期间用 WAITING_RL 状态占位,避免下一轮 tick 重复关弹窗
var _rld = Number(CFG.RL_RETRY_DELAY) || 1000;
var _rldDelta = _rld * 0.2;
var _rlDelay = Math.max(1, Math.round(_rld + (Math.random() * 2 - 1) * _rldDelta));
taskPhase = 'WAITING_RL';
setTimeout(() => { taskPhase = 'IDLE'; }, _rlDelay);
return;
taskPhase = 'IDLE'; return;
}
if (isPayDialog()) {
const verdict = checkPayDialog();
@@ -1142,9 +1070,9 @@
return;
}
const prices = readDialogPrices();
if (everSucceeded && PS.bizId || prices?.any) {
if (everSucceeded || prices?.any) {
state = 'DONE';
if (everSucceeded && PS.bizId) showPayAlarm();
if (everSucceeded) showPayAlarm();
setBar('💳 <b>支付弹窗已出现!请立即扫码支付!</b> 脚本已停止。', '#16a34a');
} else {
setBar(`🔄 ${TABS_MAP[tab]}·${PKGS_MAP[pkg]} 弹窗等待确认...`, '#1677ff');
@@ -1221,24 +1149,15 @@
'use strict';
if (window.__glmCaptchaPromptBridge === 1) return;
window.__glmCaptchaPromptBridge = 1;
const RUSH_CFG = (() => {
try {
const raw = GM_getValue('glm_coding_config_v5', '{}');
const cfg = JSON.parse(raw || '{}');
const ph = parseInt(cfg.RUSH_TARGET_HOUR, 10); const pm = parseInt(cfg.RUSH_TARGET_MIN, 10); const ps = parseInt(cfg.RUSH_TARGET_SEC, 10);
return {
enabled: cfg.RUSH_ENABLED === true,
targetHour: Number.isFinite(ph) ? ph : 9,
targetMin: Number.isFinite(pm) ? pm : 59,
targetSec: Number.isFinite(ps) ? ps : 58,
staggerMs: 2000,
pollInterval: 50,
pollTimeout: 20000,
};
} catch {
return { enabled: false, targetHour: 9, targetMin: 59, targetSec: 58, staggerMs: 2000, pollInterval: 50, pollTimeout: 20000 };
}
})();
const RUSH_CONFIG = {
enabled: true,
targetHour: 10,
targetMin: 0,
targetSec: 0,
staggerMs: 2000,
pollInterval: 50,
pollTimeout: 20000,
};
const CAPTCHA_CFG = (() => {
try {
const raw = GM_getValue('glm_coding_config_v5', '{}');
@@ -1247,13 +1166,6 @@
return { AUTO_CAPTCHA_CLICK: true, AUTO_CAPTCHA_CONFIRM: false };
}
})();
// v8.21.3: captcha IIFE 没有 CFG 闭包,必须走 CAPTCHA_CFG 拿点击/重试间隔
const _ccd = Number(CAPTCHA_CFG.CAPTCHA_CLICK_DELAY) || 220;
const _cdDelta = _ccd * 0.2;
const _clickDelay = Math.max(1, Math.round(_ccd + (Math.random() * 2 - 1) * _cdDelta));
const _rld = Number(CAPTCHA_CFG.RL_RETRY_DELAY) || 1000;
const _rldDelta = _rld * 0.2;
const _rlDelay = Math.max(1, Math.round(_rld + (Math.random() * 2 - 1) * _rldDelta));
function getWindowIndex() {
const params = new URLSearchParams(location.search);
return parseInt(params.get('wi') || '0', 10);
@@ -1261,8 +1173,8 @@
function getTargetTimestamp() {
const now = new Date();
const target = new Date(now.getFullYear(), now.getMonth(), now.getDate(),
RUSH_CFG.targetHour, RUSH_CFG.targetMin, RUSH_CFG.targetSec, 0);
const offset = getWindowIndex() * RUSH_CFG.staggerMs;
RUSH_CONFIG.targetHour, RUSH_CONFIG.targetMin, RUSH_CONFIG.targetSec, 0);
const offset = getWindowIndex() * RUSH_CONFIG.staggerMs;
return target.getTime() + offset;
}
let lastCaptchaText = '';
@@ -1301,14 +1213,14 @@
return new Promise((resolve, reject) => {
const start = Date.now();
function poll() {
if (Date.now() - start > RUSH_CFG.pollTimeout) {
if (Date.now() - start > RUSH_CONFIG.pollTimeout) {
reject(new Error('poll timeout'));
return;
}
serverRequest('POST', '/result', { ts: ts }).then(d => {
if (d.has_result) resolve(d.result);
else setTimeout(poll, RUSH_CFG.pollInterval);
}).catch(() => setTimeout(poll, RUSH_CFG.pollInterval));
else setTimeout(poll, RUSH_CONFIG.pollInterval);
}).catch(() => setTimeout(poll, RUSH_CONFIG.pollInterval));
}
poll();
});
@@ -1331,6 +1243,8 @@
var selectors = [
'.tencent-captcha-dy__btn-confirm',
'.tencent-captcha-dy__footer .btn',
'.pay-dialog button.el-button--primary',
'.el-dialog__wrapper:not([style*="display: none"]) .el-button--primary',
'[class*="captcha"] [class*="confirm"]',
'[class*="captcha"] [class*="submit"]',
];
@@ -1805,8 +1719,6 @@
if (!result || !result.success || !Array.isArray(result.click_coords)) {
throw new Error('bad direct result: ' + JSON.stringify(resp).substring(0, 180));
}
// v8.21.2: 成功后锁定 30 秒,期间同一张图不再重发(即使 catch 被异常触发)
window.__glmSentLockUntil = Date.now() + 30000;
var rect = bgEl.getBoundingClientRect();
console.log('[captcha-direct-page] click result:', JSON.stringify(result).substring(0, 260));
for (var i = 0; i < result.click_coords.length; i++) {
@@ -1817,40 +1729,24 @@
if (!Number.isFinite(ny) && Number.isFinite(Number(c.rel_y))) ny = Number(c.rel_y) / rect.height;
if (!Number.isFinite(nx) || !Number.isFinite(ny)) continue;
dispatchClickAt(bgEl, nx * rect.width, ny * rect.height, c.char || String(i + 1));
await new Promise(function(r) { setTimeout(r, _clickDelay); });
await new Promise(function(r) { setTimeout(r, 220); });
}
await new Promise(function(r) { setTimeout(r, 350); });
rushState = 'idle';
if (RUSH_CFG.enabled) {
var targetTs = getTargetTimestamp();
if (Date.now() < targetTs) {
console.log('[captcha-rush] waiting ' + ((targetTs - Date.now())/1000).toFixed(1) + 's until target...');
await waitForTargetTime();
console.log('[captcha-rush] target time reached, confirming...');
var clicked = findAndClickConfirm();
if (clicked) { window.__glmRushConfirmed = Date.now(); }
} else {
var clicked = findAndClickConfirm();
if (clicked) { window.__glmRushConfirmed = Date.now(); }
}
} else if (CAPTCHA_CFG.AUTO_CAPTCHA_CONFIRM) {
if (CAPTCHA_CFG.AUTO_CAPTCHA_CONFIRM) {
findAndClickConfirm();
} else {
console.log('[captcha-direct-page] captcha confirm is disabled; waiting for manual confirm');
}
} catch (e) {
console.error('[captcha-direct-page] error:', e);
// v8.21.3: 出错时不再立即重置 captchaSent,避免 50ms tick 死循环。
// 改为加 30s 冷却:期间相同 prompt 不再重发;prompt 变化时 checkCaptchaPrompt 自然会重置。
window.__glmCaptchaCooldownUntil = Date.now() + 30000;
captchaSent = false;
lastCaptchaText = '';
} finally {
rushState = 'idle';
}
}
async function checkCaptchaPrompt() {
if (rushState === 'solving') return;
// v8.21.3: 出错后 30s 冷却,期间不再触发 handleCaptchaDirectInPage
if (window.__glmCaptchaCooldownUntil && Date.now() < window.__glmCaptchaCooldownUntil) return;
var found = findCaptchaPromptElement();
if (!found) { captchaSent = false; return; }
var payloadText = found.chars.join('');
@@ -1867,7 +1763,7 @@
console.log('[captcha] page direct solver:', payloadText);
handleCaptchaDirectInPage(found.chars).catch(function(e) { console.error('[captcha-direct-page] unhandled:', e); });
return;
if (RUSH_CFG.enabled) {
if (RUSH_CONFIG.enabled) {
handleCaptchaRush(found.chars).catch(function(e) { console.error('[captcha-rush] unhandled:', e); });
} else {
serverRequest('POST', '/captcha', { text: payloadText, ts: Date.now() })
@@ -1877,5 +1773,5 @@
}
}
setInterval(checkCaptchaPrompt, 50);
console.log('[captcha] bridge v2 started | rush=' + RUSH_CFG.enabled + ' | wi=' + getWindowIndex() + ' | target=10:' + String(RUSH_CFG.targetSec).padStart(2,'0') + '+' + (getWindowIndex() * RUSH_CFG.staggerMs / 1000) + 's');
console.log('[captcha] bridge v2 started | rush=' + RUSH_CONFIG.enabled + ' | wi=' + getWindowIndex() + ' | target=10:' + String(RUSH_CONFIG.targetSec).padStart(2,'0') + '+' + (getWindowIndex() * RUSH_CONFIG.staggerMs / 1000) + 's');
})();