您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
极速多链接并发检测,智能状态持久化显示,自动提取码填充,实时回复检测,
当前为
// ==UserScript== // @name 123网盘链接检测助手 // @namespace https://pan1.me // @version 4.0.0 // @description 极速多链接并发检测,智能状态持久化显示,自动提取码填充,实时回复检测, // @author 绘梦 // @license CC BY-NC-ND 4.0 // @icon data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHZpZXdCb3g9IjAgMCA2NCA2NCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiByeD0iMTIiIGZpbGw9IiMxODc1ZmYiLz4KPHN2ZyB4PSIxMiIgeT0iMTIiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIj4KPHBhdGggZD0iTTggNGg4YzEuMSAwIDIgLjkgMiAydjEyYzAgMS4xLS45IDItMiAySDhjLTEuMSAwLTItLjktMi0yVjZjMC0xLjEuOS0yIDItMnoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xMiA4djgiIHN0cm9rZT0iIzE4NzVmZiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHN2ZyB4PSIxOCIgeT0iMTgiIHdpZHRoPSI4IiBoZWlnaHQ9IjgiIHZpZXdCb3g9IjAgMCA4IDgiPgo8Y2lyY2xlIGN4PSI0IiBjeT0iNCIgcj0iMyIgZmlsbD0iIzEwYjk4MSIvPgo8L3N2Zz4KPC9zdmc+Cjwvc3ZnPg== // @match *//*/* // @match https://pan1.me/* // @match https://*.123pan.com/* // @match https://*.123865.com/* // @match https://*.123684.com/* // @match https://*.123yunpan.com/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @connect 123pan.com // @connect 123865.com // @connect 123684.com // @connect 123yunpan.com // @connect * // @require https://code.jquery.com/jquery-3.6.0.min.js // @run-at document-end // ==/UserScript== /* * 123网盘链接检测助手 v4.0.0 * * 版权所有 (c) 2024 Your Name * * 本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。 * 要查看该许可协议,可访问 http://creativecommons.org/licenses/by-nc-nd/4.0/ * * 许可协议要点: * ✅ 署名 - 您必须给出适当的署名 * ❌ 非商业性使用 - 您不得将本作品用于商业目的 * ❌ 禁止演绎 - 您不得修改、转换或以此为基础创作 * * 警告:未经授权的修改、商业使用或再分发将构成侵权行为 */ (function() { 'use strict'; // 配置参数 - 极速检测版 const CONFIG = { // 高速链接检测模式 linkPattern: /https?:\/\/(?:www\.)?[a-z0-9]*123[a-z0-9]*\.com\/s\/[A-Za-z0-9\-_]+/g, extractCodeRegex: /(?:提取码|密码|pwd)[::=\s]*([A-Za-z0-9]{4,8})/i, maxConcurrentChecks: Infinity, // 无限并发,极速处理所有链接 requestTimeout: 500, // 缩短超时时间 retryAttempts: 0, // 取消重试,直接失败 // 缓存配置(24小时缓存) cacheExpireTime: 24 * 60 * 60 * 1000, // 同域检测配置 sameDomainDetection: true, realtimeDetection: true, detectionInterval: 2000 // 减少实时检测频率,避免资源浪费 }; // 状态标记样式 const STATUS_STYLES = { valid: 'color: #28a745; font-weight: bold; background: #d4edda; padding: 2px 6px; border-radius: 3px;', invalid: 'color: #dc3545; text-decoration: line-through; background: #f8d7da; padding: 2px 6px; border-radius: 3px;', processing: 'color: #fd7e14; background: #fff3cd; padding: 2px 6px; border-radius: 3px;', encrypted: 'color: #6f42c1; background: #e2e3f0; padding: 2px 6px; border-radius: 3px;', error: 'color: #868e96; background: #f8f9fa; padding: 2px 6px; border-radius: 3px;', unknown: 'color: #6c757d; background: #e9ecef; padding: 2px 6px; border-radius: 3px;' }; // 用户代理列表(随机化) const USER_AGENTS = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0' ]; // 统一的全局状态管理(避免变量分散) const GlobalState = { // 链接检测状态 checkedLinks: new Set(), linkStatusMap: new Map(), isScanning: false, activeRequests: 0, // 同域检测状态 sameDomainDetector: null, detectionTimer: null, // 回复功能状态 replyState: { tried: false, inProgress: false }, autoReplyEnabled: GM_getValue('autoReplyEnabled', true), // 默认开启 // 缓存状态 pageContentCache: null, lastContentCheck: 0, // 智能缓存系统(24小时有效) linkCache: new Map(), // 反爬虫机制状态 antiCrawler: { lastReplyTime: 0, replyCount: 0, sessionStartTime: Date.now() }, // 防重复回复机制 replyHistory: { usedTexts: new Set(), // 已使用的回复文案 pageUrls: new Set(), // 已回复的页面URL maxHistorySize: 100 // 最大历史记录数量 }, // 调试模式 debugMode: false, // 重置所有状态 reset() { this.checkedLinks.clear(); this.linkStatusMap.clear(); this.linkCache.clear(); this.isScanning = false; this.activeRequests = 0; this.replyState = { tried: false, inProgress: false }; this.pageContentCache = null; this.lastContentCheck = 0; }, // 缓存管理方法 getCachedResult(url) { const cached = this.linkCache.get(url); if (cached && (Date.now() - cached.timestamp < CONFIG.cacheExpireTime)) { Log.debug('🎯 使用缓存结果:', url); return cached; } return null; }, setCachedResult(url, status, method = 'unknown') { this.linkCache.set(url, { status: status, method: method, timestamp: Date.now() }); // 限制缓存大小(最多100个) if (this.linkCache.size > 100) { const oldestKey = this.linkCache.keys().next().value; this.linkCache.delete(oldestKey); } } }; // 初始化菜单 function initMenu() { // 注册菜单项 GM_registerMenuCommand( GlobalState.autoReplyEnabled ? '🔴 关闭自动回复' : '🟢 开启自动回复', toggleAutoReply, 'a' ); // 添加状态信息菜单 GM_registerMenuCommand( '📊 查看回复统计', showReplyStats, 's' ); Log.info(`自动回复状态: ${GlobalState.autoReplyEnabled ? '已开启' : '已关闭'}`); } // 切换自动回复状态 function toggleAutoReply() { GlobalState.autoReplyEnabled = !GlobalState.autoReplyEnabled; GM_setValue('autoReplyEnabled', GlobalState.autoReplyEnabled); // 重新注册菜单以更新显示 location.reload(); // 简单的重新加载来更新菜单显示 Log.info(`自动回复已${GlobalState.autoReplyEnabled ? '开启' : '关闭'}`); } // 显示回复统计 function showReplyStats() { const stats = GlobalState.antiCrawler; const sessionTime = Math.floor((Date.now() - stats.sessionStartTime) / 1000 / 60); // 分钟 const history = GlobalState.replyHistory; alert(`📊 回复统计信息: 🔄 自动回复状态: ${GlobalState.autoReplyEnabled ? '已开启' : '已关闭'} 📝 本次会话回复次数: ${stats.replyCount} ⏱️ 本次会话时长: ${sessionTime} 分钟 🕐 上次回复时间: ${stats.lastReplyTime ? new Date(stats.lastReplyTime).toLocaleTimeString() : '无'} 🔒 已使用文案数量: ${history.usedTexts.size} 📄 已回复页面数量: ${history.pageUrls.size} 💡 提示: 智能防重复机制,自定义+平台文案混合,0-2秒极速回复`); } // 核心功能立即启动(不依赖控制面板) function init() { // 初始化菜单 initMenu(); // 立即启动独立的实时回复检测(最高优先级) startRealtimeReplyDetection(); // 启动定期清理机制(防内存泄漏) startPeriodicCleanup(); // 立即启动核心检测功能 - 统一检测系统 try { // 启动统一的实时检测系统(合并所有检测逻辑) startUnifiedDetection(); // 立即扫描现有链接(仅执行一次)- 极速模式 const existingLinks = findPanLinks(); if (existingLinks.length > 0) { Log.info(`发现 ${existingLinks.length} 个链接,极速检测启动`); // 全部链接立即并发检测,无延迟 existingLinks.forEach(checkLink); } if (isOn123PanSite()) { initAutoFillExtractCode(); if (CONFIG.sameDomainDetection) { initSameDomainDetection(); } } console.log('✅ 核心检测功能已独立启动 - 无UI模式'); } catch (error) { console.error('❌ 核心功能启动失败:', error); } } // 彻底修复的链接查找(防重复识别) function findPanLinks() { const links = []; const foundUrls = new Set(); // 严格去重 // 1. 优先查找a标签链接 const aLinks = document.querySelectorAll('a[href]'); aLinks.forEach(link => { if (isPanLink(link.href)) { const normalizedUrl = normalizeUrl(link.href); if (!foundUrls.has(normalizedUrl)) { foundUrls.add(normalizedUrl); // 为现有a标签链接也自动附加提取码 enhanceExistingLink(link); links.push(link); } } }); // 2. 查找并转换纯文本链接为可点击链接 const textNodes = getTextNodes(document.body); textNodes.forEach(node => { // 检查文本节点是否在a标签内 if (node.parentElement && node.parentElement.closest('a[href]')) { return; // 跳过已在a标签中的文本 } const matches = node.textContent.match(CONFIG.linkPattern); if (matches) { matches.forEach(url => { const normalizedUrl = normalizeUrl(url); if (!foundUrls.has(normalizedUrl)) { foundUrls.add(normalizedUrl); // 创建真实的可点击a标签 const clickableLink = createClickableLink(url, node); if (clickableLink) { links.push(clickableLink); } } }); } }); return links; } // URL标准化(去除参数差异) function normalizeUrl(url) { try { const urlObj = new URL(url); return `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`; } catch (e) { return url.split('?')[0]; // 简单去参数 } } // 创建可点击的链接(替换纯文本,自动附加提取码) function createClickableLink(url, textNode) { try { const parent = textNode.parentElement; if (!parent) return null; // 查找周围的提取码 const extractCode = findExtractCodeNearElement(textNode); // 如果找到提取码且URL中没有密码参数,则自动附加 let finalUrl = url; if (extractCode && !url.includes('pwd=')) { const separator = url.includes('?') ? '&' : '?'; finalUrl = `${url}${separator}pwd=${extractCode}`; console.log(`为链接自动附加提取码: ${finalUrl}`); } // 创建可点击的a标签 const linkElement = document.createElement('a'); linkElement.href = finalUrl; linkElement.textContent = url; // 显示原始URL,但实际跳转带提取码 linkElement.target = '_blank'; // 新标签页打开 linkElement.rel = 'noopener noreferrer'; // 安全性 linkElement.style.cssText = ` color: #007bff; text-decoration: underline; cursor: pointer; word-break: break-all; padding: 2px 4px; border-radius: 3px; background-color: rgba(0, 123, 255, 0.1); transition: background-color 0.2s ease; `; // 添加悬停效果 linkElement.addEventListener('mouseenter', () => { linkElement.style.backgroundColor = 'rgba(0, 123, 255, 0.2)'; }); linkElement.addEventListener('mouseleave', () => { linkElement.style.backgroundColor = 'rgba(0, 123, 255, 0.1)'; }); // 替换文本节点中的URL为可点击链接 const textContent = textNode.textContent; const urlIndex = textContent.indexOf(url); if (urlIndex !== -1) { // 分割文本:前部分 + 链接 + 后部分 const beforeText = textContent.substring(0, urlIndex); const afterText = textContent.substring(urlIndex + url.length); // 创建文档片段 const fragment = document.createDocumentFragment(); // 添加前部分文本 if (beforeText) { fragment.appendChild(document.createTextNode(beforeText)); } // 添加可点击链接 fragment.appendChild(linkElement); // 添加后部分文本 if (afterText) { fragment.appendChild(document.createTextNode(afterText)); } // 替换原文本节点 parent.replaceChild(fragment, textNode); return linkElement; } return null; } catch (e) { console.warn('创建可点击链接失败:', e); return null; } } // 获取所有文本节点 function getTextNodes(element) { const textNodes = []; const walker = document.createTreeWalker( element, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { // 跳过script、style等标签 const parent = node.parentElement; if (parent && ['SCRIPT', 'STYLE', 'NOSCRIPT'].includes(parent.tagName)) { return NodeFilter.FILTER_REJECT; } // 只处理包含123链接的文本节点 if (CONFIG.linkPattern.test(node.textContent)) { return NodeFilter.FILTER_ACCEPT; } return NodeFilter.FILTER_REJECT; } } ); let node; while (node = walker.nextNode()) { textNodes.push(node); } return textNodes; } // 精简的链接验证 function isPanLink(url) { return CONFIG.linkPattern.test(url); } // 检测是否在123网盘站点 function isOn123PanSite() { const hostname = window.location.hostname.toLowerCase(); return /123[a-z0-9]*\.com$/.test(hostname); } // ==================== 一比一复刻的专业检测系统 ==================== // 同域检测配置(完全复刻参考脚本) const SAME_ORIGIN_CONFIG = { // 123网盘相关域名(扩展支持更多子域名) domains: [ '123pan.com', // 官方主域名 '123865.com', // 常见子域名 '123684.com', // 常见子域名 '123912.com', // 新发现的子域名 '123yunpan.com', // 云盘子域名 '123netdisk.com', // 网盘子域名 '123disk.com', // 磁盘子域名 '123file.com', // 文件子域名 '123share.com' // 分享子域名 ], // 支持同域检测的页面路径 supportedPaths: ['/s/', '/share/', '/home/share/'] }; // 精简日志管理器(防重复版) const Log = { _lastMessages: new Map(), _maxCacheSize: 50, // 智能防重复日志输出 _logWithDedupe(level, icon, ...args) { const message = args.join(' '); const now = Date.now(); // 对于包含数字的消息(如"发现 X 个链接"),使用特殊处理 if (/发现\s+\d+\s+个/.test(message) || /检测完成/.test(message)) { // 多链接相关日志允许更频繁输出 const key = `${level}:${message.replace(/\d+/g, 'N')}`; if (this._lastMessages.has(key)) { const lastTime = this._lastMessages.get(key); if (now - lastTime < 2000) return; // 2秒内不重复 } this._lastMessages.set(key, now); } else { // 其他日志保持5秒去重 const key = `${level}:${message}`; if (this._lastMessages.has(key)) { const lastTime = this._lastMessages.get(key); if (now - lastTime < 5000) return; } this._lastMessages.set(key, now); } // 限制缓存大小 if (this._lastMessages.size > this._maxCacheSize) { const oldestKey = this._lastMessages.keys().next().value; this._lastMessages.delete(oldestKey); } console.log(icon, ...args); }, info: (...args) => Log._logWithDedupe('info', 'ℹ️', ...args), success: (...args) => Log._logWithDedupe('success', '✅', ...args), warn: (...args) => Log._logWithDedupe('warn', '⚠️', ...args), error: (...args) => Log._logWithDedupe('error', '❌', ...args), debug: (...args) => GlobalState.debugMode && Log._logWithDedupe('debug', '🔍', ...args), // 分组日志(减少使用) group: (name, collapsed = true) => GlobalState.debugMode && console[collapsed ? 'groupCollapsed' : 'group'](`🔍 [${name}]`), groupEnd: () => GlobalState.debugMode && console.groupEnd() }; // 专业的123网盘页面状态检测器(一比一复刻) function detectPanPageStatus(doc) { const body = doc.body; const text = body.textContent; const detectedElements = []; // 检测元素可见性 function checkElementVisibility(element) { if (!element) return false; const style = doc.defaultView.getComputedStyle(element); if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') { return false; } return true; } // 1. 优先检测"提取文件"按钮(最可靠的有效页面标志) const buttons = body.querySelectorAll('button'); for (const button of buttons) { const buttonText = button.textContent || ''; if ((buttonText.includes('提取文件') || buttonText.includes('取文件') || buttonText.includes('extract')) && checkElementVisibility(button)) { detectedElements.push('提取文件按钮'); return { status: '有效页面(提取文件)', isValid: true, needsPassword: false, confidence: 0.98, detectedElements }; } } // 1.5. 检测下载按钮和保存按钮(高置信度有效特征) for (const button of buttons) { const buttonText = button.textContent || ''; if ((buttonText.includes('下载') || buttonText.includes('保存至云盘') || buttonText.includes('浏览器下载')) && checkElementVisibility(button)) { detectedElements.push('下载/保存按钮'); return { status: '有效页面(下载)', isValid: true, needsPassword: false, confidence: 0.96, detectedElements }; } } // 2. 检测失效页面特征(增强版) const invalidTextPatterns = [ '分享链接已失效', '分享链接不存在', '分享已过期', '链接已失效', '文件不存在', '文件已被删除', '分享不存在', '资源不存在', 'share not found', 'file not found', 'link expired' ]; for (const pattern of invalidTextPatterns) { if (text.includes(pattern)) { detectedElements.push(`失效文本: ${pattern}`); return { status: '失效页面', isValid: false, needsPassword: false, confidence: 0.95, detectedElements }; } } // 检测失效页面的CSS类名和元素 const invalidSelectors = ['.shareWeb404', '.error-page', '.not-found', '.expired']; for (const selector of invalidSelectors) { const element = body.querySelector(selector); if (element && checkElementVisibility(element)) { detectedElements.push(`失效元素: ${selector}`); return { status: '失效页面', isValid: false, needsPassword: false, confidence: 0.95, detectedElements }; } } // 3. 检测提取码输入页面 const passwordInput = body.querySelector('input[type="password"]'); if (passwordInput && checkElementVisibility(passwordInput)) { detectedElements.push('密码输入框'); return { status: '提取码输入页面', isValid: true, needsPassword: true, confidence: 0.92, detectedElements }; } if (text.includes('请输入提取码')) { detectedElements.push('提取码提示文本'); return { status: '提取码输入页面', isValid: true, needsPassword: true, confidence: 0.90, detectedElements }; } // 4. 检测文件列表页面(多重特征评分) let fileListScore = 0; const fileListFeatures = [ { selector: '.file-list', score: 0.4, name: 'file-list元素' }, { selector: '.ant-table', score: 0.4, name: 'ant-table元素' }, { selector: '.ant-table-wrapper', score: 0.4, name: 'ant-table-wrapper元素' }, { selector: '.file-item', score: 0.3, name: 'file-item元素' }, { text: '文件大小', score: 0.3, name: '文件大小文本' }, { text: '修改时间', score: 0.3, name: '修改时间文本' }, { text: '下载', score: 0.2, name: '下载文本' }, { text: '预览', score: 0.2, name: '预览文本' }, { text: '保存至云盘', score: 0.3, name: '保存至云盘文本' }, { text: '浏览器下载', score: 0.3, name: '浏览器下载文本' }, { text: '文件列表', score: 0.3, name: '文件列表文本' } ]; for (const feature of fileListFeatures) { if (feature.selector) { const element = body.querySelector(feature.selector); if (element && checkElementVisibility(element)) { fileListScore += feature.score; detectedElements.push(feature.name); } } else if (feature.text && text.includes(feature.text)) { fileListScore += feature.score; detectedElements.push(feature.name); } } if (fileListScore >= 0.6) { return { status: '文件列表页面', isValid: true, needsPassword: false, confidence: Math.min(0.95, 0.7 + fileListScore * 0.2), detectedElements }; } // 5. 检测其他有效页面特征 const contentLayoutPage = body.querySelector('.content-layout-page'); if (contentLayoutPage && checkElementVisibility(contentLayoutPage)) { detectedElements.push('content-layout-page元素'); return { status: '有效页面(布局)', isValid: true, needsPassword: false, confidence: 0.85, detectedElements }; } // 6. 默认状态 return { status: '未知状态', isValid: false, needsPassword: false, confidence: 0.3, detectedElements: ['无关键元素'] }; } // 增强页面分析 - 高精度多层检测(优化版) function enhancedPageAnalysis(html, doc) { const htmlLower = html.toLowerCase(); const textContent = doc.body ? doc.body.textContent : ''; const detectedElements = []; Log.debug('增强分析开始,HTML长度:', html.length, '文本长度:', textContent.length); // 1. 优先检测最明确的有效页面特征(提升精度) // 检测"提取文件"按钮 - 最可靠的有效标志 if (htmlLower.includes('提取文件') || htmlLower.includes('extract') || htmlLower.includes('取文件')) { detectedElements.push('提取文件按钮文本'); return { status: '有效页面(提取文件)', isValid: true, needsPassword: false, confidence: 0.98, detectedElements }; } // 2. 检测失效页面的明确特征(扩展模式) const invalidPatterns = [ '分享链接已失效', '分享链接不存在', '链接已失效', '文件不存在', '分享已过期', 'share not found', 'file not found', '404' ]; for (const pattern of invalidPatterns) { if (htmlLower.includes(pattern.toLowerCase())) { detectedElements.push(`失效特征: ${pattern}`); return { status: '失效页面', isValid: false, needsPassword: false, confidence: 0.95, detectedElements }; } } // 3. 检测密码输入页面(增强精度) const passwordPatterns = [ '请输入提取码', '输入提取码', '提取码', 'access code', 'password' ]; for (const pattern of passwordPatterns) { if (htmlLower.includes(pattern.toLowerCase())) { detectedElements.push(`密码特征: ${pattern}`); return { status: '提取码输入页面', isValid: true, needsPassword: true, confidence: 0.92, detectedElements }; } } // 4. 检测文件列表页面的多重特征(评分系统) let fileListScore = 0; const fileListPatterns = [ { pattern: '文件大小', score: 0.3 }, { pattern: '修改时间', score: 0.3 }, { pattern: 'file-list', score: 0.4 }, { pattern: 'ant-table', score: 0.3 }, { pattern: '下载', score: 0.2 }, { pattern: '预览', score: 0.2 }, { pattern: '保存至云盘', score: 0.3 }, { pattern: 'content-layout-page', score: 0.3 } ]; for (const { pattern, score } of fileListPatterns) { if (htmlLower.includes(pattern.toLowerCase())) { fileListScore += score; detectedElements.push(`文件列表特征: ${pattern}`); } } if (fileListScore >= 0.6) { return { status: '文件列表页面', isValid: true, needsPassword: false, confidence: Math.min(0.95, 0.7 + fileListScore * 0.2), detectedElements }; } // 5. 检测其他有效页面特征 const validPatterns = [ '分享文件', '共享文件', '文件分享', 'shared file', 'file share' ]; for (const pattern of validPatterns) { if (htmlLower.includes(pattern.toLowerCase())) { detectedElements.push(`有效特征: ${pattern}`); return { status: '有效页面(分享)', isValid: true, needsPassword: false, confidence: 0.85, detectedElements }; } } // 6. 如果没有明确特征,返回null让专业检测器处理 Log.debug('增强分析未找到明确特征,交由专业检测器处理'); return null; } // 同域检测函数(一比一复刻) async function checkLinkBySameOrigin(shareUrl, extractCode = '') { return new Promise((resolve) => { let resolved = false; // 设置3秒超时(极速模式) const timeout = setTimeout(() => { if (!resolved) { resolved = true; Log.debug('同域检测超时'); resolve(null); } }, 3000); const targetUrl = extractCode ? `${shareUrl}?pwd=${extractCode}` : shareUrl; Log.debug('同域检测请求:', targetUrl); GM_xmlhttpRequest({ method: 'GET', url: targetUrl, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Referer': 'https://www.123pan.com/' }, timeout: 3000, onload: function(response) { if (resolved) return; clearTimeout(timeout); resolved = true; try { Log.debug('GM_xmlhttpRequest加载完成,状态:', response.status); if (response.status !== 200) { Log.debug('HTTP状态错误:', response.status); resolve(null); return; } const html = response.responseText; Log.debug('成功获取目标页面HTML,长度:', html.length); // 检查HTML内容是否足够 if (html.length < 100) { Log.warn('HTML内容过少,可能是重定向页面'); resolve(null); return; } // 创建虚拟DOM进行分析 const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); // 增强HTML分析 - 模拟实时检测器逻辑 const enhancedResult = enhancedPageAnalysis(html, doc); // 如果增强分析有结果,使用增强结果;否则使用专业检测器 const detectionResult = enhancedResult || detectPanPageStatus(doc); Log.debug('使用', enhancedResult ? '增强分析' : '专业检测器', '进行检测'); let isValid = detectionResult.isValid; let needsPassword = detectionResult.needsPassword; let confidence = detectionResult.confidence; Log.debug('专业页面检测结果:', { 状态: detectionResult.status, 有效: isValid, 需要密码: needsPassword, 置信度: confidence, 检测到的元素: detectionResult.detectedElements }); // 记录成功集成专业检测器的日志 Log.success('✅ 同域检测 + GM_xmlhttpRequest + 专业检测器工作正常!'); const result = { isValid, needsPassword, confidence, source: 'same_origin_xhr' }; Log.debug(`同域检测完成: 有效=${isValid}, 需密码=${needsPassword}, 置信度=${confidence}`); // 记录检测成功统计 const successLog = `📊 同域检测成功 | 状态: ${detectionResult.status} | 置信度: ${confidence} | 元素: ${detectionResult.detectedElements.join(',')}`; Log.info(successLog); resolve(result); } catch (error) { Log.debug('同域检测解析失败:', error.message); resolve(null); } }, onerror: function(error) { if (!resolved) { clearTimeout(timeout); resolved = true; Log.debug('GM_xmlhttpRequest请求失败:', error); Log.info('⚠️ 网络请求失败,同域检测不可用'); resolve(null); } }, ontimeout: function() { if (!resolved) { clearTimeout(timeout); resolved = true; Log.debug('GM_xmlhttpRequest请求超时'); resolve(null); } } }); }); } // 判断是否为同域链接 function isSameOriginLink(url) { try { const urlObj = new URL(url); const hostname = urlObj.hostname.toLowerCase(); // 检查是否为123网盘域名 return SAME_ORIGIN_CONFIG.domains.some(domain => hostname.includes(domain)); } catch (e) { return false; } } // 智能检测策略:综合多种方案(一比一复刻核心逻辑) async function smartDetectLink(shareUrl, extractCode = '') { try { Log.group('智能检测'); Log.info('开始检测:', shareUrl); // 精简检测策略,同域优先 const strategies = []; // 优先级1: 同域检测(123网盘链接可尝试) Log.debug('检查是否可使用同域检测:', shareUrl); const canUseSameOrigin = isSameOriginLink(shareUrl); Log.info('同域检测可用:', canUseSameOrigin); if (canUseSameOrigin) { strategies.push({ name: 'same_origin', func: checkLinkBySameOrigin }); Log.info('✅ 123网盘链接,使用GM_xmlhttpRequest进行同域检测'); } else { Log.info('❌ 非123网盘链接,跳过同域检测'); } // 优先级2: 传统页面检测作为兜底 strategies.push({ name: 'traditional', func: checkLinkByTraditional }); // 依次执行检测策略 for (const strategy of strategies) { try { Log.debug(`尝试检测策略: ${strategy.name}`); const result = await strategy.func(shareUrl, extractCode); if (result && result.confidence > 0.5) { Log.success(`✅ ${strategy.name} 检测成功:`, result); Log.groupEnd(); return result; } else { Log.debug(`⚠️ ${strategy.name} 检测失败或置信度过低`); } } catch (error) { Log.error(`❌ ${strategy.name} 检测出错:`, error.message); } } // 所有策略都失败 Log.warn('⚠️ 所有检测策略都失败'); Log.groupEnd(); return { isValid: false, needsPassword: false, confidence: 0.1, source: 'fallback' }; } catch (error) { Log.error('智能检测出错:', error); Log.groupEnd(); return null; } } // 传统检测方法(增强准确性) async function checkLinkByTraditional(shareUrl, extractCode = '') { return new Promise((resolve) => { const fullUrl = extractCode ? `${shareUrl}?pwd=${extractCode}` : shareUrl; GM_xmlhttpRequest({ method: 'GET', url: fullUrl, timeout: 3000, // 极速3秒超时 headers: { 'User-Agent': USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)], 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Referer': 'https://www.123pan.com/' }, onload: function(response) { try { Log.debug('传统检测响应状态:', response.status); if (response.status !== 200) { resolve({ isValid: false, needsPassword: false, confidence: 0.8, source: 'traditional_xhr_error' }); return; } const html = response.responseText; Log.debug('传统检测HTML长度:', html.length); // 使用增强页面分析 const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const enhancedResult = enhancedPageAnalysis(html, doc); if (enhancedResult) { Log.debug('传统检测使用增强分析结果'); resolve({ isValid: enhancedResult.isValid, needsPassword: enhancedResult.needsPassword, confidence: Math.max(0.6, enhancedResult.confidence - 0.1), // 略降置信度 source: 'traditional_enhanced' }); } else { // 回退到基础分析 const status = analyzePageStatus(html, response.status, true); resolve({ isValid: status === 'valid', needsPassword: status === 'encrypted', confidence: status === 'valid' ? 0.7 : (status === 'invalid' ? 0.8 : 0.3), source: 'traditional_basic' }); } } catch (error) { Log.error('传统检测解析失败:', error); resolve(null); } }, onerror: () => { Log.debug('传统检测网络错误'); resolve(null); }, ontimeout: () => { Log.debug('传统检测超时'); resolve(null); } }); }); } // ==================== 同域检测功能模块 ==================== // 初始化同域检测 function initSameDomainDetection() { console.log('启动同域检测功能'); // 创建同域检测器 GlobalState.sameDomainDetector = new SameDomainDetector(); // 实时检测已在startUnifiedDetection中启动,无需重复 } // 同域检测器类 class SameDomainDetector { constructor() { this.detectedLinks = new Map(); this.isDetecting = false; } // 检测单个链接(供外部调用) async detectSingleLink(linkElement) { if (!linkElement) return; const linkInfo = { url: linkElement.href, element: linkElement, isCurrentPage: linkElement.href === window.location.href }; console.log(`同域检测单个链接: ${linkInfo.url}`); await this.detectLink(linkInfo); } // 检测当前页面状态(实时页面元素检测) detectCurrentPageStatus() { console.log('实时页面元素检测'); // 直接分析当前页面DOM元素 const currentStatus = this.analyzeCurrentPage(); console.log(`当前页面状态: ${currentStatus}`); // 更新当前页面状态显示 this.updateCurrentPageStatus(currentStatus); return currentStatus; } // 更新当前页面状态显示 updateCurrentPageStatus(status) { // 查找或创建页面状态指示器 let indicator = document.querySelector('.current-page-status'); if (!indicator) { indicator = document.createElement('div'); indicator.className = 'current-page-status'; indicator.style.cssText = ` position: fixed; top: 60px; right: 20px; z-index: 9998; padding: 4px 8px; border-radius: 4px; font-size: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); `; document.body.appendChild(indicator); } indicator.textContent = `页面状态: ${this.getStatusText(status)}`; indicator.style.cssText += this.getStatusStyle(status); indicator.title = `实时检测: ${this.getStatusTitle(status)}`; } // 检测链接是否需要同域处理 shouldUseSameDomainDetection(url) { try { const urlObj = new URL(url); const currentHost = window.location.hostname.toLowerCase(); const targetHost = urlObj.hostname.toLowerCase(); // 同域且都是123网盘域名 return currentHost === targetHost && /123[a-z0-9]*\.com$/.test(currentHost); } catch (e) { return false; } } // 检测单个链接(同域优化版) async detectLink(linkInfo) { const { url, element, isCurrentPage } = linkInfo; try { console.log(`同域检测链接: ${url}`); // 同域链接直接分析当前页面DOM if (this.shouldUseSameDomainDetection(url)) { const status = this.analyzeCurrentPage(); this.updateLinkStatus(url, status, element); console.log(`同域检测完成: ${url} -> ${status}`); return; } // 非同域链接标记为需要跨域检测 console.log(`非同域链接,需要跨域检测: ${url}`); this.updateLinkStatus(url, 'unknown', element); } catch (error) { console.error(`同域检测失败: ${url}`, error); this.updateLinkStatus(url, 'error', element); } } // 分析当前页面状态 analyzeCurrentPage() { const bodyText = document.body.textContent; return analyzePageStatus(bodyText, 200, true); } // 更新链接状态 updateLinkStatus(url, status, element) { this.detectedLinks.set(url, status); if (element) { // 为链接添加状态标记 this.addStatusIndicator(element, status); } console.log(`链接状态更新: ${url} -> ${status}`); } // 添加状态指示器 addStatusIndicator(element, status) { // 移除现有状态指示器 const existing = element.parentNode.querySelector('.same-domain-status'); if (existing) { existing.remove(); } // 创建新的状态指示器 const indicator = document.createElement('span'); indicator.className = 'same-domain-status'; indicator.textContent = this.getStatusText(status); indicator.style.cssText = ` margin-left: 6px; font-size: 10px; padding: 1px 4px; border-radius: 2px; ${this.getStatusStyle(status)} `; indicator.title = `同域检测: ${this.getStatusTitle(status)}`; element.parentNode.insertBefore(indicator, element.nextSibling); } // 获取状态文本 getStatusText(status) { const statusMap = { 'valid': '✓同域有效', 'invalid': '✗同域失效', 'encrypted': '🔒同域加密', 'error': '⚠同域错误', 'timeout': '⏱同域超时', 'unknown': '❓同域未知' }; return statusMap[status] || '❓'; } // 获取状态样式 getStatusStyle(status) { const styleMap = { 'valid': 'color: #28a745; background: #d4edda;', 'invalid': 'color: #dc3545; background: #f8d7da;', 'encrypted': 'color: #6f42c1; background: #e2e3f0;', 'error': 'color: #868e96; background: #f8f9fa;', 'timeout': 'color: #fd7e14; background: #fff3cd;', 'unknown': 'color: #6c757d; background: #f8f9fa;' }; return styleMap[status] || 'color: #6c757d; background: #f8f9fa;'; } // 获取状态标题 getStatusTitle(status) { const titleMap = { 'valid': '同域检测有效', 'invalid': '同域检测失效', 'encrypted': '同域检测需要密码', 'error': '同域检测错误', 'timeout': '同域检测超时', 'unknown': '同域检测未知状态' }; return titleMap[status] || '未知状态'; } // 延迟函数 delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } // 统一的检测系统(合并所有检测逻辑,避免重复) function startUnifiedDetection() { if (GlobalState.detectionTimer) { clearInterval(GlobalState.detectionTimer); } Log.info('启动统一检测系统'); // 统一检测循环(合并实时检测、DOM监控、同域检测) GlobalState.detectionTimer = setInterval(() => { try { // 扫描新出现的链接(防重复) const currentLinks = findPanLinks(); const newLinks = currentLinks.filter(link => !GlobalState.checkedLinks.has(normalizeUrl(link.href)) ); if (newLinks.length > 0) { Log.info(`发现 ${newLinks.length} 个新链接`); // 极速模式:立即并发检测所有新链接 newLinks.forEach(checkLink); } // 同域页面状态检测(仅在123网盘) if (GlobalState.sameDomainDetector && isOn123PanSite()) { GlobalState.sameDomainDetector.detectCurrentPageStatus(); } } catch (error) { Log.error('统一检测出错:', error); } }, CONFIG.detectionInterval); // 同时启动DOM变化监听(替代多个观察器) startDOMChangeObserver(); Log.success('统一检测系统已启动'); } // 统一的DOM变化观察器(替代多个分散的观察器) function startDOMChangeObserver() { let lastScanTime = 0; const SCAN_COOLDOWN = 1000; // 1秒冷却时间,适合多链接页面 const unifiedObserver = new MutationObserver((mutations) => { // 检查变化是否由脚本自身引起 - 修复误判逻辑 const isScriptChange = mutations.some(mutation => { return Array.from(mutation.addedNodes).some(node => { return node.nodeType === 1 && (node.classList?.contains('link-status') || node.classList?.contains('same-domain-status') || node.classList?.contains('current-page-status')); }); }); // 检查链接删除,同步清理状态标记 mutations.forEach(mutation => { Array.from(mutation.removedNodes).forEach(node => { if (node.nodeType === 1) { // 如果删除的是链接,清理对应状态标记 if (node.tagName === 'A' && isPanLink(node.href)) { const normalizedUrl = normalizeUrl(node.href); clearLinkStatus(normalizedUrl); GlobalState.checkedLinks.delete(normalizedUrl); } // 检查删除节点的子链接 const innerLinks = node.querySelectorAll ? node.querySelectorAll('a[href]') : []; innerLinks.forEach(link => { if (isPanLink(link.href)) { const normalizedUrl = normalizeUrl(link.href); clearLinkStatus(normalizedUrl); GlobalState.checkedLinks.delete(normalizedUrl); } }); } }); }); if (isScriptChange) return; // 冷却时间检查 const now = Date.now(); if (now - lastScanTime < SCAN_COOLDOWN) return; // 检查新增的链接 const newLinks = []; mutations.forEach(mutation => { Array.from(mutation.addedNodes).forEach(node => { if (node.nodeType === 1) { // 检查节点本身是否是链接 if (node.tagName === 'A' && isPanLink(node.href)) { const normalizedUrl = normalizeUrl(node.href); if (!GlobalState.checkedLinks.has(normalizedUrl)) { newLinks.push(node); } } // 检查节点内部的链接 const innerLinks = node.querySelectorAll ? node.querySelectorAll('a[href]') : []; innerLinks.forEach(link => { if (isPanLink(link.href)) { const normalizedUrl = normalizeUrl(link.href); if (!GlobalState.checkedLinks.has(normalizedUrl)) { newLinks.push(link); } } }); // 检查文本节点中的链接 - 极速处理 const text = node.textContent || ''; if (CONFIG.linkPattern.test(text)) { // 立即处理,无延迟 const textNodes = getTextNodes(node); textNodes.forEach(textNode => { const matches = textNode.textContent.match(CONFIG.linkPattern); if (matches) { matches.forEach(url => { const normalizedUrl = normalizeUrl(url); if (!GlobalState.checkedLinks.has(normalizedUrl)) { const clickableLink = createClickableLink(url, textNode); if (clickableLink) { newLinks.push(clickableLink); } } }); } }); } } }); }); // 批量检测新发现的链接 if (newLinks.length > 0) { lastScanTime = now; Log.info(`DOM变化发现 ${newLinks.length} 个新链接`); // 极速模式:立即检测所有新链接,无延迟 newLinks.forEach(checkLink); } }); unifiedObserver.observe(document.body, { childList: true, subtree: true }); Log.success('DOM变化观察器已启动'); } // 定期清理机制(防内存泄漏)- 改为智能清理 function startPeriodicCleanup() { setInterval(() => { try { // 清理过期的日志缓存 if (Log._lastMessages && Log._lastMessages.size > 30) { const now = Date.now(); for (const [key, timestamp] of Log._lastMessages.entries()) { if (now - timestamp > 300000) { // 5分钟过期 Log._lastMessages.delete(key); } } } // 清理过期的链接缓存 const now = Date.now(); let cleanedCount = 0; for (const [url, cache] of GlobalState.linkCache.entries()) { if (now - cache.timestamp > CONFIG.cacheExpireTime) { GlobalState.linkCache.delete(url); cleanedCount++; } } // 智能清理孤立的状态标记(与链接生命周期同步) clearOrphanedStatus(); if (cleanedCount > 0) { Log.debug(`清理了 ${cleanedCount} 个过期缓存`); } } catch (error) { Log.error('定期清理出错:', error); } }, 60000); // 1分钟清理一次,频率提高以保持同步 Log.debug('智能清理机制已启动'); } // 停止实时检测(已集成到cleanup中,此函数保留备用) function stopRealtimeDetection() { if (GlobalState.detectionTimer) { clearInterval(GlobalState.detectionTimer); GlobalState.detectionTimer = null; Log.debug('停止实时检测'); } } // 统一的提取码查找函数(消除重复逻辑) function findExtractCodeNearElement(element) { try { // 1. 从元素本身查找 if (element.textContent) { const match = element.textContent.match(CONFIG.extractCodeRegex); if (match && match[1]) { console.log(`从元素找到提取码: ${match[1]}`); return match[1]; } } // 2. 从父元素查找 const parent = element.parentElement; if (parent) { const parentText = parent.textContent; const match = parentText.match(CONFIG.extractCodeRegex); if (match && match[1]) { console.log(`从父元素找到提取码: ${match[1]}`); return match[1]; } } // 3. 从祖先元素查找 let ancestor = parent; for (let i = 0; i < 3 && ancestor; i++) { ancestor = ancestor.parentElement; if (ancestor) { const ancestorText = ancestor.textContent; const match = ancestorText.match(CONFIG.extractCodeRegex); if (match && match[1]) { console.log(`从祖先元素找到提取码: ${match[1]}`); return match[1]; } } } // 4. 查找相邻元素(仅对文本节点) if (element.nodeType === Node.TEXT_NODE && parent) { const siblings = Array.from(parent.childNodes || []); const nodeIndex = siblings.indexOf(element); for (let i = Math.max(0, nodeIndex - 2); i <= Math.min(siblings.length - 1, nodeIndex + 2); i++) { const sibling = siblings[i]; if (sibling && sibling.nodeType === Node.TEXT_NODE && sibling !== element) { const match = sibling.textContent.match(CONFIG.extractCodeRegex); if (match && match[1]) { console.log(`从相邻节点找到提取码: ${match[1]}`); return match[1]; } } } } return null; } catch (e) { console.warn('查找提取码时出错:', e); return null; } } // 增强现有的a标签链接,自动附加提取码 function enhanceExistingLink(linkElement) { try { const originalUrl = linkElement.href; // 如果链接已经包含密码参数,跳过 if (originalUrl.includes('pwd=')) { return; } // 查找周围的提取码 const extractCode = findExtractCodeNearElement(linkElement); if (extractCode) { const separator = originalUrl.includes('?') ? '&' : '?'; const enhancedUrl = `${originalUrl}${separator}pwd=${extractCode}`; // 更新链接href linkElement.href = enhancedUrl; console.log(`为现有链接附加提取码: ${enhancedUrl}`); // 添加视觉提示 linkElement.title = `链接已自动附加提取码: ${extractCode}`; linkElement.style.borderBottom = '2px dotted #28a745'; } } catch (e) { console.warn('增强链接时出错:', e); } } // 智能清理孤立状态标记(只清理没有对应链接的标记) function clearOrphanedStatus() { const existingLinks = new Set(); // 收集所有存在的链接 document.querySelectorAll('a[href]').forEach(link => { if (isPanLink(link.href)) { existingLinks.add(normalizeUrl(link.href)); } }); // 清理没有对应链接的状态标记 let cleanedCount = 0; GlobalState.linkStatusMap.forEach((element, url) => { if (!existingLinks.has(url)) { if (element && element.parentNode) { element.remove(); } GlobalState.linkStatusMap.delete(url); GlobalState.checkedLinks.delete(url); cleanedCount++; } }); if (cleanedCount > 0) { Log.debug(`清理了 ${cleanedCount} 个孤立状态标记`); } } // 精简的独立扫描函数(不依赖控制面板) function scanLinks() { if (GlobalState.isScanning) { console.log('扫描进行中,跳过'); return; } GlobalState.isScanning = true; console.log('🔍 开始独立扫描'); try { // 在123网盘站点优先使用同域检测 if (isOn123PanSite() && GlobalState.sameDomainDetector) { console.log('使用同域页面检测'); GlobalState.sameDomainDetector.detectCurrentPageStatus(); GlobalState.isScanning = false; return; } // 查找所有链接 const links = findPanLinks(); const newLinks = links.filter(link => !GlobalState.checkedLinks.has(normalizeUrl(link.href))); if (newLinks.length === 0) { GlobalState.isScanning = false; console.log('没有发现新链接'); return; } console.log(`发现 ${newLinks.length} 个新链接,极速并发检测`); // 极速模式:立即并发检测所有新链接 newLinks.forEach(checkLink); } catch (error) { console.error('扫描链接时出错:', error); GlobalState.isScanning = false; } } // 极速检测函数 - 缓存优先 + 无限并发 async function checkLink(linkElement) { // 无限并发模式:移除并发限制,所有链接立即处理 const linkUrl = linkElement.href; const normalizedUrl = normalizeUrl(linkUrl); // 防重复检测 if (GlobalState.checkedLinks.has(normalizedUrl)) { return; } // 🎯 优先检查缓存 const cached = GlobalState.getCachedResult(normalizedUrl); if (cached) { updateLinkStatus(linkElement, cached.status); GlobalState.checkedLinks.add(normalizedUrl); Log.debug(`缓存命中: ${cached.status}`); return; } GlobalState.checkedLinks.add(normalizedUrl); updateLinkStatus(linkElement, 'processing'); // 无限并发模式:不需要计数activeRequests try { // 获取提取码 const extractCode = getExtractCode(linkElement); // 使用智能检测系统 const result = await smartDetectLink(linkUrl, extractCode); if (result) { // 转换检测结果为状态 let status; if (result.isValid) { status = result.needsPassword ? 'encrypted' : 'valid'; } else { status = 'invalid'; } // 💾 缓存高置信度结果 if (result.confidence >= 0.7) { GlobalState.setCachedResult(normalizedUrl, status, result.source || 'smart'); } // 多链接场景显示进度 const checkedCount = GlobalState.checkedLinks.size; Log.debug(`检测完成 (${checkedCount}): ${status} (${result.confidence})`); updateLinkStatus(linkElement, status); } else { Log.debug('检测失败'); updateLinkStatus(linkElement, 'error'); } } catch (error) { Log.error('检测出错:', error); updateLinkStatus(linkElement, 'error'); } // 无限并发模式:不需要减少activeRequests计数 // 检查扫描是否完成 checkScanComplete(); } // 检查扫描是否完成(无限并发版) function checkScanComplete() { // 无限并发模式:扫描完成由其他逻辑控制,这里只记录统计信息 const totalChecked = GlobalState.checkedLinks.size; const cacheHits = GlobalState.linkCache.size; Log.debug(`当前进度 - 已检测: ${totalChecked} 个链接, 缓存: ${cacheHits} 个`); } // 统一的页面状态分析函数(消除重复逻辑) function analyzePageStatus(content, statusCode = 200, isTextContent = true) { const text = isTextContent ? content.toLowerCase() : content; // 检测有效标识 if (text.includes('content-layout-page') || text.includes('file-list-container') || text.includes('ant-table-wrapper') || text.includes('保存至云盘') || text.includes('浏览器下载') || text.includes('文件列表')) { console.log('检测到有效页面标识'); return 'valid'; } // 检测文件信息标识 if ((text.includes('修改时间') && text.includes('大小')) || (text.includes('共') && text.includes('项') && !text.includes('共0项'))) { console.log('检测到文件信息'); return 'valid'; } // 检测明确的失效标识 if (text.includes('分享链接已失效') || text.includes('分享页面不存在') || text.includes('sharewebloading') || text.includes('文件已被删除') || text.includes('分享已过期') || text.includes('链接失效') || text.includes('链接已失效') || text.includes('分享不存在') || text.includes('资源不存在')) { console.log('检测到失效页面'); return 'invalid'; } // 检测加密状态 if (text.includes('请输入提取码') || text.includes('提取码错误') || text.includes('密码错误')) { console.log('检测到加密页面'); return 'encrypted'; } // 基于内容长度和结构判断(仅对文本内容) if (isTextContent && statusCode === 200) { if (text.length > 15000 || text.includes('ant-table')) { console.log('基于内容判断为有效'); return 'valid'; } if (text.length < 3000) { console.log('内容过短,可能失效'); return 'invalid'; } // 中等长度内容,尝试更多特征检测 if (text.includes('123pan') || text.includes('网盘') || text.includes('分享')) { console.log('基于关键词判断为有效'); return 'valid'; } } // HTTP错误状态 if (statusCode >= 400) { console.log('HTTP错误状态,判断为失效'); return 'invalid'; } console.log('无法确定状态,默认为有效'); return 'valid'; // 默认为有效,避免过多unknown状态 } // 统一的状态显示(确保唯一标记) function updateLinkStatus(linkElement, status) { const normalizedUrl = normalizeUrl(linkElement.href); // 检查是否已有状态标记 if (GlobalState.linkStatusMap.has(normalizedUrl)) { const existingElement = GlobalState.linkStatusMap.get(normalizedUrl); if (existingElement && existingElement.parentNode) { // 直接更新现有标记 existingElement.textContent = getStatusText(status); existingElement.style.cssText = `margin-right: 6px; font-size: 11px; ${STATUS_STYLES[status]}`; existingElement.title = getStatusTitle(status); return; } // 清理无效引用 GlobalState.linkStatusMap.delete(normalizedUrl); } // 彻底清除该URL的所有状态标记 clearLinkStatus(normalizedUrl); // 创建新的状态标记 const statusElement = document.createElement('span'); statusElement.className = 'link-status'; statusElement.style.cssText = `margin-right: 6px; font-size: 11px; ${STATUS_STYLES[status]}`; statusElement.textContent = getStatusText(status); statusElement.title = getStatusTitle(status); statusElement.dataset.normalizedUrl = normalizedUrl; // 统一插入位置:链接最前方(现在都是真实a标签) if (linkElement.parentNode) { linkElement.parentNode.insertBefore(statusElement, linkElement); } // 记录状态元素 GlobalState.linkStatusMap.set(normalizedUrl, statusElement); } // 清除指定URL的所有状态标记(使用标准化URL) function clearLinkStatus(normalizedUrl) { // 清除记录中的状态 if (GlobalState.linkStatusMap.has(normalizedUrl)) { const element = GlobalState.linkStatusMap.get(normalizedUrl); if (element && element.parentNode) { element.remove(); } GlobalState.linkStatusMap.delete(normalizedUrl); } // 清除页面中所有相关状态标记 document.querySelectorAll('.link-status').forEach(el => { if (el.dataset.normalizedUrl === normalizedUrl) { el.remove(); } }); } // 精简的状态文本 function getStatusText(status) { const statusMap = { 'valid': '✔ 有效', 'invalid': '✖ 失效', 'processing': '⏳ 检测中', 'encrypted': '🔒 需密码', 'error': '⚠ 错误', 'unknown': '❓ 未知' }; return statusMap[status] || '❓ 未知'; } function getStatusTitle(status) { const titleMap = { 'valid': '链接有效', 'invalid': '链接失效', 'processing': '检测中...', 'encrypted': '需要提取码', 'error': '检测错误', 'unknown': '状态未知' }; return titleMap[status] || '未知状态'; } // 精简的提取码获取 function getExtractCode(linkElement) { // 从URL参数获取 try { const url = new URL(linkElement.href); if (url.searchParams.has('pwd')) { return url.searchParams.get('pwd'); } } catch (e) {} // 从周围文本获取 const parent = linkElement.closest('div') || linkElement.parentElement; if (parent) { const match = parent.textContent.match(CONFIG.extractCodeRegex); if (match && match[1]) { return match[1]; } } return null; } // 自动填充提取码功能 function initAutoFillExtractCode() { console.log('启动自动填充提取码功能'); // 查找密码输入框 const passwordInput = findPasswordInput(); if (!passwordInput) { console.log('未找到密码输入框,5秒后重试'); setTimeout(initAutoFillExtractCode, 5000); return; } // 查找提取码 const extractCode = findExtractCodeFromPage(); if (!extractCode) { console.log('未找到提取码'); return; } // 自动填充 autoFillPassword(passwordInput, extractCode); } // 查找密码输入框 function findPasswordInput() { // 常见的密码输入框选择器 const selectors = [ 'input[type="password"]', 'input[placeholder*="密码"]', 'input[placeholder*="提取码"]', 'input[placeholder*="访问密码"]', '.ant-input[type="password"]', '.password-input', '#password', '.pwd-input' ]; for (const selector of selectors) { const input = document.querySelector(selector); if (input && input.offsetParent !== null) { // 确保元素可见 console.log(`找到密码输入框: ${selector}`); return input; } } return null; } // 从页面查找提取码 function findExtractCodeFromPage() { // 从当前页面URL获取 try { const urlParams = new URLSearchParams(window.location.search); if (urlParams.has('pwd')) { const code = urlParams.get('pwd'); console.log(`从URL获取提取码: ${code}`); return code; } } catch (e) {} // 从页面文本中提取 const pageText = document.body.textContent || ''; const match = pageText.match(CONFIG.extractCodeRegex); if (match && match[1]) { console.log(`从页面文本获取提取码: ${match[1]}`); return match[1]; } // 从来源页面获取(通过document.referrer) if (document.referrer) { try { const referrerUrl = new URL(document.referrer); if (referrerUrl.hostname.includes('pan1.me')) { // 如果是从pan1.me跳转来的,尝试从referrer获取提取码 console.log('来源页面是pan1.me,尝试获取提取码'); return null; // 这里可以进一步扩展 } } catch (e) {} } return null; } // 自动填充密码 function autoFillPassword(input, password) { console.log(`准备填充提取码: ${password}`); try { // 聚焦输入框 input.focus(); // 清空现有内容 input.value = ''; // 填充密码 input.value = password; // 触发输入事件(兼容React等框架) const events = ['input', 'change', 'keyup']; events.forEach(eventType => { const event = new Event(eventType, { bubbles: true }); input.dispatchEvent(event); }); console.log('提取码填充完成'); // 查找并点击确认按钮 setTimeout(() => { const submitButton = findSubmitButton(); if (submitButton) { console.log('找到确认按钮,准备自动提交'); submitButton.click(); console.log('已自动点击确认按钮'); } else { console.log('未找到确认按钮'); } }, 500); } catch (error) { console.error('填充提取码时发生错误:', error); } } // 查找提交按钮 function findSubmitButton() { const selectors = [ 'button[type="submit"]', 'button:contains("确认")', 'button:contains("提交")', 'button:contains("访问")', '.ant-btn-primary', '.submit-btn', '.confirm-btn' ]; for (const selector of selectors) { const button = document.querySelector(selector); if (button && button.offsetParent !== null) { return button; } } // 通过按钮文本查找 const buttons = document.querySelectorAll('button'); for (const button of buttons) { const text = button.textContent.trim(); if (['确认', '提交', '访问', '确定', '进入'].includes(text)) { return button; } } return null; } // ==================== 自动回复功能模块 ==================== // 智能回复文案库(自定义+平台文案混合) const REPLY_TEXTS = { // 自定义文案(原创内容) custom: [ "好东西,先mark一下!", "这个必须收藏,感谢楼主!", "正好需要,楼主好人!", "资源不错,已下载,谢谢!", "找了很久,终于找到了!", "楼主威武,必须支持!", "这个太实用了,感谢分享!", "好资源,已保存,谢谢楼主!", "内容很棒,学习了!", "感谢楼主的无私分享!", "这个资源很给力,收藏!", "楼主辛苦了,内容很赞!", "正是我需要的,太感谢了!", "好东西要分享,支持楼主!", "资源质量很高,必须点赞!" ], // 平台常见文案(保留部分经典) platform: [ "感谢楼主分享,内容很实用,收藏了~", "这个资源看起来很棒,谢谢分享!", "刚好需要这类内容,太及时了,感谢!", "之前一直在找类似的,谢谢分享👍", "内容不错,已保存,感谢整理~", "这个很有帮助,谢谢啦!", "支持一下,内容很精彩", "感谢分享,学到了不少", "收藏了,慢慢研究,谢谢~", "内容对我很有帮助,谢谢!" ], // 简短回复(混合风格) short: [ "谢谢!", "收藏!", "赞!", "支持!", "好的!", "不错!", "感谢!", "给力!", "👍", "🔥" ], // 表情回复(增加趣味性) emoji: [ "👍👍👍", "🔥🔥🔥", "💯💯💯", "❤️❤️❤️", "🎉🎉🎉", "✨✨✨", "👏👏👏", "🙏🙏🙏" ] }; // 核心检测关键词(只保留最常用的) const REPLY_TRIGGER = '您好,本帖含有特定内容,请回复后再查看'; // 反爬虫机制配置 const ANTI_CRAWLER_CONFIG = { // 回复间隔控制(秒) minInterval: 0, // 最小间隔0秒 maxInterval: 5, // 最大间隔5秒 // 行为模拟延迟(毫秒)- 灵活模式 readingTime: [100, 500], // 模拟阅读时间 0.1-0.5秒 typingTime: [50, 200], // 模拟打字时间 0.05-0.2秒 // 频率控制 maxRepliesPerHour: 20, // 每小时最多回复20次 maxRepliesPerDay: 100, // 每天最多回复100次 // 随机行为 skipProbability: 0.1, // 10%概率跳过回复(模拟人类不总是回复) // 用户行为模拟 enableMouseMove: true, // 启用鼠标移动模拟 enableScrolling: false, // 禁用滚动模拟(不下拉页面) enableFocusBlur: true, // 启用焦点切换模拟 enableTypingSimulation: false // 禁用打字模拟(极速模式) }; // 智能回复文案选择器(防重复+混合文案) function getRandomReplyText() { const weights = { 'custom': 0.7, // 70%概率选择自定义文案(优先使用) 'platform': 0.15, // 15%概率选择平台文案 'short': 0.1, // 10%概率选择简短回复 'emoji': 0.05 // 5%概率选择表情回复 }; const history = GlobalState.replyHistory; let attempts = 0; let selectedText = ''; // 最多尝试20次找到未使用的文案 while (attempts < 20) { // 根据权重随机选择分类 const random = Math.random(); let accumulated = 0; let selectedCategory = 'custom'; for (const [category, weight] of Object.entries(weights)) { accumulated += weight; if (random <= accumulated) { selectedCategory = category; break; } } // 从选中分类随机选择文案 const texts = REPLY_TEXTS[selectedCategory]; const randomIndex = Math.floor(Math.random() * texts.length); selectedText = texts[randomIndex]; // 检查是否已使用过 if (!history.usedTexts.has(selectedText)) { // 添加到已使用列表 history.usedTexts.add(selectedText); // 限制历史记录大小 if (history.usedTexts.size > history.maxHistorySize) { const firstItem = history.usedTexts.values().next().value; history.usedTexts.delete(firstItem); } Log.info(`✅ 选择回复分类: ${selectedCategory}, 文案: "${selectedText}"`); console.log(`🎯 文案选择详情: 类别=${selectedCategory}, 权重=${weights[selectedCategory]}, 文案="${selectedText}"`); return selectedText; } attempts++; } // 如果20次都找不到未使用的文案,清空历史记录重新开始 if (attempts >= 20) { Log.info('回复文案历史记录已满,清空重新开始'); history.usedTexts.clear(); // 重新选择 const categories = Object.keys(REPLY_TEXTS); const randomCategory = categories[Math.floor(Math.random() * categories.length)]; const texts = REPLY_TEXTS[randomCategory]; selectedText = texts[Math.floor(Math.random() * texts.length)]; history.usedTexts.add(selectedText); } Log.debug(`最终选择文案: ${selectedText}`); return selectedText; } // 反爬虫检查(增加防重复机制) function shouldSkipReply() { const now = Date.now(); const stats = GlobalState.antiCrawler; const history = GlobalState.replyHistory; const currentUrl = window.location.href; // 检查是否禁用自动回复 if (!GlobalState.autoReplyEnabled) { Log.info('自动回复已禁用,跳过回复'); return true; } // 检查当前页面是否已经回复过 if (history.pageUrls.has(currentUrl)) { Log.info('当前页面已回复过,跳过重复回复'); return true; } // 随机跳过(模拟人类行为) if (Math.random() < ANTI_CRAWLER_CONFIG.skipProbability) { Log.info('随机跳过回复(模拟人类行为)'); return true; } // 检查回复间隔(0秒间隔时跳过检查) if (stats.lastReplyTime > 0 && ANTI_CRAWLER_CONFIG.minInterval > 0) { const timeSinceLastReply = now - stats.lastReplyTime; const minInterval = ANTI_CRAWLER_CONFIG.minInterval * 1000; if (timeSinceLastReply < minInterval) { Log.info(`回复间隔过短,需等待 ${Math.ceil((minInterval - timeSinceLastReply) / 1000)} 秒`); return true; } } // 检查每小时频率限制 const oneHourAgo = now - 60 * 60 * 1000; if (stats.lastReplyTime > oneHourAgo && stats.replyCount >= ANTI_CRAWLER_CONFIG.maxRepliesPerHour) { Log.info('已达到每小时回复限制'); return true; } return false; } // 用户行为模拟(无滚动版) async function simulateUserBehavior() { Log.debug('开始用户行为模拟(无滚动)'); // 简化的鼠标移动(不等待) if (ANTI_CRAWLER_CONFIG.enableMouseMove) { simulateMouseMovement(); } // 极简的阅读时间 const readingTime = randomBetween(ANTI_CRAWLER_CONFIG.readingTime[0], ANTI_CRAWLER_CONFIG.readingTime[1]); Log.debug(`阅读时间: ${readingTime}ms`); await delay(readingTime); // 焦点切换(如果启用) if (ANTI_CRAWLER_CONFIG.enableFocusBlur) { simulateFocusBlur(); } // 明确跳过页面滚动 Log.debug('用户行为模拟完成(已跳过页面滚动)'); } // 模拟鼠标移动 function simulateMouseMovement() { const moveCount = randomBetween(2, 5); for (let i = 0; i < moveCount; i++) { setTimeout(() => { const x = randomBetween(100, window.innerWidth - 100); const y = randomBetween(100, window.innerHeight - 100); const event = new MouseEvent('mousemove', { clientX: x, clientY: y, bubbles: true }); document.dispatchEvent(event); }, i * randomBetween(200, 800)); } Log.debug('模拟鼠标移动完成'); } // 模拟页面滚动(已禁用) async function simulateScrolling() { // 滚动功能已禁用,不执行任何滚动操作 Log.debug('页面滚动已禁用,跳过滚动模拟'); return; } // 模拟焦点切换 function simulateFocusBlur() { // 模拟窗口失焦和重新获得焦点 window.dispatchEvent(new Event('blur')); setTimeout(() => { window.dispatchEvent(new Event('focus')); }, randomBetween(100, 500)); Log.debug('模拟焦点切换完成'); } // 生成随机数 function randomBetween(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } // 延迟函数 function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 极速内容检测(最优化) function needsReply() { // 使用innerHTML是最快的文本搜索方式 return document.body.innerHTML.includes('本帖含有特定内容,请回复后再查看'); } // 独立的实时回复检测系统(不依赖控制面板) function startRealtimeReplyDetection() { console.log('🚀 启动独立实时回复检测系统'); // 立即检测一次 if (needsReply() && !GlobalState.replyState.tried) { console.log('🔍 立即发现回复触发条件'); initAutoReply(); } // 创建实时监控定时器 const replyDetectionTimer = setInterval(() => { try { // 如果已经尝试过回复,停止检测 if (GlobalState.replyState.tried) { clearInterval(replyDetectionTimer); console.log('✅ 回复检测完成,停止监控'); return; } // 检测回复触发条件 if (needsReply()) { console.log('🔍 实时检测到回复触发条件'); clearInterval(replyDetectionTimer); initAutoReply(); } } catch (error) { console.error('实时回复检测出错:', error); } }, 500); // 500ms检测间隔,确保快速响应 // DOM变化监听器 - 立即响应新内容 const replyObserver = new MutationObserver((mutations) => { // 如果已经尝试过回复,停止监听 if (GlobalState.replyState.tried) { replyObserver.disconnect(); return; } mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { const text = node.textContent || ''; if (text.includes('本帖含有特定内容,请回复后再查看')) { console.log('🔍 DOM监听检测到回复触发条件'); replyObserver.disconnect(); clearInterval(replyDetectionTimer); initAutoReply(); } } }); } }); }); // 开始监听DOM变化 replyObserver.observe(document.body, { childList: true, subtree: true }); console.log('✅ 实时回复检测系统已启动'); } // 智能回复初始化(集成反爬虫机制) async function initAutoReply() { if (GlobalState.replyState.tried || GlobalState.replyState.inProgress) return; if (!needsReply()) { GlobalState.replyState.tried = true; return; } // 反爬虫检查 if (shouldSkipReply()) { GlobalState.replyState.tried = true; return; } console.log('🤖 智能回复启动(反爬虫模式)'); GlobalState.replyState.tried = true; GlobalState.replyState.inProgress = true; try { // 模拟用户行为 await simulateUserBehavior(); // 添加0-5秒的随机延迟 const randomDelay = randomBetween(0, ANTI_CRAWLER_CONFIG.maxInterval * 1000); if (randomDelay > 0) { Log.debug(`随机延迟: ${randomDelay}ms`); await delay(randomDelay); } // 执行回复 await executeAutoReply(); } catch (error) { Log.error('自动回复执行失败:', error); GlobalState.replyState.inProgress = false; } } // 增强的回复元素查找 function findReplyElements() { let replyBox, submitBtn; // 优先查找文本输入框(避免快速回复) const textInputSelectors = [ 'textarea[name="message"]', 'textarea[name="content"]', 'textarea[placeholder*="回复"]', 'textarea[placeholder*="内容"]', 'textarea', 'input[type="text"][name="message"]', 'input[type="text"][name="content"]' ]; // 快速回复选择器(备用) const quickReplySelectors = [ 'select[name="quick_reply_message"]', 'select[name="message"]', 'select' ]; // 优先查找文本输入框 for (const selector of textInputSelectors) { const element = document.querySelector(selector); if (element && element.offsetParent !== null) { replyBox = element; Log.debug('找到文本输入框:', selector); break; } } // 如果没找到文本框,再找快速回复框 if (!replyBox) { for (const selector of quickReplySelectors) { const element = document.querySelector(selector); if (element && element.offsetParent !== null) { replyBox = element; Log.debug('找到快速回复框:', selector); break; } } } // 查找提交按钮 const submitSelectors = [ 'button[type="submit"]', 'input[type="submit"]', 'button[value="提交"]', 'button[value="回复"]', 'button[value="发表"]' ]; for (const selector of submitSelectors) { submitBtn = document.querySelector(selector); if (submitBtn && submitBtn.offsetParent !== null) break; } // 如果没找到,尝试通过文本查找按钮 if (!submitBtn) { const buttons = document.querySelectorAll('button'); for (const btn of buttons) { if (/回复|发表|提交|发送/i.test(btn.textContent) && btn.offsetParent !== null) { submitBtn = btn; break; } } } const isQuickReply = replyBox && replyBox.tagName === 'SELECT'; Log.info('回复元素查找结果:', { replyBox: replyBox ? replyBox.tagName + (replyBox.name ? `[name="${replyBox.name}"]` : '') : '未找到', submitBtn: submitBtn ? submitBtn.textContent || submitBtn.value : '未找到', isQuickReply }); return { replyBox, submitBtn, isQuickReply }; } // 智能执行自动回复(集成反爬虫机制) async function executeAutoReply() { const { replyBox, submitBtn, isQuickReply } = findReplyElements(); if (!replyBox || !submitBtn) { console.log('❌ 未找到回复元素'); GlobalState.replyState.inProgress = false; return; } await performReply(replyBox, submitBtn, isQuickReply); } // 智能回复操作(集成反爬虫机制) async function performReply(replyBox, submitBtn, isQuickReply = false) { try { Log.info('🤖 开始智能回复过程'); // 灵活思考时间 await delay(randomBetween(50, 200)); // 强制使用我们的自定义文案,不使用平台快速回复 const replyText = getRandomReplyText(); if (isQuickReply && replyBox.tagName === 'SELECT') { // 检查是否有自定义选项,如果没有就寻找文本输入框 Log.debug('检测到快速回复框,尝试寻找文本输入框'); // 寻找文本输入框 const textInputs = [ document.querySelector('textarea[name="message"]'), document.querySelector('textarea[name="content"]'), document.querySelector('textarea'), document.querySelector('input[type="text"]'), document.querySelector('input[name="message"]') ].filter(el => el && el.offsetParent !== null); if (textInputs.length > 0) { // 找到文本输入框,使用自定义文案 const textBox = textInputs[0]; textBox.focus(); textBox.value = replyText; textBox.dispatchEvent(new Event('input', { bubbles: true })); textBox.dispatchEvent(new Event('change', { bubbles: true })); Log.info('✅ 使用文本框填充自定义文案:', replyText); } else { // 没找到文本框,随机选择快速回复选项(避免总是第一个) const options = Array.from(replyBox.options).filter((opt, idx) => idx > 0 && opt.value); // 跳过第一个空选项 if (options.length > 0) { const randomOption = options[Math.floor(Math.random() * options.length)]; replyBox.value = randomOption.value; replyBox.dispatchEvent(new Event('change', { bubbles: true })); Log.info('✅ 随机选择快速回复选项:', randomOption.textContent); } else { // 兜底:选择第二个选项(避免第一个) replyBox.selectedIndex = replyBox.options.length > 1 ? 1 : 0; replyBox.dispatchEvent(new Event('change', { bubbles: true })); Log.debug('兜底选择快速回复选项'); } } } else { // 传统文本输入框:使用智能文案选择器 if (ANTI_CRAWLER_CONFIG.enableTypingSimulation) { // 模拟逐字打字 await simulateTyping(replyBox, replyText); } else { // 极速模式:直接填充文本 replyBox.focus(); replyBox.value = replyText; replyBox.dispatchEvent(new Event('input', { bubbles: true })); replyBox.dispatchEvent(new Event('change', { bubbles: true })); Log.info('✅ 极速填充自定义文案:', replyText); } } // 灵活提交检查 const submitDelay = randomBetween(100, 300); Log.debug(`灵活提交检查: ${submitDelay}ms`); await delay(submitDelay); // 提交回复 submitBtn.click(); Log.success('✅ 智能回复已提交'); // 更新统计信息 updateReplyStats(); // 重置状态 GlobalState.replyState.inProgress = false; } catch (error) { Log.error('❌ 智能回复失败:', error); GlobalState.replyState.inProgress = false; } } // 极速打字模拟(几乎无延迟) async function simulateTyping(inputElement, text) { Log.debug(`极速打字: ${text}`); // 先聚焦到输入框 inputElement.focus(); inputElement.value = ''; // 极速模式:直接输入完整文本,只在关键点添加微小延迟 const chunks = Math.ceil(text.length / 5); // 分成5个块 for (let i = 0; i < chunks; i++) { const endIndex = Math.min((i + 1) * 5, text.length); inputElement.value = text.substring(0, endIndex); // 触发输入事件 inputElement.dispatchEvent(new Event('input', { bubbles: true })); // 每5个字符只延迟5-15ms if (i < chunks - 1) { await delay(randomBetween(5, 15)); } } // 最终触发change事件 inputElement.dispatchEvent(new Event('change', { bubbles: true })); Log.debug('极速打字完成'); } // 更新回复统计(增加页面记录) function updateReplyStats() { const now = Date.now(); const currentUrl = window.location.href; const history = GlobalState.replyHistory; // 更新回复统计 GlobalState.antiCrawler.lastReplyTime = now; GlobalState.antiCrawler.replyCount += 1; // 记录已回复的页面 history.pageUrls.add(currentUrl); // 限制页面历史记录大小 if (history.pageUrls.size > history.maxHistorySize) { const firstUrl = history.pageUrls.values().next().value; history.pageUrls.delete(firstUrl); } Log.info(`📊 回复统计更新: 总计 ${GlobalState.antiCrawler.replyCount} 次回复`); Log.debug(`🔒 页面防重复: 已记录 ${history.pageUrls.size} 个页面`); } // 温和的资源清理函数(只清理内存,保留DOM状态) function cleanup() { Log.info('清理脚本内部资源'); // 停止检测定时器 if (GlobalState.detectionTimer) { clearInterval(GlobalState.detectionTimer); GlobalState.detectionTimer = null; } // 清理同域检测器 if (GlobalState.sameDomainDetector) { GlobalState.sameDomainDetector.isDetecting = false; GlobalState.sameDomainDetector = null; } // 清理Log缓存 if (Log._lastMessages) { Log._lastMessages.clear(); } // 只清理内部状态,保留DOM元素和状态映射 GlobalState.isScanning = false; // 无限并发模式:不需要重置activeRequests GlobalState.replyState = { tried: false, inProgress: false }; // 注意:不调用GlobalState.reset(),避免清空linkStatusMap和checkedLinks // 这样状态标记可以继续显示直到链接真正消失 Log.success('内部资源清理完成,状态标记保持显示'); } // 页面卸载时清理资源 window.addEventListener('beforeunload', cleanup); // 早期回复检测(脚本加载时立即执行) (function immediateReplyDetection() { // 脚本加载时立即检测 if (document.readyState !== 'loading' && needsReply()) { console.log('⚡ 脚本加载时发现回复触发条件'); initAutoReply(); } })(); // 初始化其他功能 init(); // 验证功能独立性的测试函数 window.testRealtimeDetection = function() { console.log('🧪 测试实时检测功能'); const testResults = { realtimeDetection: !!GlobalState.detectionTimer, earlyMonitoring: true, // 已在页面加载时启动 dynamicObserver: true, // 已在init中启动 independentFromPanel: true, // 已完全独立 cacheSystem: GlobalState.linkCache.size >= 0, smartDetection: typeof smartDetectLink === 'function' }; console.log('✅ 实时检测功能测试结果:', testResults); const allPassed = Object.values(testResults).every(result => result === true); console.log(allPassed ? '🎉 所有功能正常' : '⚠️ 部分功能异常'); return testResults; }; // 测试链接检测功能 window.testLinkDetection = function(testUrl) { if (!testUrl) { testUrl = prompt('请输入测试链接:') || 'https://www.123pan.com/s/test123'; } console.log('🧪 测试链接检测:', testUrl); if (isPanLink(testUrl)) { console.log('✅ 链接格式验证通过'); // 创建测试链接元素 const testLink = document.createElement('a'); testLink.href = testUrl; testLink.textContent = testUrl; // 立即检测 checkLink(testLink); console.log('✅ 检测已启动'); } else { console.log('❌ 不是有效的123网盘链接'); } }; // 测试自动回复功能 window.testAutoReply = function() { console.log('🧪 测试智能自动回复功能'); const testResults = { realtimeDetection: typeof startRealtimeReplyDetection === 'function', replyDetection: needsReply(), replyEnabled: GlobalState.autoReplyEnabled, antiCrawlerCheck: !shouldSkipReply(), replyElements: (() => { const { replyBox, submitBtn } = findReplyElements(); return { hasReplyBox: !!replyBox, hasSubmitBtn: !!submitBtn }; })(), replyState: GlobalState.replyState, antiCrawlerStats: GlobalState.antiCrawler, smartTextSelection: typeof getRandomReplyText === 'function' }; console.log('✅ 智能自动回复功能测试结果:', testResults); if (testResults.replyDetection) { console.log('🔍 检测到回复触发条件'); if (testResults.replyEnabled && testResults.antiCrawlerCheck) { console.log('✅ 反爬虫检查通过,可以测试回复'); if (confirm('是否立即测试智能自动回复?\n\n将模拟真实用户行为:\n- 随机选择回复文案\n- 模拟鼠标移动和滚动\n- 模拟逐字打字\n- 智能延迟控制')) { GlobalState.replyState = { tried: false, inProgress: false }; initAutoReply(); } } else if (!testResults.replyEnabled) { console.log('⚠️ 自动回复已禁用,请在菜单中开启'); } else { console.log('⚠️ 反爬虫机制阻止回复(间隔限制或频率限制)'); } } else { console.log('ℹ️ 当前页面无回复触发条件'); } return testResults; }; // 测试灵活回复时间 window.testSpeedReply = function() { console.log('🚀 测试灵活回复时间'); const startTime = Date.now(); // 模拟各个步骤的时间 const readingTime = randomBetween(ANTI_CRAWLER_CONFIG.readingTime[0], ANTI_CRAWLER_CONFIG.readingTime[1]); const randomDelay = randomBetween(0, ANTI_CRAWLER_CONFIG.maxInterval * 1000); const thinkTime = randomBetween(50, 200); const submitDelay = randomBetween(100, 300); const typingTime = ANTI_CRAWLER_CONFIG.enableTypingSimulation ? randomBetween(50, 200) : 0; const totalTime = readingTime + randomDelay + thinkTime + submitDelay + typingTime; console.log('⚡ 灵活回复时间分析:'); console.log(` 阅读时间: ${readingTime}ms (${(readingTime/1000).toFixed(3)}秒)`); console.log(` 随机延迟: ${randomDelay}ms (${(randomDelay/1000).toFixed(3)}秒)`); console.log(` 思考时间: ${thinkTime}ms (${(thinkTime/1000).toFixed(3)}秒)`); console.log(` 打字时间: ${typingTime}ms (${(typingTime/1000).toFixed(3)}秒) - ${ANTI_CRAWLER_CONFIG.enableTypingSimulation ? '启用' : '禁用'}`); console.log(` 提交延迟: ${submitDelay}ms (${(submitDelay/1000).toFixed(3)}秒)`); console.log(` 📊 预计总时间: ${totalTime}ms (${(totalTime/1000).toFixed(3)}秒)`); console.log(` 🎯 目标范围: 0.1-5.0秒`); // 测试文案选择 console.log('\n📝 文案选择测试(70%自定义文案):'); for (let i = 0; i < 5; i++) { const text = getRandomReplyText(); console.log(` ${i + 1}. "${text}"`); } return { readingTime, randomDelay, thinkTime, submitDelay, typingTime, totalTime: totalTime / 1000, isWithinRange: totalTime >= 100 && totalTime <= 5000 }; }; // 测试回复元素查找 window.testReplyElements = function() { console.log('🔍 测试回复元素查找'); const { replyBox, submitBtn, isQuickReply } = findReplyElements(); console.log('📋 查找结果:'); console.log(' 回复框:', replyBox ? { 标签: replyBox.tagName, 名称: replyBox.name || '无', ID: replyBox.id || '无', 类名: replyBox.className || '无', 占位符: replyBox.placeholder || '无', 是否可见: replyBox.offsetParent !== null } : '❌ 未找到'); console.log(' 提交按钮:', submitBtn ? { 标签: submitBtn.tagName, 文本: submitBtn.textContent || submitBtn.value, 类型: submitBtn.type || '无', 是否可见: submitBtn.offsetParent !== null } : '❌ 未找到'); console.log(' 快速回复:', isQuickReply ? '是' : '否'); // 测试文案生成 if (replyBox) { console.log('\n📝 文案生成测试:'); for (let i = 0; i < 3; i++) { const text = getRandomReplyText(); console.log(` ${i + 1}. "${text}"`); } } // 显示页面上所有可能的输入元素 console.log('\n🔍 页面上所有输入元素:'); const allInputs = document.querySelectorAll('input, textarea, select'); allInputs.forEach((el, idx) => { if (el.offsetParent !== null) { console.log(` ${idx + 1}. ${el.tagName}[name="${el.name || '无'}"][type="${el.type || '无'}"] - "${el.placeholder || el.textContent?.substring(0, 20) || '无文本'}"`); } }); return { replyBox, submitBtn, isQuickReply }; }; // 测试反爬虫机制 window.testAntiCrawler = function() { console.log('🧪 测试反爬虫机制'); const config = ANTI_CRAWLER_CONFIG; const stats = GlobalState.antiCrawler; console.log('⚙️ 反爬虫配置:', config); console.log('📊 当前统计:', stats); console.log('🔄 回复状态:', GlobalState.autoReplyEnabled ? '已开启' : '已关闭'); console.log('⏰ 下次可回复时间:', stats.lastReplyTime && config.minInterval > 0 ? new Date(stats.lastReplyTime + config.minInterval * 1000).toLocaleTimeString() : '立即可用'); // 测试文案选择 console.log('📝 随机文案测试(自定义+平台混合):'); for (let i = 0; i < 5; i++) { console.log(` ${i + 1}. ${getRandomReplyText()}`); } // 显示防重复状态 const history = GlobalState.replyHistory; console.log('🔒 防重复状态:'); console.log(` 已使用文案: ${history.usedTexts.size}/${history.maxHistorySize}`); console.log(` 已回复页面: ${history.pageUrls.size}/${history.maxHistorySize}`); console.log(` 当前页面已回复: ${history.pageUrls.has(window.location.href) ? '是' : '否'}`); return { config, stats, enabled: GlobalState.autoReplyEnabled, canReply: !shouldSkipReply() }; }; console.log('🚀 123网盘链接检测助手 v4.0.0 已启动!'); console.log('⚡ 核心功能:无限并发检测、状态持久化显示、自动提取码'); console.log('🔧 技术特性:GM_xmlhttpRequest同域优化、智能缓存、实时DOM监控'); console.log('🤖 静默回复:菜单开关控制、0.1-5秒总延迟、智能防重复、70%自定义文案、无页面滚动'); console.log('💡 测试命令: testReplyElements() 或 testSpeedReply() 或 testAutoReply() 或 testAntiCrawler()'); console.log('📋 菜单功能:右键脚本图标 → 开启/关闭自动回复、查看回复统计'); console.log('ℹ️ 无UI设计,完全后台运行,支持超大批量链接场景'); })();