Greasy Fork

Greasy Fork is available in English.

复制限制解除器

解除网页复制限制,支持多种反复制技术

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         复制限制解除器
// @namespace    https://chat.deepseek.com/a/chat/s/762d1db8-7364-4668-a56e-2549eb5c8662?rev=552
// @version      1.1.0
// @description  解除网页复制限制,支持多种反复制技术
// @author       DeepSeek
// @match        *://*/*
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_addStyle
// @license      GPL-3.0
// ==/UserScript==

(function() {
    'use strict';

    // ==================== 配置 ====================
    const CONFIG = {
        debug: false,
        aggressiveMode: false, // 激进模式,hook更多API
        removeCopyListeners: true,
        restoreSelection: true,
        forceUserSelect: true,
        replaceNativeCopy: false,
    };

    // ==================== 工具函数 ====================
    function log(...args) {
        if (CONFIG.debug) {
            console.log('[ForceCopy]', ...args);
        }
    }

    function warn(...args) {
        console.warn('[ForceCopy]', ...args);
    }

    // ==================== CSS注入 ====================
    if (CONFIG.forceUserSelect) {
        const css = `
            * {
                user-select: text !important;
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                -ms-user-select: text !important;
            }
            
            input, textarea, [contenteditable="true"] {
                user-select: auto !important;
            }
        `;
        
        if (typeof GM_addStyle !== "undefined") {
            GM_addStyle(css);
        } else {
            const style = document.createElement('style');
            style.textContent = css;
            document.documentElement.appendChild(style);
        }
        log('CSS样式已注入');
    }

    // ==================== 核心Hook类 ====================
    class CopyEnforcer {
        constructor() {
            this.hookedEvents = new WeakMap();
            this.originalAddEventListener = EventTarget.prototype.addEventListener;
            this.originalRemoveEventListener = EventTarget.prototype.removeEventListener;
            this.hookAddEventListener();
            this.hookRemoveEventListener();
            this.hookShadowDOM();
            this.setupSelectionProtection();
            this.setupCopyProtection();
            log('复制强制执行器已初始化');
        }

        // ==================== EventListener Hook ====================
        hookAddEventListener() {
            const self = this;
            EventTarget.prototype.addEventListener = function(type, listener, options) {
                // 记录被hook的事件
                if (typeof listener === 'function') {
                    if (!self.hookedEvents.has(this)) {
                        self.hookedEvents.set(this, new Map());
                    }
                    const eventMap = self.hookedEvents.get(this);
                    if (!eventMap.has(type)) {
                        eventMap.set(type, []);
                    }
                    eventMap.get(type).push({ listener, options });
                }

                // 阻止复制相关事件的默认行为阻止
                const copyBlockingEvents = [
                    'copy', 'cut', 'selectstart', 'selectionchange',
                    'mousedown', 'mouseup', 'click', 'dragstart',
                    'contextmenu', 'keydown', 'keyup', 'keypress'
                ];

                if (CONFIG.removeCopyListeners && copyBlockingEvents.includes(type.toLowerCase())) {
                    // 检查是否是阻止复制的事件处理器
                    const listenerStr = listener.toString().toLowerCase();
                    const blockingKeywords = [
                        'preventdefault', 'returnfalse', 'stopimmediatepropagation',
                        'stoppropagation', 'select', 'selection', 'copy', 'clipboard'
                    ];

                    if (blockingKeywords.some(keyword => listenerStr.includes(keyword))) {
                        log(`阻止了可能妨碍复制的${type}事件监听器`, listener);
                        
                        // 包装监听器以允许默认行为
                        const wrappedListener = function(e) {
                            try {
                                // 对于某些事件,始终允许默认行为
                                if (['copy', 'cut', 'selectstart', 'dragstart'].includes(e.type)) {
                                    e.stopImmediatePropagation = () => {
                                        log(`阻止了stopImmediatePropagation: ${e.type}`);
                                    };
                                    e.stopPropagation = () => {
                                        log(`阻止了stopPropagation: ${e.type}`);
                                    };
                                    if (e.type === 'copy' || e.type === 'cut') {
                                        // 确保复制事件能正常工作
                                        setTimeout(() => {
                                            self.restoreClipboardIfNeeded();
                                        }, 10);
                                    }
                                }
                                return listener.call(this, e);
                            } catch (err) {
                                log(`事件监听器执行错误: ${err.message}`);
                            }
                        };

                        // 复制原始属性
                        Object.defineProperties(wrappedListener, {
                            name: { value: listener.name || 'wrappedListener' },
                            toString: { value: () => listener.toString() }
                        });

                        return self.originalAddEventListener.call(this, type, wrappedListener, options);
                    }
                }

                return self.originalAddEventListener.call(this, type, listener, options);
            };
        }

        hookRemoveEventListener() {
            const self = this;
            EventTarget.prototype.removeEventListener = function(type, listener, options) {
                // 清理记录
                if (self.hookedEvents.has(this)) {
                    const eventMap = self.hookedEvents.get(this);
                    if (eventMap && eventMap.has(type)) {
                        const listeners = eventMap.get(type);
                        const index = listeners.findIndex(l => l.listener === listener);
                        if (index > -1) {
                            listeners.splice(index, 1);
                        }
                    }
                }
                return self.originalRemoveEventListener.call(this, type, listener, options);
            };
        }

        // ==================== Shadow DOM Hook ====================
        hookShadowDOM() {
            if (!Element.prototype.attachShadow) return;

            const originalAttachShadow = Element.prototype.attachShadow;
            Element.prototype.attachShadow = function(options) {
                const shadowRoot = originalAttachShadow.call(this, options);
                
                // Hook shadow root内部的事件监听
                const originalShadowAdd = shadowRoot.addEventListener;
                shadowRoot.addEventListener = function(type, listener, options) {
                    return this.ownerDocument.defaultView.EventTarget.prototype.addEventListener.call(this, type, listener, options);
                };
                
                // 为shadow root添加CSS
                setTimeout(() => {
                    const style = document.createElement('style');
                    style.textContent = `
                        * {
                            user-select: text !important;
                            -webkit-user-select: text !important;
                        }
                    `;
                    shadowRoot.appendChild(style);
                }, 100);
                
                return shadowRoot;
            };
            
            log('Shadow DOM API已hook');
        }

        // ==================== 选择保护 ====================
        setupSelectionProtection() {
            if (!CONFIG.restoreSelection) return;

            // 保护getSelection
            const originalGetSelection = window.getSelection;
            if (originalGetSelection) {
                window.getSelection = function() {
                    const selection = originalGetSelection.call(window);
                    if (selection && selection.rangeCount > 0) {
                        try {
                            // 确保选择范围有效
                            const range = selection.getRangeAt(0);
                            if (range && range.cloneContents) {
                                return selection;
                            }
                        } catch (e) {
                            log('选择范围恢复失败:', e);
                        }
                    }
                    return selection;
                };
            }

            // 保护selection相关事件
            document.addEventListener('selectstart', (e) => {
                e.stopImmediatePropagation();
                log('selectstart事件已允许');
            }, true);

            document.addEventListener('selectionchange', (e) => {
                log('selectionchange事件触发');
            }, true);
        }

        // ==================== 复制保护 ====================
        setupCopyProtection() {
            if (!CONFIG.replaceNativeCopy) return;
            // 拦截复制事件
            document.addEventListener('copy', (e) => {
                log('复制事件触发');
                e.stopImmediatePropagation();
                
                // 获取当前选择
                const selection = window.getSelection();
                if (selection && selection.toString().trim().length > 0) {
                    try {
                        // 设置剪贴板数据
                        if (e.clipboardData) {
                            e.clipboardData.setData('text/plain', selection.toString());
                            e.clipboardData.setData('text/html', this.getSelectedHTML(selection));
                        }
                        log('复制内容已设置:', selection.toString().substring(0, 100));
                    } catch (err) {
                        warn('设置剪贴板数据失败:', err);
                    }
                }
                
                e.preventDefault();
                return false;
            }, true);

            document.addEventListener('cut', (e) => {
                log('剪切事件触发');
                e.stopImmediatePropagation();
                e.preventDefault();
            }, true);
        }

        // ==================== 辅助方法 ====================
        getSelectedHTML(selection) {
            if (!selection || selection.rangeCount === 0) return '';
            
            try {
                const range = selection.getRangeAt(0);
                const container = document.createElement('div');
                container.appendChild(range.cloneContents());
                return container.innerHTML;
            } catch (e) {
                return selection.toString();
            }
        }

        restoreClipboardIfNeeded() {
            setTimeout(() => {
                try {
                    const selection = window.getSelection();
                    if (selection && selection.toString().trim().length > 0) {
                        navigator.clipboard.writeText(selection.toString()).then(() => {
                            log('剪贴板已恢复');
                        }).catch(err => {
                            log('剪贴板恢复失败:', err);
                        });
                    }
                } catch (e) {
                    // 忽略错误
                }
            }, 50);
        }

        // ==================== 清理函数 ====================
        cleanup() {
            EventTarget.prototype.addEventListener = this.originalAddEventListener;
            EventTarget.prototype.removeEventListener = this.originalRemoveEventListener;
            log('清理完成');
        }
    }

    // ==================== 激进模式Hook ====================
    if (CONFIG.aggressiveMode) {
        // Hook Object.defineProperty,防止通过设置oncopy等属性阻止复制
        const originalDefineProperty = Object.defineProperty;
        Object.defineProperty = function(obj, prop, descriptor) {
            if (prop && typeof prop === 'string') {
                const lowerProp = prop.toLowerCase();
                if (lowerProp.includes('copy') || lowerProp.includes('select') || lowerProp.includes('drag')) {
                    log(`拦截了defineProperty: ${prop}`, obj);
                    // 允许设置但记录
                }
            }
            return originalDefineProperty.call(this, obj, prop, descriptor);
        };

        // Hook setAttribute
        const originalSetAttribute = Element.prototype.setAttribute;
        Element.prototype.setAttribute = function(name, value) {
            if (name && typeof name === 'string') {
                const lowerName = name.toLowerCase();
                if (lowerName.includes('onselect') || lowerName.includes('oncopy') || 
                    lowerName.includes('oncut') || lowerName.includes('ondrag')) {
                    log(`拦截了setAttribute: ${name}`, this);
                    return; // 直接阻止设置
                }
            }
            return originalSetAttribute.call(this, name, value);
        };
    }

    // ==================== 初始化 ====================
    // 等待DOM准备就绪
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            window.copyEnforcer = new CopyEnforcer();
        });
    } else {
        window.copyEnforcer = new CopyEnforcer();
    }

    // ==================== 全局辅助函数 ====================
    // 提供手动复制函数
    window.forceCopy = function() {
        const selection = window.getSelection();
        if (selection && selection.toString().trim().length > 0) {
            try {
                document.execCommand('copy');
                log('手动复制执行成功');
                return true;
            } catch (e) {
                warn('手动复制失败:', e);
                return false;
            }
        }
        return false;
    };

    // 提供清理函数
    window.cleanupForceCopy = function() {
        if (window.copyEnforcer) {
            window.copyEnforcer.cleanup();
            delete window.copyEnforcer;
        }
    };

    log('强制复制脚本已加载');
})();