// ==UserScript== // @name 复制授权拦截器 // @namespace https://viayoo.com/ // @version 3.20 // @description 复制必弹窗,2次允许后开始自由复制,3次拒绝永久禁用(变灰,点击灰点恢复变红),点击小红点恢复,红绿切换自由复制。 // @author Aloazny & Deepseek // @match *://*/* // @run-at document-start // @icon data:image/png;base64,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 // @grant GM_setClipboard // @license MIT // ==/UserScript== (function () { const KEY = 'copyMonitor:v3'; if (window[KEY]) return; window[KEY] = true; const CONFIG = { COPY_TIME_THRESHOLD: 2000, COPY_COUNT_THRESHOLD: 5, QUICK_COPY_INTERVAL: 50, LONG_PRESS_THRESHOLD: 500 }; let copyRecords = [], lastTouchTime = 0, longPressTimer = null, hasUserAction = false, isMonitoring = true, lastUserClickTime = 0; const setupUserActionDetection = () => { document.addEventListener('click', (e) => { hasUserAction = true; lastUserClickTime = Date.now(); const isCopyButton = e.target.closest && (e.target.closest('[data-action="copy"]') || e.target.closest('[onclick*="copy"]')); if (isCopyButton) setTimeout(() => {}, 1000); }, { capture: true, passive: true }); document.addEventListener('touchstart', (e) => { hasUserAction = true; lastTouchTime = Date.now(); longPressTimer = setTimeout(() => { hasUserAction = true; }, CONFIG.LONG_PRESS_THRESHOLD); }, { capture: true, passive: true }); document.addEventListener('touchend', () => { clearTimeout(longPressTimer); }, { capture: true, passive: true }); document.addEventListener('mousedown', () => { hasUserAction = true; }, { capture: true, passive: true }); document.addEventListener('selectionchange', () => { const selection = window.getSelection().toString(); if (selection.length > 0) hasUserAction = true; }, { capture: true, passive: true }); }; const recordCopy = (method) => { if (!isMonitoring) return; const now = Date.now(), isRecentUserAction = now - lastUserClickTime < 1000; copyRecords.push({ timestamp: now, method: method, hasUserAction: hasUserAction || isRecentUserAction }); copyRecords = copyRecords.filter(record => now - record.timestamp <= CONFIG.COPY_TIME_THRESHOLD); detectMaliciousCopy(); }; const detectMaliciousCopy = () => { const now = Date.now(); const copiesWithoutUserAction = copyRecords.filter(record => !record.hasUserAction && now - record.timestamp <= CONFIG.COPY_TIME_THRESHOLD); if (copiesWithoutUserAction.length >= CONFIG.COPY_COUNT_THRESHOLD) { const hasMaliciousPattern = checkMaliciousPattern(copiesWithoutUserAction); if (hasMaliciousPattern) blockMaliciousCopy("检测到异常复制行为:短时间内自动复制次数过多"); return; } if (copiesWithoutUserAction.length >= 2) { const hasQuickCopy = checkMaliciousPattern(copiesWithoutUserAction); if (hasQuickCopy) blockMaliciousCopy("检测到异常复制行为:快速连续自动复制"); } }; const checkMaliciousPattern = (copies) => { if (copies.length < 2) return false; for (let i = 1; i < copies.length; i++) { const timeDiff = copies[i].timestamp - copies[i - 1].timestamp; if (timeDiff <= CONFIG.QUICK_COPY_INTERVAL) return true; } return false; }; const blockMaliciousCopy = (reason) => { console.warn('复制拦截:', reason); isMonitoring = false; showWarning(reason); setTimeout(() => { isMonitoring = true; copyRecords = []; }, 10000); }; const showWarning = (message) => { if (document.querySelector('.copy-monitor-warning')) return; const modal = document.createElement('div'); modal.className = 'copy-monitor-warning'; modal.style.cssText = `position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:2147483647;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;`; const dialog = document.createElement('div'); dialog.style.cssText = `background:white;padding:20px;border-radius:12px;max-width:80%;text-align:center;box-shadow:0 4px 20px rgba(0,0,0,0.15);`; const title = document.createElement('h3'); title.textContent = '安全警告'; title.style.cssText = 'color:#ff3b30;margin:0 0 10px 0;'; const msg = document.createElement('p'); msg.textContent = message; msg.style.cssText = 'margin:0 0 20px 0;color:#333;'; const button = document.createElement('button'); button.textContent = '我知道了'; button.style.cssText = `background:#007AFF;color:white;border:none;padding:10px 20px;border-radius:8px;cursor:pointer;font-size:14px;`; button.onclick = () => { modal.remove(); }; dialog.append(title, msg, button); modal.appendChild(dialog); document.body.appendChild(modal); }; const monitorCopyMethods = () => { const originalExecCommand = document.execCommand; const originalWriteText = navigator.clipboard?.writeText; const originalWrite = navigator.clipboard?.write; document.execCommand = function (command, showUI, value) { if (command === 'copy') { const now = Date.now(), isRecentUserAction = now - lastUserClickTime < 1000; if (hasUserAction || isRecentUserAction) { recordCopy('execCommand'); return originalExecCommand.apply(this, arguments); } else { recordCopy('execCommand'); return askForCopyPermission().then((allowed) => { return allowed ? originalExecCommand.apply(this, arguments) : false; }).catch(() => false); } } return originalExecCommand.apply(this, arguments); }; if (navigator.clipboard && originalWriteText) { navigator.clipboard.writeText = function (text) { const now = Date.now(), isRecentUserAction = now - lastUserClickTime < 1000; recordCopy('clipboard.writeText'); if (hasUserAction || isRecentUserAction) return originalWriteText.call(this, text); return askForCopyPermission().then((allowed) => { return allowed ? originalWriteText.call(this, text) : Promise.reject(new Error('用户拒绝复制')); }); }; } if (navigator.clipboard && originalWrite) { navigator.clipboard.write = function (data) { const now = Date.now(), isRecentUserAction = now - lastUserClickTime < 1000; recordCopy('clipboard.write'); if (hasUserAction || isRecentUserAction) return originalWrite.call(this, data); return askForCopyPermission().then((allowed) => { return allowed ? originalWrite.call(this, data) : Promise.reject(new Error('用户拒绝复制')); }); }; } document.addEventListener('copy', (e) => { const now = Date.now(), isRecentUserAction = now - lastUserClickTime < 1000; recordCopy('copy-event'); if (hasUserAction || isRecentUserAction) return; e.preventDefault(); e.stopImmediatePropagation(); askForCopyPermission().then((allowed) => { if (allowed && e.clipboardData) { const selection = window.getSelection().toString(); if (selection) e.clipboardData.setData('text/plain', selection); } }); }, { capture: true }); }; const askForCopyPermission = () => { return new Promise((resolve) => { if (document.querySelector('.copy-permission-dialog')) { resolve(false); return; } const modal = document.createElement('div'); modal.className = 'copy-permission-dialog'; modal.style.cssText = `position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:2147483647;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;`; const dialog = document.createElement('div'); dialog.style.cssText = `background:white;padding:20px;border-radius:12px;max-width:80%;text-align:center;box-shadow:0 4px 20px rgba(0,0,0,0.15);`; const title = document.createElement('h3'); title.textContent = '复制确认'; title.style.margin = '0 0 10px 0'; const message = document.createElement('p'); message.textContent = '检测到自动复制操作,是否允许?'; message.style.margin = '0 0 20px 0'; const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = 'display:flex;gap:10px;justify-content:center;'; const allowButton = document.createElement('button'); allowButton.textContent = '允许'; allowButton.style.cssText = `background:#34C759;color:white;border:none;padding:10px 20px;border-radius:8px;cursor:pointer;`; const denyButton = document.createElement('button'); denyButton.textContent = '拒绝'; denyButton.style.cssText = `background:#FF3B30;color:white;border:none;padding:10px 20px;border-radius:8px;cursor:pointer;`; const cleanup = () => { modal.remove(); setTimeout(() => { hasUserAction = false; }, 100); }; allowButton.onclick = () => { cleanup(); resolve(true); }; denyButton.onclick = () => { cleanup(); resolve(false); }; buttonContainer.append(denyButton, allowButton); dialog.append(title, message, buttonContainer); modal.appendChild(dialog); document.body.appendChild(modal); }); }; const init = () => { setupUserActionDetection(); monitorCopyMethods(); console.log('复制行为监控器已启动'); }; if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init(); })();