diff --git a/CHANGELOG.md b/CHANGELOG.md index f89ffc9..2b2c2e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 2026-06-21 +- 发布用户脚本 v23.0:新增独立启动状态条,主页面一加载就显示准备态、`F9`/`F8` 操作提示,并异步检查 `127.0.0.1:8888/health`,显示本地 OCR 后端已连接或未连接;状态条提前到扫描主流程之前创建,避免页面看不到任何提示。 - 发布用户脚本 v22.9:新增明确的准备态状态栏。脚本加载后会提示“准备中:默认不主动点击订阅,按 F9 开启,或等待 Rush 目标时间”;发现可购时也不再显示“即将点击”,避免误导用户以为会提前进入购买链路。 - 发布用户脚本 v22.8:自动点击订阅改为默认关闭,并迁移旧配置为关闭;脚本加载后只观察和提醒,不会主动进入购买链路。只有用户显式开启自动点击(配置面板或 `F9`)或 Rush mode 目标时间已到,才会自动点击订阅。 - 发布用户脚本 v22.7:快捷键默认从 `Alt+Shift+P/A` 改为外挂工具更常见且更少冲突的 `F8/F9`,并在配置面板新增快捷键自定义和说明。 diff --git a/glm-coding-helper.user.js b/glm-coding-helper.user.js index 4e03c9d..3f80f0f 100644 --- a/glm-coding-helper.user.js +++ b/glm-coding-helper.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name 智谱 GLM Coding Plan 抢购助手 + 本地 OCR 自动验证码 // @namespace http://tampermonkey.net/ -// @version 22.9 +// @version 23.0 // @description GLM Coding Rush / 智谱 GLM Coding Plan 抢购助手,一键抢购油猴脚本 / Tampermonkey userscript,配合本地 CPU/GPU OCR 自动识别中文点选验证码并点击,支持多窗口并发、限流重试和支付页安全保护 // @author mumumi // @include https://*bigmodel.cn/glm-coding* @@ -32,6 +32,8 @@ // ==/UserScript== (function () { 'use strict'; + const SCRIPT_VERSION = '23.0'; + const BOOT_BAR_ID = 'glm-helper-status-bar'; const __glmHost = (() => { try { return location.hostname || ''; } catch { return ''; } })(); const __inMiniMax = __glmHost === 'platform.minimaxi.com'; if (__inMiniMax) { @@ -267,8 +269,85 @@ setInterval(tick, 1200); } // ── 去重保护:防止篡猴里装了改名导致的两个实例同时运行 ────────────────── - if (document.documentElement.dataset.glmHelper === '1') { return; } + function ensureStatusBarNode() { + let bar = document.getElementById(BOOT_BAR_ID); + if (bar) return bar; + const parent = document.body || document.documentElement; + if (!parent) return null; + bar = document.createElement('div'); + bar.id = BOOT_BAR_ID; + 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 text = document.createElement('span'); + 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.append(text, x); + parent.appendChild(bar); + return bar; + } + function setBootBar(html, bg = '#1677ff') { + const run = () => { + const bar = ensureStatusBarNode(); + if (!bar) return; + bar.style.background = bg; + bar.firstElementChild.innerHTML = `🤖 抢购助手 v${SCRIPT_VERSION}  |  ${html}`; + }; + if (document.body || document.documentElement) run(); + else document.addEventListener('DOMContentLoaded', run, { once: true }); + } + function requestBackendHealth(timeoutMs = 2500) { + return new Promise((resolve, reject) => { + const url = 'http://127.0.0.1:8888/health?from=glm_helper_' + Date.now(); + let done = false; + const timer = setTimeout(() => { + if (done) return; + done = true; + reject(new Error('timeout')); + }, timeoutMs); + function finish(ok, value) { + if (done) return; + done = true; + clearTimeout(timer); + ok ? resolve(value) : reject(value); + } + if (typeof GM_xmlhttpRequest === 'function') { + GM_xmlhttpRequest({ + method: 'GET', + url, + timeout: timeoutMs, + onload: (res) => { + try { finish(true, JSON.parse(res.responseText || '{}')); } + catch (e) { finish(false, e); } + }, + onerror: () => finish(false, new Error('request failed')), + ontimeout: () => finish(false, new Error('timeout')), + }); + return; + } + fetch(url, { cache: 'no-store' }) + .then(r => r.json()) + .then(data => finish(true, data)) + .catch(e => finish(false, e)); + }); + } + function bootBackendProbe() { + setBootBar('准备中:默认不主动点击订阅;按 F9 开启自动点击,或等待 Rush 目标时间。正在检查本地 OCR 后端...', '#1677ff'); + requestBackendHealth().then((h) => { + const ready = `${h.ready_workers ?? '?'} / ${h.workers ?? '?'}`; + const status = h.status || 'unknown'; + const color = status === 'ok' ? '#237804' : '#d46b08'; + setBootBar(`已连接本地 OCR 后端:${status},worker ${ready},端口 ${h.port || 8888}。默认不自动点击订阅;按 F9 开启,按 F8 暂停。`, color); + }).catch(() => { + setBootBar('未连接本地 OCR 后端:请先启动后端 127.0.0.1:8888。脚本仍会准备页面,但验证码识别不可用;默认不自动点击订阅,按 F9 可手动开启。', '#d46b08'); + }); + } + if (document.documentElement.dataset.glmHelper === '1') { + setBootBar('检测到页面上已有一个抢购助手实例,本实例未重复启动。若没有状态提示,请在篡改猴里只保留最新版脚本。', '#d46b08'); + return; + } document.documentElement.dataset.glmHelper = '1'; + bootBackendProbe(); // ── 最早读配置(document-start 时还没有主流程)────────────────────────── const EARLY_STORAGE_KEY = 'glm_coding_config_v5'; const SAFE_DEFAULTS_VERSION = 4; @@ -925,14 +1004,8 @@ var _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 = ensureStatusBarNode(); + if (!_bar) return; } _bar.style.background = bg; _bar.firstElementChild.innerHTML = `🤖 抢购助手  |  ${html}`;