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://github.com/lanzeweie
// @version      0.82
// @description  我真的没有切屏!!!采用多重策略,从内核层面阻止浏览器将失焦或隐藏状态暴露给网站。尽最大可能伪造一直在窗口的假象
// @author       [email protected]
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const blockEvents = ['visibilitychange', 'blur', 'focus', 'focusin', 'focusout', 'pagehide', 'pageshow'];
    const originalAddEventListener = EventTarget.prototype.addEventListener;

    EventTarget.prototype.addEventListener = function(type, listener, options) {
        if (blockEvents.includes(type)) {
            addLog('proxy', `🚫 已代理并阻止 ${type} 监听器附加`, '#6f42c1');
            return;
        }
        return originalAddEventListener.call(this, type, listener, options);
    };

    ["visibilitychange", "blur", "focus", "focusin", "focusout"].forEach((e) => {
        originalAddEventListener.call(
            window,
            e,
            (event) => {
                event.stopImmediatePropagation();
                event.stopPropagation();
                event.preventDefault();
                return false;
            },
            true
        );
    });

    let isEnabled = true;
    let isUIVisible = false;
    let uiContainer = null;
    let uiMinimizedTag = null;
    let uiUpdateFunction = () => {};

    const stats = {
        visibilitychange: 0,
        blur: 0,
        focusout: 0,
        focusin: 0,
        pagehide: 0,
        pageshow: 0,
        proxy: 0,
        get total() {
            return this.visibilitychange + this.blur + this.focusout + this.focusin + this.pagehide + this.pageshow + this.proxy;
        }
    };

    const interceptLog = [];
    const MAX_LOG_ITEMS = 15;

    function addLog(type, message, color = '#dc3545') {
        const timestamp = new Date().toLocaleTimeString();
        interceptLog.unshift({ time: timestamp, type: type, message: message, color: color });
        if (interceptLog.length > MAX_LOG_ITEMS) {
            interceptLog.pop();
        }
        if (type === 'proxy') {
            stats.proxy++;
        }
        if (uiUpdateFunction) {
            uiUpdateFunction();
        }
    }

    try {
        document.hasFocus = () => true;
        Object.defineProperty(document, 'hidden', {
            get() { return false; },
            configurable: true
        });
        Object.defineProperty(document, 'visibilityState', {
            get: () => 'visible',
            configurable: true
        });
        if (!document.__hasFocusPatched) {
            document.hasFocus = function() { return true; };
            document.__hasFocusPatched = true;
        }
        addLog('system', '✓ 属性覆盖成功 (hasFocus/hidden/visibilityState)', '#28a745');
    } catch (e) {
        addLog('system', '❌ 属性覆盖失败: ' + e.message, '#dc3545');
    }

    const interceptOtherEvents = (e) => {
        if (!isEnabled) return;
        e.preventDefault();
        e.stopImmediatePropagation();
        e.stopPropagation();

        const eventType = e.type;
        if (stats.hasOwnProperty(eventType)) {
            stats[eventType]++;
            let logMessage = '';
            switch (eventType) {
                case 'visibilitychange':
                    logMessage = `📱 强力阻止 页面可见性检测`;
                    break;
                case 'pagehide':
                    logMessage = `📄 强力阻止 页面隐藏 (pagehide)`;
                    break;
                case 'pageshow':
                    logMessage = `📄 强力阻止 页面显示 (pageshow)`;
                    break;
                case 'focusin':
                    logMessage = `⚡ 强力阻止 焦点获得 (focusin)`;
                    break;
            }
            addLog(eventType, logMessage, '#28a745');
        }
    };

    try {
        ['visibilitychange', 'pagehide', 'pageshow', 'focusin'].forEach(eventType => {
            originalAddEventListener.call(window, eventType, interceptOtherEvents, true);
            originalAddEventListener.call(document, eventType, interceptOtherEvents, true);
        });
    } catch (e) {
         addLog('system', '❌ 监听器附加失败: ' + e.message, '#dc3545');
    }

    (function() {
        try {
            const originalObserve = MutationObserver.prototype.observe;
            MutationObserver.prototype.observe = function(target, options) {
                if (target === document || target === document.documentElement) {
                    addLog('system', '🛡️ 拦截 DOM 观察器', '#007bff');
                    return { disconnect: () => {}, observe: () => {}, unobserve: () => {} };
                }
                return originalObserve.apply(this, arguments);
            };
            const originalMutationObserver = window.MutationObserver;
            window.MutationObserver = function(callback) {
                const observer = new originalMutationObserver((mutations) => {
                    const filteredMutations = mutations.filter(mutation => {
                        return mutation.target !== document && mutation.target !== document.documentElement;
                    });
                    if (filteredMutations.length > 0) {
                        callback(filteredMutations);
                    }
                });
                return observer;
            };
            window.MutationObserver.prototype = originalMutationObserver.prototype;
        } catch (e) {
        }
    })();

    addLog('system', '✅ 切屏防护系统已启动', '#007bff');
    addLog('system', '📌 UI优化: 最小化标签尺寸缩小 + 模式颜色区分', '#17a2b8');


    const createUI = () => {
        if (window.top !== window.self) {
            return;
        }

        const completelyHideUI = () => {
            if (uiContainer && uiContainer.parentNode) {
                uiContainer.parentNode.removeChild(uiContainer);
                uiContainer = null;
            }
            if (uiMinimizedTag && uiMinimizedTag.parentNode) {
                uiMinimizedTag.parentNode.removeChild(uiMinimizedTag);
                uiMinimizedTag = null;
            }
            addLog('system', '👻 UI 已完全隐藏 (需刷新页面恢复)', '#6c757d');
        };

        // === 最小化标签 ===
        uiMinimizedTag = document.createElement('div');
        const uiTagTitle = document.createElement('span');
        const uiTagHideButton = document.createElement('span');

        uiTagTitle.textContent = '🛡️ 防护';
        uiTagHideButton.textContent = '❌';
        uiTagHideButton.title = '从页面中移除所有UI元素 (需刷新恢复)';

        uiMinimizedTag.appendChild(uiTagTitle);
        uiMinimizedTag.appendChild(uiTagHideButton);

        // 最小化标签样式
        Object.assign(uiMinimizedTag.style, {
            position: 'fixed',
            bottom: '20px',
            right: '20px',
            color: 'white',
            padding: '4px 8px',
            borderRadius: '15px',
            fontSize: '11px',
            fontFamily: 'sans-serif',
            cursor: 'pointer',
            zIndex: '999999',
            boxShadow: '0 2px 8px rgba(0, 0, 0, 0.2)',
            transition: 'all 0.3s ease',
            userSelect: 'none',
            display: 'flex',
            gap: '5px',
            alignItems: 'center',
            fontWeight: 'bold',
        });

        Object.assign(uiTagHideButton.style, {
            padding: '2px 4px',
            backgroundColor: 'rgba(255, 255, 255, 0.2)',
            borderRadius: '10px',
            cursor: 'pointer',
            fontSize: '10px',
            fontWeight: 'normal',
            lineHeight: '1'
        });

        const updateUIMinimizedTagBackground = () => {
            const enabledColor = 'rgba(0, 123, 255, 0.9)';
            const disabledColor = 'rgba(108, 117, 125, 0.9)';
            if (uiMinimizedTag) {
                uiMinimizedTag.style.backgroundColor = isEnabled ? enabledColor : disabledColor;
            }
        };


        uiTagTitle.addEventListener('click', (e) => {
            e.stopPropagation();
            isUIVisible = !isUIVisible;
            uiContainer.style.display = isUIVisible ? 'block' : 'none';
            uiMinimizedTag.style.display = isUIVisible ? 'none' : 'flex';
        });

        uiTagHideButton.addEventListener('click', (e) => {
            e.stopPropagation();
            completelyHideUI();
        });

        // === 主面板 ===
        uiContainer = document.createElement('div');
        const uiHeader = document.createElement('div');
        const uiTitle = document.createElement('span');
        const uiControls = document.createElement('div');
        const uiToggleButton = document.createElement('span');
        const uiMinimizeButton = document.createElement('span');
        const uiHideButton = document.createElement('span');
        const uiBody = document.createElement('div');
        const uiRealTimePanel = document.createElement('div');
        const uiRealTimeTitle = document.createElement('div');
        const uiFocusStatus = document.createElement('div');
        const uiVisibilityStatus = document.createElement('div');
        const uiLogPanel = document.createElement('div');
        const uiLogTitle = document.createElement('div');
        const uiLogContent = document.createElement('div');
        const uiStatsPanel = document.createElement('div');
        const uiStatsTitle = document.createElement('div');
        const uiStatsContent = document.createElement('div');
        const uiControlPanel = document.createElement('div');
        const uiStatus = document.createElement('div');
        const uiHotKey = document.createElement('div');

        uiHeader.appendChild(uiTitle);
        uiControls.appendChild(uiToggleButton);
        uiControls.appendChild(uiMinimizeButton);
        uiControls.appendChild(uiHideButton);
        uiHeader.appendChild(uiControls);
        uiRealTimePanel.appendChild(uiRealTimeTitle);
        uiRealTimePanel.appendChild(uiFocusStatus);
        uiRealTimePanel.appendChild(uiVisibilityStatus);
        uiLogPanel.appendChild(uiLogTitle);
        uiLogPanel.appendChild(uiLogContent);
        uiStatsPanel.appendChild(uiStatsTitle);
        uiStatsPanel.appendChild(uiStatsContent);
        uiControlPanel.appendChild(uiStatus);
        uiControlPanel.appendChild(uiHotKey);
        uiBody.appendChild(uiRealTimePanel);
        uiBody.appendChild(uiLogPanel);
        uiBody.appendChild(uiStatsPanel);
        uiBody.appendChild(uiControlPanel);
        uiContainer.appendChild(uiHeader);
        uiContainer.appendChild(uiBody);

        const appendUI = () => {
            if (document.body) {
                document.body.appendChild(uiContainer);
                document.body.appendChild(uiMinimizedTag);
                uiContainer.style.display = 'none';
                updateUIMinimizedTagBackground();
            }
        };

        if (document.body) {
            appendUI();
        } else {
            originalAddEventListener.call(document, 'DOMContentLoaded', appendUI);
        }

        uiTitle.textContent = "🛡️ 切屏防护";
        uiToggleButton.title = '点击切换 启用/禁用';
        uiMinimizeButton.textContent = '—';
        uiMinimizeButton.title = '点击最小化 (切换到右下角标签)';
        uiHideButton.textContent = '✕';
        uiHideButton.title = '完全隐藏 UI';

        const updateUI = () => {
            updateUIMinimizedTagBackground();

            uiRealTimeTitle.innerHTML = '<strong>📊 实时状态</strong>';
            uiFocusStatus.innerHTML = `🎯 焦点状态: <span style="color: #28a745; font-weight: bold;">✓ 强制聚焦</span>`;
            uiVisibilityStatus.innerHTML = `📱 页面可见: <span style="color: #28a745; font-weight: bold;">visible</span> | hidden: <span style="color: #28a745; font-weight: bold;">false</span>`;

            uiLogTitle.innerHTML = '<strong>📝 拦截日志</strong>';
            if (interceptLog.length === 0) {
                uiLogContent.innerHTML = '<div style="color: #6c757d; font-style: italic;">暂无拦截记录</div>';
            } else {
                uiLogContent.innerHTML = interceptLog.map(log => {
                    return `<div style="font-size: 11px; margin: 2px 0; padding: 2px 4px; background: rgba(0,0,0,0.03); border-radius: 3px;">
                        <span style="color: #888;">${log.time}</span> - <span style="color: ${log.color};">${log.message}</span>
                    </div>`;
                }).join('');
            }

            uiStatsTitle.innerHTML = '<strong>📈 拦截统计</strong>';
            uiStatsContent.innerHTML = `
                <div style="margin-top: 5px;">
                    <div style="color: #6f42c1;">🚫 代理拦截 (blur/focusout): <strong>${stats.proxy}</strong></div>
                    <div style="color: #dc3545;">📱 页面可见性 (vis/page): <strong>${stats.visibilitychange + stats.pagehide + stats.pageshow}</strong></div>
                    <div style="color: #20c997;">⚡ 焦点事件 (focusin): <strong>${stats.focusin}</strong></div>
                    <div style="margin-top: 5px; padding-top: 5px; border-top: 1px solid #eee; color: #28a745; font-weight: bold;">
                        总拦截次数: ${stats.total}
                    </div>
                </div>
            `;

            uiStatus.innerHTML = `<span style="color: ${isEnabled ? '#28a745' : '#6c757d'}; font-weight: bold;">🛡️ 防护: ${isEnabled ? '✓ 开启' : '✗ 关闭'}</span>`;
            uiToggleButton.textContent = isEnabled ? '🟢' : '🔴';
            uiHotKey.textContent = 'Ctrl+Alt+H 显示/最小化 | 点击拖动';
        };

        uiUpdateFunction = updateUI;
        updateUI();

        // --- 样式 (主面板) ---
        Object.assign(uiContainer.style, {
            position: 'fixed',
            bottom: '20px',
            right: '20px',
            top: 'auto',
            left: 'auto',
            width: '320px',
            backgroundColor: 'rgba(255, 255, 255, 0.97)',
            color: '#333',
            border: '1px solid #ccc',
            borderRadius: '8px',
            zIndex: '999999',
            fontFamily: 'sans-serif',
            fontSize: '12px',
            backdropFilter: 'blur(5px)',
            webkitBackdropFilter: 'blur(5px)',
            boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
            transition: 'transform 0.3s ease',
            display: isUIVisible ? 'block' : 'none',
            fontWeight: '500'
        });

        Object.assign(uiHeader.style, {
            display: 'flex',
            justifyContent: 'space-between',
            alignItems: 'center',
            padding: '8px 12px',
            backgroundColor: 'rgba(0, 123, 255, 0.1)',
            cursor: 'move',
            userSelect: 'none',
            borderRadius: '8px 8px 0 0'
        });
        Object.assign(uiTitle.style, { fontWeight: 'bold', color: '#007bff' });
        Object.assign(uiControls.style, { display: 'flex', gap: '10px' });
        const buttonStyle = {
            cursor: 'pointer',
            fontWeight: 'bold',
            padding: '2px 8px',
            borderRadius: '4px',
            transition: 'background-color 0.2s'
        };
        Object.assign(uiToggleButton.style, buttonStyle);
        Object.assign(uiMinimizeButton.style, buttonStyle);
        Object.assign(uiHideButton.style, buttonStyle);
        Object.assign(uiBody.style, { padding: '10px 12px', maxHeight: '70vh', overflowY: 'auto' });
        const panelStyle = { marginBottom: '12px', padding: '8px', backgroundColor: 'rgba(0, 0, 0, 0.02)', borderRadius: '6px', border: '1px solid rgba(0, 0, 0, 0.05)' };
        Object.assign(uiRealTimePanel.style, panelStyle);
        Object.assign(uiLogPanel.style, panelStyle);
        Object.assign(uiStatsPanel.style, panelStyle);
        Object.assign(uiControlPanel.style, panelStyle);
        const titleStyle = { marginBottom: '6px', color: '#495057', fontSize: '12px' };
        Object.assign(uiRealTimeTitle.style, titleStyle);
        Object.assign(uiLogTitle.style, titleStyle);
        Object.assign(uiStatsTitle.style, titleStyle);
        Object.assign(uiFocusStatus.style, { margin: '4px 0', fontSize: '11px' });
        Object.assign(uiVisibilityStatus.style, { margin: '4px 0', fontSize: '11px' });
        Object.assign(uiLogContent.style, { maxHeight: '120px', overflowY: 'auto' });
        Object.assign(uiStatsContent.style, { lineHeight: '1.6' });
        Object.assign(uiStatus.style, { marginBottom: '6px', fontSize: '13px' });
        Object.assign(uiHotKey.style, { color: '#888', fontSize: '10px', textAlign: 'center' });

        // --- UI 交互 ---
        uiToggleButton.addEventListener('click', (e) => {
            e.stopPropagation();
            isEnabled = !isEnabled;
            addLog('system', isEnabled ? '✅ 防护已开启' : '❌ 防护已关闭', isEnabled ? '#28a745' : '#6c757d');
            updateUI();
        });

        uiMinimizeButton.addEventListener('click', (e) => {
            e.stopPropagation();
            isUIVisible = false;
            uiContainer.style.display = 'none';
            uiMinimizedTag.style.display = 'flex';
            addLog('system', '📌 已最小化到右下角标签', '#17a2b8');
        });

        uiHideButton.addEventListener('click', (e) => {
            e.stopPropagation();
            completelyHideUI();
        });

        let isDragging = false;
        let offset = { x: 0, y: 0 };
        uiHeader.addEventListener('mousedown', (e) => {
            isDragging = true;
            offset.x = e.clientX - uiContainer.getBoundingClientRect().left;
            offset.y = e.clientY - uiContainer.getBoundingClientRect().top;
            uiHeader.style.cursor = 'grabbing';
            document.body.style.userSelect = 'none';
        });
        window.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            let newX = e.clientX - offset.x;
            newX = Math.max(0, Math.min(newX, window.innerWidth - uiContainer.offsetWidth));
            let newRight = window.innerWidth - (newX + uiContainer.offsetWidth);
            newRight = Math.max(0, newRight);

            Object.assign(uiContainer.style, {
                left: 'auto',
                right: `${newRight}px`,
                bottom: '20px',
                top: 'auto',
            });
        });
        window.addEventListener('mouseup', () => {
            if (!isDragging) return;
            isDragging = false;
            uiHeader.style.cursor = 'move';
            document.body.style.userSelect = 'auto';
        });
    };

    originalAddEventListener.call(window, 'keydown', (e) => {
        if (e.key.toLowerCase() === 'h' && e.ctrlKey && e.altKey && !e.shiftKey) {
            e.preventDefault();
            e.stopPropagation();
            isUIVisible = !isUIVisible;
            if (uiContainer) {
                uiContainer.style.display = isUIVisible ? 'block' : 'none';
                if (uiMinimizedTag) {
                    uiMinimizedTag.style.display = isUIVisible ? 'none' : 'flex';
                }
            }
        }
    }, true);

    if (document.readyState === 'loading') {
        originalAddEventListener.call(window, 'DOMContentLoaded', createUI);
    } else {
        createUI();
    }

})();