Greasy Fork

来自缓存

Greasy Fork is available in English.

智谱 GLM Coding 特惠订购抢购助手

用于在前端代码中去除按钮的disabled属性,使其在界面上显示为可点击状态。这仅影响前端表现,不改变后端逻辑(脚本只是辅助,重点是教程)。新增 DOM 渲染置灰。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         智谱 GLM Coding 特惠订购抢购助手
// @name:en      智谱 GLM Coding 特惠订购抢购助手
// @namespace    http://tampermonkey.net/
// @version      6.5.2
// @description  用于在前端代码中去除按钮的disabled属性,使其在界面上显示为可点击状态。这仅影响前端表现,不改变后端逻辑(脚本只是辅助,重点是教程)。新增 DOM 渲染置灰。
// @description:en  用于在前端代码中去除按钮的disabled属性,使其在界面上显示为可点击状态。这仅影响前端表现,不改变后端逻辑(脚本只是辅助,重点是教程)。modifying the front-end code to remove the `disabled` attribute from the purchase button(the script is merely auxiliary; the focus is on the tutorial)
// @author       YourName
// @match        *://www.bigmodel.cn/*
// @match        *://www.bigmodel.cn/glm-coding
// @match        *://bigmodel.cn/glm-coding*
// @match        *://*.bigmodel.cn/glm-coding*
// @run-at       document-start
// @grant        none
// @buy me a coff   邀请链接,邀请码新购,下单立减5%金额 https://www.bigmodel.cn/glm-coding?ic=ISKOMMVBRB
// @license MIT
// ==/UserScript==

(function () {
    'use strict';
    console.log('[抢购助手2.0] 🚀 网络拦截器已在页面最早期启动...');

    // ==========================================
    // 战术一:拦截 SSR 页面初始注入数据与内部方法解析
    // 通过劫持浏览器的 JSON 解析器,任何带有"售罄"属性的对象强制改为"有货"
    // ==========================================
    const originalJSONParse = JSON.parse;
    JSON.parse = function (text, reviver) {
        let result = originalJSONParse(text, reviver);

        // 递归遍历所有解析出的对象属性
        function deepModify(obj) {
            if (!obj || typeof obj !== 'object') return;

            // 篡改核心售罄标识
            if (obj.isSoldOut === true) obj.isSoldOut = false;
            if (obj.soldOut === true) obj.soldOut = false;
            // 如果遇到 disabled,且该对象看起来是个商品(包含 price/id 等),则强制启用
            if (obj.disabled === true && (obj.price !== undefined || obj.productId || obj.title)) {
                obj.disabled = false;
            }
            // 有些系统会下发库存数量,顺手给它改大
            if (obj.stock === 0) obj.stock = 999;

            for (let key in obj) {
                if (obj[key] && typeof obj[key] === 'object') {
                    deepModify(obj[key]);
                }
            }
        }

        try { deepModify(result); } catch (e) { }
        return result;
    };

    // ==========================================
    // 战术二:拦截 Fetch 接口请求
    // 针对用户在页面停留时,前端向后端发起的存量/价格二次检查
    // ==========================================
    const originalFetch = window.fetch;
    window.fetch = async function (...args) {
        const response = await originalFetch.apply(this, args);
        // 我们只处理 JSON 接口
        const contentType = response.headers.get('content-type') || '';
        if (contentType.includes('application/json')) {
            const clone = response.clone();
            try {
                let text = await clone.text();
                // 粗暴地全局替换响应体文字中的售罄状态
                if (text.includes('"isSoldOut":true') || text.includes('"disabled":true') || text.includes('"soldOut":true')) {
                    console.log('[抢购助手] 拦截到 Fetch 售罄数据,正在执行篡改!', args[0]);
                    text = text.replace(/"isSoldOut":true/g, '"isSoldOut":false')
                        .replace(/"disabled":true/g, '"disabled":false')
                        .replace(/"soldOut":true/g, '"soldOut":false')
                        .replace(/"stock":0/g, '"stock":999');
                    // 构造并返回一份假的响应给 Vue
                    return new Response(text, {
                        status: response.status,
                        statusText: response.statusText,
                        headers: response.headers
                    });
                }
            } catch (e) { }
        }
        return response;
    };

    // ==========================================
    // 战术三:拦截老式的 XMLHttpRequest (兜底)
    // ==========================================
    const originalXHROpen = XMLHttpRequest.prototype.open;
    const originalXHRSend = XMLHttpRequest.prototype.send;

    XMLHttpRequest.prototype.open = function (method, url, ...rest) {
        this._reqUrl = url;
        return originalXHROpen.call(this, method, url, ...rest);
    };

    XMLHttpRequest.prototype.send = function (...args) {
        this.addEventListener('readystatechange', function () {
            if (this.readyState === 4 && this.status === 200) {
                const contentType = this.getResponseHeader('content-type') || '';
                if (contentType.includes('application/json')) {
                    try {
                        let text = this.responseText;
                        if (text.includes('"isSoldOut":true') || text.includes('"disabled":true') || text.includes('"soldOut":true')) {
                            console.log('[抢购助手] 拦截到 XHR 售罄数据,正在执行篡改!', this._reqUrl);
                            text = text.replace(/"isSoldOut":true/g, '"isSoldOut":false')
                                .replace(/"disabled":true/g, '"disabled":false')
                                .replace(/"soldOut":true/g, '"soldOut":false');

                            // 用劫持 getter 的方式修改 this.responseText 给框架层消化
                            Object.defineProperty(this, 'responseText', { get: function () { return text; } });
                            Object.defineProperty(this, 'response', { get: function () { return JSON.parse(text); } });
                        }
                    } catch (e) { }
                }
            }
        });
        originalXHRSend.apply(this, args);
    };

    // ==========================================
    // 战术四:前端 DOM  (MutationObserver)
    // 针对各种极端情况(如网络限流导致Vue重新渲染按钮为置灰状态)
    // ==========================================
    function startDOMObserver() {
        if (!document.body) {
            setTimeout(startDOMObserver, 100);
            return;
        }

        const observer = new MutationObserver((mutations) => {
            // 匹配各类按钮样式,特别是 Arco Design / Element UI 等常见库
            const buttons = document.querySelectorAll('button, [role="button"], .btn, .arco-btn');
            buttons.forEach(btn => {
                // 1. 移除 disabled 属性
                if (btn.disabled || btn.hasAttribute('disabled')) {
                    btn.disabled = false;
                    btn.removeAttribute('disabled');
                }

                // 2. 移除导致置灰和不可点击的 CSS 类名
                const classList = Array.from(btn.classList);
                const disabledClasses = classList.filter(c => c.toLowerCase().includes('disabled'));
                if (disabledClasses.length > 0) {
                    disabledClasses.forEach(c => btn.classList.remove(c));
                }

                // 3. 强制解除 pointer-events 限制
                if (btn.style.pointerEvents === 'none') {
                    btn.style.setProperty('pointer-events', 'auto', 'important');
                }
            });
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['disabled', 'class', 'style']
        });
        console.log('[抢购助手] DOM 观察器已启动,持续检测按钮置灰状态...');
    }

    // 页面解析完成后尽早启动 DOM 观察器
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', startDOMObserver);
    } else {
        startDOMObserver();
    }

})();