Greasy Fork is available in English.
优化性能与结构,解除按钮限制,自动分配快捷键映射
// ==UserScript==
// @name 智谱 GLM Coding 快速订阅助手 (优化版)
// @name:en 智谱 GLM Coding Fast Subscription Helper (Optimized)
// @namespace http://tampermonkey.net/
// @version 7.2.0
// @description 优化性能与结构,解除按钮限制,自动分配快捷键映射
// @description:en Optimize performance and structure, remove button restrictions, and automatically allocate shortcut key mappings
// @author Tim
// @match *://www.bigmodel.cn/*
// @match https://www.bigmodel.cn/glm-coding
// @match https://bigmodel.cn/glm-coding*
// @run-at document-start
// @grant none
// @license MIT
// @buy me a coff 邀请链接,邀请码新购,下单立减5%金额 https://www.bigmodel.cn/glm-coding?ic=EAADSLV8VZ
// ==/UserScript==
(function () {
'use strict';
// ==========================================
// 配置与常量
// ==========================================
const CONFIG = {
KEYWORDS: /购买|订阅|立即|下单|选购/,
SELECTORS: 'button, .ant-btn, .btn',
CLOSE_SELECTOR: '.el-dialog__headerbtn', // 弹窗关闭按钮
DEBOUNCE_TIME: 150,
PANEL_ID: 'glm-helper-panel',
TIME_URL: 'https://time.is/zh/Beijing'
};
// ==========================================
// 工具函数
// ==========================================
const Utils = {
debounce(fn, delay) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
},
deepModify(obj) {
if (!obj || typeof obj !== 'object') return;
// 处理当前层级
if ('isSoldOut' in obj) obj.isSoldOut = false;
if ('soldOut' in obj) obj.soldOut = false;
if ('stock' in obj && obj.stock === 0) obj.stock = 999;
if ('disabled' in obj && (obj.price !== undefined || obj.productId || obj.title)) {
obj.disabled = false;
}
// 递归子对象
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key] && typeof obj[key] === 'object') {
this.deepModify(obj[key]);
}
}
},
getPlanInfo(btn) {
const btnText = btn.innerText.trim().replace(/\n/g, ' ');
// 提取动作名:优先保留“订阅中”、“处理中”等状态词
const action = btnText.length > 5 ? btnText.substring(0, 5) : btnText;
let container = btn.parentElement;
let planName = '';
// 向上回溯 4 层寻找卡片容器
for (let i = 0; i < 4; i++) {
if (!container) break;
// 常见的标题识别逻辑:寻找卡片中字相对较少且在顶部的显著文字
const candidates = Array.from(container.querySelectorAll('div, span, h1, h2, p'))
.filter(el => {
const text = el.innerText.trim();
return text.length > 0 &&
text.length < 15 &&
!text.includes('¥') &&
!text.includes(btn.innerText.trim());
});
if (candidates.length > 0) {
// 通常第一个匹配项(在 DOM 顺序上靠前)是标题
planName = candidates[0].innerText.trim();
break;
}
container = container.parentElement;
}
return planName ? `${action}-${planName}` : btn.innerText.trim().substring(0, 8);
},
getSubscriptionPeriod() {
// 扫描常见的激活态类名:active, is-active, selected, checked
const periodElements = Array.from(document.querySelectorAll('div, span, li, button'))
.filter(el => {
const text = el.innerText.trim();
const isPeriod = /包月|包季|包年|按月|按年/.test(text);
if (!isPeriod) return false;
const activeClasses = ['active', 'is-active', 'selected', 'checked', 'current'];
const hasActiveClass = activeClasses.some(cls => el.classList.contains(cls)) ||
el.getAttribute('aria-checked') === 'true' ||
el.getAttribute('aria-selected') === 'true';
return hasActiveClass;
});
return periodElements.length > 0 ? periodElements[0].innerText.trim() : '未识别';
}
};
// ==========================================
// UI 管理器
// ==========================================
const UIManager = {
panel: null,
init() {
if (this.panel) return;
this.panel = document.createElement('div');
this.panel.id = CONFIG.PANEL_ID;
Object.assign(this.panel.style, {
position: 'fixed',
left: '20px',
top: '50%',
transform: 'translateY(-50%)',
backgroundColor: 'rgba(0, 0, 0, 0.85)',
color: '#fff',
padding: '15px',
borderRadius: '12px',
zIndex: '10000',
fontSize: '13px',
lineHeight: '1.6',
pointerEvents: 'auto', // 恢复交互以支持点击链接
boxShadow: '0 8px 24px rgba(0,0,0,0.4)',
border: '1px solid #444',
display: 'none',
backdropFilter: 'blur(4px)',
fontFamily: 'Inter, system-ui, sans-serif'
});
document.documentElement.appendChild(this.panel);
},
update(buttons) {
if (!this.panel) this.init();
this.panel.style.display = 'block'; // 持久显示
const period = Utils.getSubscriptionPeriod();
const header = `
<div style="border-bottom: 1px solid #444; margin-bottom: 10px; padding-bottom: 8px;">
<b style="color: #00ff88; display: block; font-size: 14px;">🚀 快速订阅助手</b>
<span style="color: #00e5ff; font-size: 11px;">[ 当前周期 ] ${period}</span>
</div>
`;
// 订阅按钮快捷键列表
let list = buttons.map((btnObj, index) => {
const label = btnObj.label;
const isProcessing = label.includes('中') || label.includes('载') || label.includes('处理');
const style = isProcessing ? 'color: #00e5ff; font-style: italic; opacity: 0.8;' : 'color: #fff;';
return `<div><span style="color: #ffcc00; font-weight: bold; margin-right: 8px;">[ ${index + 1} ]</span><span style="${style}">${label}</span></div>`;
}).join('');
// 始终显示 Esc 快捷键
const separator = list ? '<div style="border-top: 1px solid #444; margin-top: 8px; padding-top: 4px;"></div>' : '';
list += `${separator}<div><span style="color: #00e5ff; font-weight: bold; margin-right: 8px;">[ Esc ]</span>关闭当前弹窗</div>`;
// 始终显示对时引导
list += `<div style="margin-top: 4px;"><span style="color: #ff8c00; font-weight: bold; margin-right: 8px;">[ Time ]</span><a href="${CONFIG.TIME_URL}" target="_blank" rel="noopener noreferrer" style="color: #bbb; text-decoration: underline; pointer-events: auto; cursor: pointer;">⌚ 精准对时</a></div>`;
this.panel.innerHTML = header + list;
}
};
// ==========================================
// 动作管理器
// ==========================================
const ActionManager = {
buttons: [],
refresh: Utils.debounce(function () {
// 1. 解除原生置灰
document.querySelectorAll('button[disabled], .ant-btn-disabled').forEach(btn => {
btn.removeAttribute('disabled');
btn.classList.remove('ant-btn-disabled');
});
// 2. 扫描有效按钮并提取套餐信息
const rawButtons = Array.from(document.querySelectorAll(CONFIG.SELECTORS));
this.buttons = rawButtons.filter(btn => {
const isVisible = btn.offsetWidth > 0 && btn.offsetHeight > 0;
return isVisible && CONFIG.KEYWORDS.test(btn.innerText);
}).map(btn => ({
element: btn,
label: Utils.getPlanInfo(btn)
}));
// 3. 更新 UI
UIManager.update(this.buttons);
}, CONFIG.DEBOUNCE_TIME),
trigger(index) {
const btnObj = this.buttons[index - 1];
if (btnObj && btnObj.element) {
btnObj.element.click();
console.log(`[助手] 触发快捷键 ${index} ->`, btnObj.label);
}
}
};
// ==========================================
// 拦截器
// ==========================================
const Interceptor = {
init() {
this.patchJSON();
this.patchFetch();
this.patchXHR();
},
patchJSON() {
const originalParse = JSON.parse;
JSON.parse = function (text, reviver) {
const result = originalParse(text, reviver);
try { Utils.deepModify(result); } catch (e) { }
return result;
};
},
patchFetch() {
const originalFetch = window.fetch;
window.fetch = async function (...args) {
const response = await originalFetch.apply(this, args);
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
try {
const clone = response.clone();
let data = await clone.json();
Utils.deepModify(data);
return new Response(JSON.stringify(data), {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
} catch (e) { }
}
return response;
};
},
patchXHR() {
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
this.addEventListener('readystatechange', () => {
if (this.readyState === 4 && this.status === 200) {
try {
const contentType = this.getResponseHeader('content-type') || '';
if (contentType.includes('application/json')) {
let data = JSON.parse(this.responseText);
Utils.deepModify(data);
Object.defineProperty(this, 'responseText', {
get: () => JSON.stringify(data),
configurable: true
});
Object.defineProperty(this, 'response', {
get: () => data,
configurable: true
});
}
} catch (e) { }
}
});
return originalOpen.apply(this, [method, url, ...rest]);
};
}
};
// ==========================================
// 启动初始化
// ==========================================
Interceptor.init();
window.addEventListener('keydown', (e) => {
// 特殊快捷键:Esc 关闭弹窗
if (e.key === 'Escape') {
const closeBtns = Array.from(document.querySelectorAll(CONFIG.CLOSE_SELECTOR));
// 找到最后一个(通常是最新弹出的)且可见的关闭按钮
const targetBtn = closeBtns.reverse().find(btn => btn.offsetWidth > 0 && btn.offsetHeight > 0);
if (targetBtn) {
e.preventDefault();
targetBtn.click();
console.log('[助手] 触发快捷键 Esc -> 已执行关闭操作', targetBtn);
} else {
console.log('[助手] 触发快捷键 Esc -> 未在页面上找到可见的关闭按钮');
}
return;
}
if (!isNaN(e.key) && e.key !== '0' && !e.ctrlKey && !e.altKey && !e.metaKey) {
const activeElement = document.activeElement;
if (activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA' || activeElement.isContentEditable)) {
return;
}
ActionManager.trigger(parseInt(e.key));
}
});
const observer = new MutationObserver(() => ActionManager.refresh());
// 尽早注入浮窗容器
UIManager.init();
// 监听 DOM 加载(增强监听范围,包括属性和字符变化)
const observerConfig = { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['class', 'disabled'] };
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
observer.observe(document.body, observerConfig);
ActionManager.refresh();
});
} else {
observer.observe(document.body, observerConfig);
ActionManager.refresh();
}
console.log('[助手] 优化版已就绪,享受极速订阅体验。');
})();