Greasy Fork is available in English.
智能监控,支持配置多级备选抢购;自动穿透限流弹窗;默认使用拼团折扣码更优惠,介意误用
当前为
// ==UserScript==
// @name GLM Coding Plan抢购助手
// @namespace http://tampermonkey.net/
// @version 8.0
// @description 智能监控,支持配置多级备选抢购;自动穿透限流弹窗;默认使用拼团折扣码更优惠,介意误用
// @author mumumi
// @include https://*bigmodel.cn/glm-coding*
// @include https://*bigmodel.cn/html/rate-limit.html*
// @include https://platform.minimaxi.com/subscribe/token-plan*
// @include https://*volcengine.com*
// @include https://maas.xfyun.cn/packageSubscription*
// @include https://cloud.infini-ai.com*
// @icon https://www.google.com/s2/favicons?sz=64&domain=bigmodel.cn
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @run-at document-start
// @license GNU GPLv3
// ==/UserScript==
(function () {
'use strict';
// ── 最早读配置(document-start 时还没有主流程)──────────────────────────
const _ec = (() => { try { return JSON.parse(GM_getValue('glm_coding_config_v5', '{}')); } catch { return {}; } })();
// ── 限流页立即跳回主页 ────────────────────────────────────────────────────
if (location.href.includes('rate-limit.html')) {
location.replace('https://www.bigmodel.cn/glm-coding?ic=UMYTQHW8I2&closedialog=true');
return;
}
// ── 其他平台优惠检测 ──────────────────────────────────────────────────────
const PLATFORM_CHECKS = [
{ host: 'platform.minimaxi.com', params: ['code'], name: 'MiniMax', code: 'UMYTQHW8I2', url: 'https://platform.minimaxi.com/subscribe/token-plan?code=UMYTQHW8I2&source=link' },
{ host: 'volcengine.com', params: ['invite', 'inviteCode'], name: '字节方舟', code: 'UMYTQHW8I2', url: 'https://volcengine.com/?invite=UMYTQHW8I2' },
{ host: 'maas.xfyun.cn', params: ['inviteCode'], name: '讯飞星辰', code: 'UMYTQHW8I2', url: 'https://maas.xfyun.cn/packageSubscription?inviteCode=UMYTQHW8I2' },
{ host: 'cloud.infini-ai.com', params: ['invite_code'], name: '无问芯穹', code: 'UMYTQHW8I2', url: 'https://cloud.infini-ai.com/login?redirect=/genstudio/invitation&invite_code=UMYTQHW8I2' },
];
const currentPlatform = PLATFORM_CHECKS.find(p => location.hostname.includes(p.host));
if (currentPlatform) {
const urlParams = new URLSearchParams(location.search);
const hasCode = currentPlatform.params.some(param => urlParams.has(param));
if (!hasCode && !sessionStorage.getItem('promo_dismissed_' + currentPlatform.name)) {
setTimeout(() => {
setBar(`⚠️ 未使用 ${currentPlatform.name} 优惠链接`, '#ff9800');
if (confirm(`智能检测到折扣渠道,是否使用优惠推广码 ${currentPlatform.code}?`)) {
location.href = currentPlatform.url;
} else {
sessionStorage.setItem('promo_dismissed_' + currentPlatform.name, '1');
}
}, 1500);
}
return;
}
// ── v8.0: 无条件激活售罄按钮 - JSON.parse 劫持 ──────────────────────────
const _oP = JSON.parse;
JSON.parse = function (t, r) {
const o = _oP(t, r);
try { (function f(x) {
if (!x || typeof x !== 'object') return;
if ('isSoldOut' in x && x.isSoldOut === true) x.isSoldOut = false;
if ('soldOut' in x && x.soldOut === true) x.soldOut = false;
if ('disabled' in x && x.disabled === true && (x.price !== undefined || x.productId || x.title)) x.disabled = false;
if ('stock' in x && x.stock === 0) x.stock = 999;
for (const k in x) f(x[k]);
})(o); } catch {}
return o;
};
// ── 购买状态(fetch 拦截器 ↔ UI 主循环共享)─────────────────────────────
const PS = {
inProgress : false,
result : null, // null | 'success' | 'sold_out'
bizId : null,
payAmount : null,
};
// ── fetch 拦截(/api/biz/pay/preview 和 check)──────────────────────────
const _oF = window.fetch;
// v8.0: 从 Cookie 提取 token 和从页面提取组织/项目信息
function getAuthHeaders() {
const token = document.cookie.match(/bigmodel_token_production=([^;]+)/)?.[1] || '';
const headers = {
'authorization': token,
'content-type': 'application/json;charset=UTF-8',
'accept': 'application/json, text/plain, */*',
'accept-language': 'zh',
'set-language': 'zh'
};
// 尝试从 localStorage 获取组织和项目 ID
try {
const orgId = localStorage.getItem('bigmodel_organization') || 'org-0E486bA654cF4ceBbA31873c4432D407';
const projId = localStorage.getItem('bigmodel_project') || 'proj_D9637f2f1DE74e57980C70E47d1ea61d';
headers['bigmodel-organization'] = orgId;
headers['bigmodel-project'] = projId;
} catch {}
return headers;
}
window.fetch = async function (...a) {
const url = (typeof a[0] === 'string' ? a[0] : a[0]?.url) || '';
// 拦截 preview:只发一次,不重试(验证码只能用一次)
if (url.includes('/api/biz/pay/preview')) {
PS.inProgress = true;
PS.result = null;
PS.bizId = null;
PS.payAmount = null;
// 提取原始 body
const [urlOrReq, init = {}] = a;
let body = init.body;
if (!body && urlOrReq instanceof Request) {
body = await urlOrReq.clone().text();
}
const authHeaders = getAuthHeaders();
try {
const r = await _oF(url, {
method: 'POST',
headers: authHeaders,
body: body,
credentials: 'include'
});
const txt = await r.text();
let d;
try { d = _oP(txt); } catch { d = {}; }
console.log('[GLM v8.0 DEBUG] preview响应:', d);
console.log('[GLM v8.0 DEBUG] soldOut值:', d?.data?.soldOut, '类型:', typeof d?.data?.soldOut);
if (d?.code === 200 && d?.data?.bizId) {
// 成功获取 bizId
PS.result = 'success';
PS.bizId = d.data.bizId;
PS.payAmount = d.data.payAmount;
PS.inProgress = false;
return new Response(txt, { status: 200, headers: { 'Content-Type': 'application/json' } });
} else if (d?.data?.soldOut === true) {
// 售罄 → 标记状态,返回错误响应(阻止前端打开支付弹窗)
PS.result = 'sold_out';
PS.inProgress = false;
return new Response(
JSON.stringify({ code: 500, msg: '商品已售罄', data: null, success: false }),
{ status: 200, headers: { 'Content-Type': 'application/json' } }
);
} else {
// 其他错误(如验证码失败)
PS.result = 'error';
PS.inProgress = false;
return new Response(txt, { status: r.status, headers: { 'Content-Type': 'application/json' } });
}
} catch (e) {
PS.result = 'error';
PS.inProgress = false;
throw e;
}
}
// 拦截 check:如果 bizId 为 null,直接返回失败
if (url.includes('/api/biz/pay/check')) {
if (url.includes('bizId=null') || !PS.bizId) {
return new Response(
'{"code":500,"msg":"无效的 bizId","data":null,"success":false}',
{ status: 200, headers: { 'Content-Type': 'application/json' } }
);
}
}
return _oF.apply(this, a);
};
// XHR 兜底(重定向到上方 fetch 拦截器)
const _xO = XMLHttpRequest.prototype.open;
const _xS = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (m, u, ...r) { this._u = u; this._m = m; return _xO.call(this, m, u, ...r); };
XMLHttpRequest.prototype.send = function (...a) {
if ((this._u || '').includes('/api/biz/pay/preview')) {
const self = this;
window.fetch(this._u, { method: this._m || 'POST', body: a[0], credentials: 'include' }).then(async r => {
const txt = await r.text();
const dp = (k, v) => Object.defineProperty(self, k, { value: v, configurable: true });
dp('readyState', 4); dp('status', 200); dp('statusText', 'OK');
dp('responseText', txt); dp('response', txt);
const ev = new Event('readystatechange');
if (typeof self.onreadystatechange === 'function') self.onreadystatechange(ev);
self.dispatchEvent(ev);
['load', 'loadend'].forEach(t => {
const e = new ProgressEvent(t);
if (typeof self[`on${t}`] === 'function') self[`on${t}`](e);
self.dispatchEvent(e);
});
});
return;
}
return _xS.apply(this, a);
};
// ── 每日套餐状态(localStorage,按日隔离)────────────────────────────────
// -1未知 0进行中(重启复位) 1今日售罄 2今日已购
const _today = new Date().toISOString().slice(0, 10);
const _dsKey = `glm_ds_${_today}`;
let _ds = (() => { try { return JSON.parse(localStorage.getItem(_dsKey) || '{}'); } catch { return {}; } })();
Object.keys(_ds).forEach(k => { if (_ds[k] === 0) _ds[k] = -1; });
_flush();
function _flush() { localStorage.setItem(_dsKey, JSON.stringify(_ds)); }
function getS(t, p) { return _ds[`${t}-${p}`] ?? -1; }
function setS(t, p, v) { _ds[`${t}-${p}`] = v; _flush(); }
if (Object.values(_ds).includes(2)) {
setTimeout(() => setBar('🎉 今日已订阅成功,脚本停止。', '#237804'), 800);
return;
}
// ── 配置 ──────────────────────────────────────────────────────────────────
const STORAGE_KEY = 'glm_coding_config_v5';
const TABS_MAP = { 1: '连续包月', 2: '连续包季', 3: '连续包年' };
const PKGS_MAP = { 1: 'Lite', 2: 'Pro', 3: 'Max' };
const DEF = {
TABS_PRIORITY : '1',
PACKAGES_PRIORITY : '2,3,1',
CHECK_INTERVAL : 80,
SMART_REFRESH : true,
};
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)); }
const CFG = loadCfg();
GM_registerMenuCommand('⚙️ 打开配置面板', openConfigPanel);
GM_registerMenuCommand('🗑️ 清除今日套餐状态缓存', () => { localStorage.removeItem(_dsKey); alert('今日状态已清除,即将刷新。'); location.reload(); });
GM_registerMenuCommand('🚀 一键多开窗口', openMultipleWindows);
// ── v8.0: ESC 键快速关闭弹窗 ──────────────────────────────────────────────
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' || e.keyCode === 27) {
const payDlg = getPayDialog();
if (payDlg) {
closePayDialog();
console.log('[GLM v8.0] ESC 键关闭支付弹窗');
}
}
});
// ── v8.0: 一键多开窗口函数 ────────────────────────────────────────────────
function openMultipleWindows() {
const count = prompt('请输入要打开的窗口数量(建议 2-5 个):', '3');
if (!count) return;
const n = parseInt(count);
if (isNaN(n) || n < 1 || n > 10) { alert('请输入 1-10 之间的数字'); return; }
const baseUrl = 'https://www.bigmodel.cn/glm-coding?ic=UMYTQHW8I2&closedialog=true';
for (let i = 0; i < n; i++) {
setTimeout(() => {
// 使用 '_blank' 强制打开新窗口,添加窗口特性参数
window.open(baseUrl, '_blank', 'width=1200,height=800,left=' + (i * 50) + ',top=' + (i * 50));
}, i * 300);
}
alert(`✅ 已打开 ${n} 个窗口!\n\n💡 建议:\n1. 提前完成验证码(选3个字)\n2. 9:58 准备就绪\n3. 10:00 一到立即点确认`);
}
// ── 扫描队列(过滤今日已确认售罄)────────────────────────────────────────
const tabs = String(CFG.TABS_PRIORITY).split(',').map(Number).filter(Boolean);
const pkgs = String(CFG.PACKAGES_PRIORITY).split(',').map(Number).filter(Boolean);
const scanQueue = tabs.flatMap(t => pkgs.map(p => ({ tab: t, pkg: p }))).filter(({ tab: t, pkg: p }) => getS(t, p) !== 1);
if (!scanQueue.length) {
setTimeout(() => { setBar('📭 今日所有配置套餐均已售罄,脚本停止。', '#434343'); triggerPromo(); }, 800);
return;
}
// ── 状态机变量 ────────────────────────────────────────────────────────────
let state = 'SCANNING'; // SCANNING | TASK_UNIT | SLEEPING | DONE
// SCANNING / TASK_UNIT
let qIdx = 0, sweepRestocks = [], lastTabSwitch = 0;
let taskTarget = null, taskPhase = 'IDLE', taskClickTime = 0, taskRLCount = 0;
let sleepUntil = 0;
const MAX_RL = 3, MODAL_WAIT = 15000;
// ── 工具函数 ──────────────────────────────────────────────────────────────
function parseRestock(text) {
const m = (text || '').match(/0?(\d{1,2})月0?(\d{1,2})日\s*(\d{1,2}):0?(\d{1,2})/);
if (!m) return null;
const t = new Date(new Date().getFullYear(), +m[1] - 1, +m[2], +m[3], +m[4]);
return { dateStr: `${+m[1]}月${+m[2]}日`, msUntil: t - Date.now() };
}
function todayStr() { const d = new Date(); return `${d.getMonth() + 1}月${d.getDate()}日`; }
function fmt(ms) {
if (ms <= 0) return '0秒';
const s = Math.floor(ms / 1000), m = Math.floor(s / 60), h = Math.floor(m / 60);
return h ? `${h}h${m % 60}m` : m ? `${m}分${s % 60}秒` : `${s}秒`;
}
function calcSleepMs(ms) {
if (ms > 3600000) return 240000;
if (ms > 1800000) return 180000;
if (ms > 900000) return 120000;
if (ms > 300000) return 60000;
if (ms > 120000) return 30000;
if (ms > 60000) return 10000;
if (ms > 10000) return 3000;
return 0;
}
function isRealBizId(id) { return id && !id.startsWith('debug-'); }
// ── v8.0: 黄金时间判断(9:50-10:10)──────────────────────────────────────
function isGoldenTime() {
const now = new Date();
const h = now.getHours();
const m = now.getMinutes();
const time = h * 60 + m;
const start = 9 * 60 + 50; // 9:50
const end = 10 * 60 + 10; // 10:10
return time >= start && time <= end;
}
// ── DOM 访问 ──────────────────────────────────────────────────────────────
const tabEl = n => document.querySelector(`#switchTabBox > div > .switch-tab-item:nth-child(${n + 1})`);
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.innerText || '').includes('特惠订阅');
const isSoldOut = b => /售罄|补货|暂时/.test(b?.innerText || '');
// ── 弹窗检测 ──────────────────────────────────────────────────────────────
function findRLModal() {
for (const w of document.querySelectorAll('.el-dialog__wrapper'))
if (getComputedStyle(w).display !== 'none' && (w.innerText || '').includes('当前购买人数较多')) return w;
return null;
}
function getPayDialog() {
const d = document.querySelector('.pay-dialog');
if (!d) return null;
const w = d.closest('.el-dialog__wrapper');
if (!w || getComputedStyle(w).display === 'none') return null;
if ((w.innerText || '').includes('当前购买人数较多')) return null;
return d;
}
function isPayDialog() { return !!getPayDialog(); }
function isSuccessDialog() {
const w = document.querySelector('.pay-success-dialog-box')?.closest('.el-dialog__wrapper');
return w ? getComputedStyle(w).display !== 'none' : false;
}
function closeModal(w) { w?.querySelector('.el-dialog__close')?.click(); }
function closePayDialog() {
const d = getPayDialog();
if (d) closeModal(d.closest('.el-dialog__wrapper'));
}
// ── 对话框实付金额读取(双通道)──────────────────────────────────────────
// 通道A:扫码区 .info-price 最后一个 <span>(纯数字,如"149")
// 通道B:计算明细区"实付金额"对应的 .price-item(含¥,如"¥149")
//
// 两通道任一 > 0 即视为有效。
function readDialogPrices() {
const dlg = getPayDialog();
if (!dlg) return null;
// 通道A
let scanPrice = 0;
const infoPrice = dlg.querySelector('.info-price');
if (infoPrice) {
const spans = infoPrice.querySelectorAll('span');
// price-icon 是 ¥,后面的 span 是数值
for (let i = spans.length - 1; i >= 0; i--) {
const v = parseFloat(spans[i].textContent.trim());
if (!isNaN(v) && v > 0) { scanPrice = v; break; }
}
}
// 通道B
let actualPrice = 0;
dlg.querySelectorAll('.calculate-content-item').forEach(li => {
if ([...li.querySelectorAll('div')].some(d => d.textContent.includes('实付金额'))) {
const v = parseFloat((li.querySelector('.price-item')?.textContent || '').replace(/[¥,]/g, '').trim());
if (!isNaN(v) && v > 0) actualPrice = v;
}
});
return { scanPrice, actualPrice, any: scanPrice > 0 || actualPrice > 0 };
}
// ── 底部状态栏 ────────────────────────────────────────────────────────────
let _bar = null;
function setBar(html, bg = '#1677ff') {
if (!_bar) {
_bar = document.createElement('div');
_bar.style.cssText = 'position:fixed;bottom:0;left:0;right:0;z-index:2147483647;padding:7px 16px;font:13px/1.5 system-ui,sans-serif;color:#fff;display:flex;align-items:center;justify-content:space-between;box-shadow:0 -2px 8px rgba(0,0,0,.25);transition:background .4s';
const x = document.createElement('button');
x.textContent = '×';
x.style.cssText = 'background:rgba(255,255,255,.2);border:none;color:#fff;width:22px;height:22px;border-radius:4px;cursor:pointer;font-size:16px;line-height:1;flex-shrink:0';
x.onclick = () => { _bar.remove(); _bar = null; };
_bar.append(document.createElement('span'), x);
document.body.appendChild(_bar);
}
_bar.style.background = bg;
_bar.firstElementChild.innerHTML = `🤖 <b>抢购助手</b> | ${html}`;
}
// ── 支付报警:视口边框红色闪烁 ────────────────────────────────────────────
let _alarm = null;
function showPayAlarm() {
if (_alarm) return;
if (!document.getElementById('glm-alarm-s')) {
const s = document.createElement('style'); s.id = 'glm-alarm-s';
s.textContent = '@keyframes glm-al{0%,100%{box-shadow:inset 0 0 0 12px rgba(220,38,38,.92)}50%{box-shadow:inset 0 0 0 12px rgba(220,38,38,.08)}}';
document.head.appendChild(s);
}
_alarm = document.createElement('div');
_alarm.style.cssText = 'position:fixed;inset:0;pointer-events:none;z-index:2147483646;animation:glm-al .5s steps(1) infinite';
const lbl = document.createElement('div');
lbl.style.cssText = 'position:absolute;top:12px;left:50%;transform:translateX(-50%);background:rgba(220,38,38,.95);color:#fff;padding:5px 22px;border-radius:20px;font:700 15px system-ui,sans-serif;white-space:nowrap;letter-spacing:.5px';
lbl.textContent = '⚠️ 请立即扫码支付!';
_alarm.appendChild(lbl);
document.body.appendChild(_alarm);
}
// ── 推广弹窗 ──────────────────────────────────────────────────────────────
function triggerPromo() {
if (Date.now() > new Date('2026-04-30T23:59:59').getTime()) { setBar('所有套餐今日售罄,脚本停止。', '#434343'); return; }
const PP = [
{ name: 'MiniMax', desc: '¥29起,赠视频/语音/音乐额度', color: '#4CAF50', url: 'https://platform.minimaxi.com/subscribe/token-plan?code=FoGlERWIF3&source=link' },
{ name: '字节·方舟', desc: '首月低至¥8.9,多模型切换', color: '#FF6B35', url: 'https://volcengine.com/L/YIeVPueJ2O4/' },
{ name: '讯飞星辰', desc: '¥19起,首月¥3.9最低门槛', color: '#00BFFF', url: 'https://maas.xfyun.cn/packageSubscription?inviteCode=MAAS-C6BE3A3B' },
{ name: '无问芯穹', desc: '多模型聚合,首月¥19.9', color: '#7B68EE', url: 'https://cloud.infini-ai.com/login?redirect=/genstudio/invitation&invite_code=IyveoKRS' },
];
const rows = PP.map(p => `
<a href="${p.url}" target="_blank" style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-radius:8px;text-decoration:none;border:1px solid #eee;background:#fafafa" onmouseover="this.style.background='#f0f7ff'" onmouseout="this.style.background='#fafafa'">
<div style="display:flex;align-items:center;gap:10px">
<span style="width:8px;height:8px;border-radius:50%;background:${p.color};display:inline-block"></span>
<span style="font-weight:600;font-size:14px;color:#111">${p.name}</span>
<span style="font-size:12px;color:#888">${p.desc}</span>
</div>
<span style="font-size:12px;color:${p.color};font-weight:500">立即开通 →</span>
</a>`).join('');
const ov = document.createElement('div');
ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:2147483645;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(5px);font-family:system-ui,sans-serif';
ov.innerHTML = `
<div style="background:#fff;width:520px;border-radius:16px;overflow:hidden;box-shadow:0 25px 50px -12px rgba(0,0,0,.5);max-height:90vh;display:flex;flex-direction:column">
<div style="background:linear-gradient(135deg,#1e3c72,#2a5298);padding:24px 24px 20px;color:#fff">
<h2 style="margin:0 0 6px;font-size:20px">GLM Coding Plan 全部售罄 🫠</h2>
<p style="margin:0;opacity:.85;font-size:14px">配置的所有套餐今日已售罄,补货后脚本将继续监控</p>
</div>
<div style="padding:16px 20px;overflow-y:auto;flex:1">
<div style="font-size:13px;color:#888;margin-bottom:12px">👇 以下平台也有编程套餐和折扣链接</div>
<div style="display:flex;flex-direction:column;gap:8px">${rows}</div>
</div>
<div style="padding:14px 20px;border-top:1px solid #f0f0f0;text-align:right">
<button id="promo-x" style="background:none;border:1px solid #ddd;color:#888;padding:7px 18px;border-radius:6px;cursor:pointer;font-size:13px">关闭并停止脚本</button>
</div>
</div>`;
document.body.appendChild(ov);
ov.querySelector('#promo-x').onclick = () => ov.remove();
ov.onclick = e => { if (e.target === ov) ov.remove(); };
}
// ── 配置面板 ──────────────────────────────────────────────────────────────
function buildTransferBox(ct, dataMap, selectedStr, title) {
const sel = selectedStr.split(',').filter(Boolean);
const avail = Object.keys(dataMap).filter(k => !sel.includes(k));
ct.innerHTML = `
<div style="font-size:13px;font-weight:bold;margin-bottom:8px;color:#444">${title}</div>
<div style="display:flex;align-items:stretch;gap:10px;margin-bottom:20px;height:140px">
<div style="flex:1;border:1px solid #ddd;border-radius:6px;display:flex;flex-direction:column;background:#fafafa">
<div style="padding:6px 10px;border-bottom:1px solid #ddd;font-size:12px;color:#666;background:#f0f0f0;border-radius:6px 6px 0 0">备选池</div>
<ul class="tf-left" style="list-style:none;padding:5px;margin:0;flex:1;overflow-y:auto">
${avail.map(k => `<li data-val="${k}" class="tf-item">${dataMap[k]}</li>`).join('')}
</ul>
</div>
<div style="display:flex;flex-direction:column;justify-content:center;gap:8px">
<button type="button" class="tf-btn tf-r">▶</button>
<button type="button" class="tf-btn tf-l">◀</button>
</div>
<div style="flex:1;border:1px solid #ddd;border-radius:6px;display:flex;flex-direction:column;background:#fff">
<div style="padding:6px 10px;border-bottom:1px solid #ddd;font-size:12px;color:#666;background:#e6f7ff;border-radius:6px 6px 0 0">选中且排序(自上而下)</div>
<ul class="tf-right" style="list-style:none;padding:5px;margin:0;flex:1;overflow-y:auto">
${sel.map(k => `<li data-val="${k}" class="tf-item">${dataMap[k]}</li>`).join('')}
</ul>
</div>
<div style="display:flex;flex-direction:column;justify-content:center;gap:8px">
<button type="button" class="tf-btn tf-up">▲</button>
<button type="button" class="tf-btn tf-dn">▼</button>
</div>
</div>`;
const L = ct.querySelector('.tf-left'), R = ct.querySelector('.tf-right');
ct.querySelectorAll('ul').forEach(ul => ul.addEventListener('click', e => {
if (e.target.tagName === 'LI') { ct.querySelectorAll('.tf-item').forEach(i => i.classList.remove('active')); e.target.classList.add('active'); }
}));
ct.querySelector('.tf-r').onclick = () => { const a = L.querySelector('.active'); if (a) { R.appendChild(a); a.classList.remove('active'); } };
ct.querySelector('.tf-l').onclick = () => { const a = R.querySelector('.active'); if (a) { L.appendChild(a); a.classList.remove('active'); } };
ct.querySelector('.tf-up').onclick = () => { const a = R.querySelector('.active'); if (a?.previousElementSibling) R.insertBefore(a, a.previousElementSibling); };
ct.querySelector('.tf-dn').onclick = () => { const a = R.querySelector('.active'); if (a?.nextElementSibling) R.insertBefore(a.nextElementSibling, a); };
return () => [...R.querySelectorAll('.tf-item')].map(i => i.dataset.val).join(',');
}
function openConfigPanel() {
document.getElementById('glm-cfg-ov')?.remove();
if (!document.getElementById('glm-tf-s')) {
const s = document.createElement('style'); s.id = 'glm-tf-s';
s.textContent = '.tf-item{padding:6px 10px;margin-bottom:4px;border-radius:4px;cursor:pointer;font-size:13px;color:#333;border:1px solid transparent;transition:all .15s}.tf-item:hover{background:#f5f5f5}.tf-item.active{background:#e6f7ff;border-color:#91d5ff;color:#1890ff;font-weight:700}.tf-btn{padding:4px 8px;font-size:10px;cursor:pointer;border:1px solid #d9d9d9;border-radius:4px;background:#fff;color:#555;height:28px;transition:.2s}.tf-btn:hover{border-color:#40a9ff;color:#40a9ff}';
document.head.appendChild(s);
}
const ov = document.createElement('div'); ov.id = 'glm-cfg-ov';
ov.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:2147483646;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(2px);font-family:system-ui,sans-serif';
const panel = document.createElement('div');
panel.style.cssText = 'background:#fff;color:#333;width:560px;padding:24px;border-radius:12px;box-shadow:0 20px 60px rgba(0,0,0,.3);max-height:90vh;overflow-y:auto';
panel.innerHTML = `
<h3 style="margin:0 0 20px;font-size:18px;color:#1a1a1a">⚙️ 抢购助手配置</h3>
<div id="glm-wp"></div>
<div id="glm-wt"></div>
<div style="margin-bottom:20px;padding-top:10px;border-top:1px dashed #eee;display:flex;flex-direction:column;gap:12px">
<label style="display:flex;align-items:center;cursor:pointer">
<input type="checkbox" id="glm-sm" ${CFG.SMART_REFRESH ? 'checked' : ''} style="margin-right:8px">
<span style="font-size:14px;color:#555">启用智能刷新(梯度嗅探补货时间)</span>
</label>
</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">
<button id="glm-cc" style="padding:8px 16px;border:1px solid #ddd;background:#f5f5f5;border-radius:6px;cursor:pointer;color:#666">取消</button>
<button id="glm-cs" style="padding:8px 20px;border:none;background:#1890ff;color:#fff;border-radius:6px;cursor:pointer;font-weight:700">保存并刷新</button>
</div>
</div>`;
ov.appendChild(panel);
document.body.appendChild(ov);
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-multi').onclick = () => { openMultipleWindows(); };
panel.querySelector('#glm-cs').onclick = () => {
const p = getPkgs(), t = getTabs();
if (!p || !t) { alert('请至少各选一个!'); return; }
saveCfg({
TABS_PRIORITY : t,
PACKAGES_PRIORITY : p,
SMART_REFRESH : panel.querySelector('#glm-sm').checked,
CHECK_INTERVAL : CFG.CHECK_INTERVAL,
});
ov.remove(); alert('已保存,即将刷新。'); location.reload();
};
ov.onclick = e => { if (e.target === ov) ov.remove(); };
}
// ═══════════════════════════════════════════════════════════════════════════
// 主循环
// ═══════════════════════════════════════════════════════════════════════════
function tick() {
if (state === 'DONE') return;
if (state === 'SLEEPING') {
const rem = sleepUntil - Date.now();
if (rem <= 0) location.replace('https://www.bigmodel.cn/glm-coding?ic=UMYTQHW8I2&closedialog=true');
else setBar(`💤 休眠中,<b>${fmt(rem)}</b> 后刷新`, '#434343');
return;
}
if (state === 'TASK_UNIT') { doTaskUnit(); return; }
doScan();
}
// ═══════════════════════════════════════════════════════════════════════════
// SCANNING / TASK_UNIT 逻辑
// ═══════════════════════════════════════════════════════════════════════════
function doScan() {
if (qIdx >= scanQueue.length) { onSweepDone(); return; }
const { tab, pkg } = scanQueue[qIdx];
const te = tabEl(tab);
if (!te) return;
if (!te.classList.contains('active')) {
te.click(); te.scrollIntoView({ behavior: 'auto', block: 'center' });
lastTabSwitch = Date.now(); setBar(`🔄 切换到 ${TABS_MAP[tab]}...`); return;
}
if (Date.now() - lastTabSwitch < 400) return;
const b = btnEl(pkg);
if (canBuy(b)) {
taskTarget = { tab, pkg }; taskPhase = 'IDLE'; taskRLCount = 0;
setS(tab, pkg, 0); state = 'TASK_UNIT';
setBar(`🎯 发现可购!${TABS_MAP[tab]} · ${PKGS_MAP[pkg]},即将点击...`, '#389e0d');
return;
}
const ri = parseRestock(b?.innerText);
if (ri?.dateStr === todayStr() && ri.msUntil > 0) sweepRestocks.push(ri);
setBar(`🔍 扫描 ${TABS_MAP[tab]} · ${PKGS_MAP[pkg]} (${qIdx + 1}/${scanQueue.length})`);
qIdx++;
}
function onSweepDone() {
if (!sweepRestocks.length) {
state = 'DONE'; setBar('📭 今日全部售罄,脚本停止。', '#434343'); triggerPromo(); return;
}
sweepRestocks.sort((a, b) => a.msUntil - b.msUntil);
const nearest = sweepRestocks[0];
const sleep = calcSleepMs(nearest.msUntil);
// ── v8.0: 黄金时间(9:50-10:10)禁止刷新页面 ──────────────────────────
if (isGoldenTime()) {
setBar(`🔥 黄金时间!补货倒计时 <b>${fmt(nearest.msUntil)}</b>,禁止刷新,高频监控!`, '#ff4d4f');
qIdx = 0; sweepRestocks = []; return;
}
if (sleep === 0) {
setBar(`⚡ 补货倒计时 <b>${fmt(nearest.msUntil)}</b>,高频监控!`, '#d4380d');
qIdx = 0; sweepRestocks = []; return;
}
if (CFG.SMART_REFRESH) {
state = 'SLEEPING'; sleepUntil = Date.now() + sleep;
setBar(`💤 补货还需 <b>${fmt(nearest.msUntil)}</b>,<b>${fmt(sleep)}</b> 后刷新`, '#434343');
} else { qIdx = 0; sweepRestocks = []; }
}
function doTaskUnit() {
const { tab, pkg } = taskTarget;
const te = tabEl(tab);
if (!te) return;
if (!te.classList.contains('active')) { te.click(); return; }
const b = btnEl(pkg);
if (taskPhase === 'IDLE') {
if (isSoldOut(b)) { exitTask(); return; }
if (!canBuy(b)) {
setBar(`⏳ 等待按钮就绪... ${TABS_MAP[tab]} · ${PKGS_MAP[pkg]}`, '#d46b08');
return;
}
PS.result = null; PS.inProgress = true;
b.click(); taskClickTime = Date.now(); taskPhase = 'WAITING';
setBar(`🔄 已点击,接口重试中... ${TABS_MAP[tab]} · ${PKGS_MAP[pkg]}(限流 ${taskRLCount}/${MAX_RL})`, '#d46b08');
return;
}
if (taskPhase === 'WAITING') {
const rlw = findRLModal();
if (rlw) {
closeModal(rlw); taskRLCount++;
if (taskRLCount >= MAX_RL) {
setBar(`🔁 连续 ${MAX_RL} 次限流,即将刷新...`, '#cf1322');
setTimeout(() => location.replace('https://www.bigmodel.cn/glm-coding?ic=UMYTQHW8I2&closedialog=true'), 50);
return;
}
setBar(`⚠️ 限流 ${taskRLCount}/${MAX_RL},重试中...`, '#d46b08');
taskPhase = 'IDLE'; return;
}
if (isPayDialog()) {
// v8.0: 检测异常支付弹窗(soldOut 但弹出了支付界面,bizId 为 null)
if (!PS.inProgress && PS.result === 'sold_out' && !isRealBizId(PS.bizId)) {
console.log('[GLM v8.0] 检测到异常支付弹窗(soldOut + bizId=null),自动关闭并重试');
closePayDialog();
taskPhase = 'IDLE';
return;
}
const prices = readDialogPrices();
const hasApiAmount = PS.payAmount && PS.payAmount > 0 && isRealBizId(PS.bizId);
const waited3s = Date.now() - taskClickTime > 3000;
if (prices?.any) {
// DOM 有实付金额 → 成功
state = 'DONE'; showPayAlarm();
setBar(`💳 <b>抢购成功!实付 ¥${prices.actualPrice || prices.scanPrice},请立即扫码支付!</b>`, '#16a34a');
return;
}
if (hasApiAmount || waited3s) {
// 接口金额有效,等 DOM 渲染
showPayAlarm();
setBar('💳 <b>抢购成功!请立即扫码支付!</b> 脚本已停止。', '#16a34a');
state = 'DONE';
return;
}
// 弹窗刚出现,等价格
return;
}
if (isSuccessDialog()) {
setS(tab, pkg, 2); state = 'DONE';
setBar('🎉 订阅成功!恭喜!', '#237804'); return;
}
if (!PS.inProgress && PS.result === 'sold_out' && Date.now() - taskClickTime > 2000) {
exitTask(); return;
}
if (Date.now() - taskClickTime > MODAL_WAIT) {
if (isSoldOut(b)) exitTask(); else taskPhase = 'IDLE';
}
}
}
function exitTask() {
// v8.0: 黄金时间内不标记售罄,持续重试
if (!isGoldenTime()) {
setS(taskTarget.tab, taskTarget.pkg, 1);
}
setBar(`📦 ${TABS_MAP[taskTarget.tab]} · ${PKGS_MAP[taskTarget.pkg]} 售罄,继续...`);
qIdx++; taskTarget = null; taskPhase = 'IDLE'; taskRLCount = 0;
state = 'SCANNING';
}
// ── 启动 ──────────────────────────────────────────────────────────────────
setInterval(tick, CFG.CHECK_INTERVAL);
})();