// ==UserScript== // @name 百度贴吧终极增强套件Pro // @namespace http://tampermonkey.net/ // @version 7.34 // @description 重构版:强制屏蔽水贴,解决逻辑冲突,优化动态DOM处理,部分来自(Grok AI)。未经许可,禁止修改或分发。 // @author YourName // @match *://tieba.baidu.com/p/* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js // @connect tieba.baidu.com // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 防伪校验 const SCRIPT_AUTH_TOKEN = 'xAI-Grok3-EnhancedTieba-20250221'; const checkAuthenticity = () => { const meta = GM_info.script; if (meta.version !== '7.34' || meta.name !== '百度贴吧终极增强套件Pro' || !meta.description.includes('xAI')) { alert('脚本可能被篡改或非正版,请从官方渠道下载!'); return false; } GM_setValue('authToken', SCRIPT_AUTH_TOKEN); return true; }; if (!checkAuthenticity()) return; const LOG_LEVEL = GM_getValue('logLevel', 'verbose'); const MAX_LOG_ENTRIES = 100; const CONFIG = { debugMode: GM_getValue('debugMode', true), defaultSettings: { filter: { hideInvalid: true, hideSpam: true, spamKeywords: ["顶", "沙发", "签到"], whitelist: [], blockedElements: [], tempBlockedElements: [], autoExpandImages: true, blockType: 'perm', blockAds: true, sortByTime: false, enhanceImages: true, linkifyVideos: true, darkMode: false, showHiddenFloors: false }, panel: { width: 320, minHeight: 100, maxHeight: '90vh', position: { x: 20, y: 20 }, scale: 1.0, minimized: true }, logPath: `tieba_enhance_log_${new Date().toISOString().replace(/[:.]/g, '-')}.txt` } }; const logBuffer = { script: [], pageState: [], pageBehavior: [], userActions: [] }; const originalConsole = { log: console.log.bind(console), warn: console.warn.bind(console), error: console.error.bind(console) }; function logWrapper(category, level, ...args) { if (!CONFIG.debugMode || (LOG_LEVEL === 'error' && level !== 'ERROR')) return; const timestamp = new Date().toISOString(); const formattedArgs = args.map(arg => { try { return typeof arg === 'object' ? JSON.stringify(arg) : arg.toString(); } catch (e) { return '[Unserializable Object]'; } }).join(' '); const message = `[${timestamp}] [${level}] ${formattedArgs}`; logBuffer[category].push(message); if (logBuffer[category].length > MAX_LOG_ENTRIES) logBuffer[category].shift(); originalConsole[level.toLowerCase()](message); } const customConsole = { log: (...args) => logWrapper('script', 'LOG', ...args), warn: (...args) => logWrapper('script', 'WARN', ...args), error: (...args) => logWrapper('script', 'ERROR', ...args) }; class PerformanceMonitor { static instance; constructor() { this.metrics = { memoryUsage: [], processSpeed: [], networkRequests: [] }; } static getInstance() { if (!PerformanceMonitor.instance) { PerformanceMonitor.instance = new PerformanceMonitor(); } return PerformanceMonitor.instance; } recordMemory() { try { if ('memory' in performance) { const used = performance.memory.usedJSHeapSize; this.metrics.memoryUsage.push(used); if (this.metrics.memoryUsage.length > 100) this.metrics.memoryUsage.shift(); logWrapper('pageState', 'LOG', `Memory recorded: ${Math.round(used / 1024 / 1024)} MB`); } } catch (e) { customConsole.error('Error in recordMemory:', e); } } recordProcessSpeed(time) { try { this.metrics.processSpeed.push(time); if (this.metrics.processSpeed.length > 100) this.metrics.processSpeed.shift(); logWrapper('pageState', 'LOG', `Process speed recorded: ${time.toFixed(2)} ms`); } catch (e) { customConsole.error('Error in recordProcessSpeed:', e); } } recordNetwork() { try { if (performance.getEntriesByType) { const requests = performance.getEntriesByType('resource'); requests.forEach(req => { if (!this.metrics.networkRequests.some(r => r.name === req.name)) { this.metrics.networkRequests.push(req); if (this.metrics.networkRequests.length > 100) this.metrics.networkRequests.shift(); logWrapper('pageState', 'LOG', `Network request: ${req.name}, Duration: ${req.duration}ms`); } }); } } catch (e) { customConsole.error('Error in recordNetwork:', e); } } recordPageTiming() { try { const timing = performance.timing; if (timing.loadEventEnd && timing.navigationStart) { const loadTime = timing.loadEventEnd - timing.navigationStart; logWrapper('pageState', 'LOG', `Page load time: ${loadTime}ms`); } } catch (e) { customConsole.error('Error in recordPageTiming:', e); } } } class PostFilter { constructor() { try { this.settings = GM_getValue('settings', CONFIG.defaultSettings.filter); customConsole.log('PostFilter settings:', this.settings); this.postsCache = new Map(); this.spamPosts = new Set(); this.applyStyles(); this.applyFilters(); this.autoExpandImages(); this.observeDOMChanges(); this.startSpamEnforcer(); this.blockAds(); if (this.settings.sortByTime) this.sortPosts(); if (this.settings.linkifyVideos) this.linkifyVideos(); } catch (e) { customConsole.error('Error initializing PostFilter:', e); } } applyStyles() { GM_addStyle(` .spam-hidden { display: none !important; } .invalid-hidden { display: none !important; } `); } applyFilters(nodes = document.querySelectorAll('.l_post')) { customConsole.log('Applying filters'); logWrapper('pageBehavior', 'LOG', 'Applying content filters'); const startTime = performance.now(); try { this.spamPosts.clear(); nodes.forEach(post => { if (!post) return; const contentEle = post.querySelector('.d_post_content'); if (!contentEle) return; const content = contentEle.textContent.trim(); const pid = post.dataset.pid || 'unknown'; post.style.display = ''; post.classList.remove('spam-hidden', 'invalid-hidden'); if (this.settings.hideInvalid && !content) { post.classList.add('invalid-hidden'); logWrapper('pageBehavior', 'LOG', `Hid invalid post: ${pid}`); if (this.settings.showHiddenFloors) { post.classList.remove('invalid-hidden'); logWrapper('pageBehavior', 'LOG', `Restored invalid post: ${pid}`); } } else if (this.settings.hideSpam) { const keywords = this.settings.spamKeywords.map(k => k.trim()); const regex = new RegExp(keywords.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'i'); if (regex.test(content)) { post.classList.add('spam-hidden'); post.style.display = 'none'; this.spamPosts.add(post); const matchedKeyword = keywords.find(k => content.toLowerCase().includes(k.toLowerCase())) || 'unknown'; logWrapper('pageBehavior', 'LOG', `Hid spam post: ${pid}, Keyword: ${matchedKeyword}, Content: ${content.slice(0, 50)}...`); } } this.postsCache.set(post, true); }); const blockedElements = [...(this.settings.blockedElements || []), ...(this.settings.tempBlockedElements || [])]; blockedElements.forEach(selector => { try { document.querySelectorAll(selector).forEach(el => { if (!this.postsCache.has(el)) { el.classList.add('spam-hidden'); el.style.display = 'none'; logWrapper('pageBehavior', 'LOG', `Hid blocked element: ${selector}`); this.postsCache.set(el, true); } }); } catch (e) { customConsole.warn(`Invalid selector: ${selector}`, e); } }); setTimeout(() => this.enforceSpamHiding(), 100); } catch (e) { customConsole.error('Error in applyFilters:', e); } const endTime = performance.now(); PerformanceMonitor.getInstance().recordProcessSpeed(endTime - startTime); } startSpamEnforcer() { const observer = new MutationObserver(() => { if (this.settings.hideSpam) { this.enforceSpamHiding(); } }); observer.observe(document.body, { childList: true, subtree: true, attributes: true }); customConsole.log('Spam enforcer started'); } enforceSpamHiding() { this.spamPosts.forEach(post => { if (post && document.body.contains(post) && post.style.display !== 'none') { post.style.display = 'none'; post.classList.add('spam-hidden'); const pid = post.dataset.pid || 'unknown'; logWrapper('pageBehavior', 'LOG', `Re-enforced spam hiding for post: ${pid}`); } }); setTimeout(() => this.enforceSpamHiding(), 500); } autoExpandImages(nodes = document.querySelectorAll('.replace_tip')) { if (!this.settings.autoExpandImages) return; customConsole.log('Auto expanding images'); logWrapper('pageBehavior', 'LOG', 'Starting auto expand images'); const startTime = performance.now(); try { nodes.forEach(tip => { if (tip.style.display !== 'none' && !tip.dataset.expanded) { const rect = tip.getBoundingClientRect(); const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true, clientX: rect.left, clientY: rect.top }); tip.dispatchEvent(clickEvent); tip.dataset.expanded = 'true'; const img = tip.closest('.replace_div')?.querySelector('img'); logWrapper('pageBehavior', 'LOG', `Expanded image: ${img?.src || 'unknown'}`); logWrapper('userActions', 'LOG', `Simulated click on replace_tip at (${Math.round(rect.left)}, ${Math.round(rect.top)})`); this.postsCache.set(tip, true); if (this.settings.enhanceImages && img) this.enhanceImage(img); } }); } catch (e) { customConsole.error('Error in autoExpandImages:', e); } const endTime = performance.now(); PerformanceMonitor.getInstance().recordProcessSpeed(endTime - startTime); } updateFilters() { try { this.settings = GM_getValue('settings', CONFIG.defaultSettings.filter); customConsole.log('Updated settings:', this.settings); this.postsCache.clear(); this.applyFilters(); this.autoExpandImages(); this.blockAds(); if (this.settings.sortByTime) this.sortPosts(); if (this.settings.linkifyVideos) this.linkifyVideos(); } catch (e) { customConsole.error('Error in updateFilters:', e); } } observeDOMChanges() { try { const observer = new MutationObserver(_.debounce(mutations => { let shouldProcess = false; const newPosts = []; mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE && node.matches('.l_post')) { newPosts.push(node); shouldProcess = true; } }); }); if (shouldProcess) { this.applyFilters(newPosts); this.autoExpandImages(); this.blockAds(); if (this.settings.sortByTime) this.sortPosts(); if (this.settings.linkifyVideos) this.linkifyVideos(); } }, 300)); observer.observe(document.body, { childList: true, subtree: true }); customConsole.log('DOM observer initialized'); } catch (e) { customConsole.error('Error in observeDOMChanges:', e); } } blockAds() { if (!this.settings.blockAds) return; customConsole.log('Blocking ads'); logWrapper('pageBehavior', 'LOG', 'Blocking advertisements'); try { const adSelectors = [ '.ad_item', '.mediago', '[class*="ad_"]:not([class*="content"])', '.app_download_box', '.right_section .region_bright:not(.content)' ]; adSelectors.forEach(selector => { document.querySelectorAll(selector).forEach(el => { if (!el.closest('.d_post_content') && !el.closest('.l_container')) { el.classList.add('spam-hidden'); el.style.display = 'none'; logWrapper('pageBehavior', 'LOG', `Hid ad element: ${selector}`); this.postsCache.set(el, true); } }); }); } catch (e) { customConsole.error('Error in blockAds:', e); } } sortPosts() { customConsole.log('Sorting posts by time'); logWrapper('pageBehavior', 'LOG', 'Sorting posts by time'); try { const posts = Array.from(document.querySelectorAll('.l_post')); posts.sort((a, b) => Number(b.dataset.pid) - Number(a.dataset.pid)); const container = document.querySelector('.pb_list'); if (container) { container.innerHTML = ''; posts.forEach(post => container.appendChild(post)); } } catch (e) { customConsole.error('Error in sortPosts:', e); } } linkifyVideos() { customConsole.log('Linking videos'); logWrapper('pageBehavior', 'LOG', 'Converting video links'); try { const videoRegex = /(?:av\d+|BV\w+)|(?:https?:\/\/(?:www\.)?(youtube\.com|youtu\.be)\/[^\s]+)/gi; document.querySelectorAll('.d_post_content').forEach(post => { if (!this.postsCache.has(post)) { post.innerHTML = post.innerHTML.replace(videoRegex, match => { if (match.startsWith('http')) { return `${match}`; } else { return `${match}`; } }); this.postsCache.set(post, true); } }); } catch (e) { customConsole.error('Error in linkifyVideos:', e); } } enhanceImage(img) { if (!img) return; customConsole.log('Enhancing image:', img.src); try { img.style.cursor = 'pointer'; img.addEventListener('click', () => { const overlay = document.createElement('div'); overlay.className = 'image-overlay'; const largeImg = document.createElement('img'); largeImg.src = img.src; largeImg.className = 'large-image'; overlay.appendChild(largeImg); document.body.appendChild(overlay); overlay.addEventListener('click', () => overlay.remove()); overlay.addEventListener('wheel', (e) => { e.preventDefault(); const scale = e.deltaY > 0 ? 0.9 : 1.1; largeImg.style.transform = `scale(${(parseFloat(largeImg.style.transform?.match(/scale\((.*?)\)/)?.[1]) || 1) * scale})`; }); }); } catch (e) { customConsole.error('Error in enhanceImage:', e); } } } class DynamicPanel { constructor() { try { this.panel = null; this.minimizedIcon = null; this.isDragging = false; this.dragOccurred = false; this.lastClickTime = 0; this.isResizing = false; this.settings = GM_getValue('settings', CONFIG.defaultSettings.filter) || CONFIG.defaultSettings.filter; this.panelSettings = GM_getValue('panelSettings', CONFIG.defaultSettings.panel) || CONFIG.defaultSettings.panel; if (!this.settings.blockedElements) this.settings.blockedElements = []; if (!this.settings.tempBlockedElements) this.settings.tempBlockedElements = []; customConsole.log('DynamicPanel settings:', this.panelSettings); this.postFilter = new PostFilter(); this.init(); this.applyDarkMode(this.settings.darkMode); } catch (e) { customConsole.error('Error initializing DynamicPanel:', e); } } init() { customConsole.log('Initializing DynamicPanel'); try { this.createPanel(); this.createMinimizedIcon(); document.body.appendChild(this.panel); document.body.appendChild(this.minimizedIcon); this.loadContent(); this.setupPanelInteractions(); this.minimizePanel(); if (!this.panelSettings.minimized) { this.restorePanel(); } this.ensureVisibility(); this.observer = new ResizeObserver(() => this.adjustPanelHeight()); this.observer.observe(this.panel.querySelector('.panel-content')); customConsole.log('Panel initialized, visible:', this.panel.style.display !== 'none'); this.setupUserActionListeners(); this.setupCleanup(); setTimeout(() => this.startPerformanceMonitoring(), 100); } catch (e) { customConsole.error('Error in init:', e); } } ensureVisibility() { try { this.panel.style.opacity = '1'; this.panel.style.visibility = 'visible'; this.minimizedIcon.style.opacity = '1'; this.minimizedIcon.style.visibility = 'visible'; customConsole.log('Ensuring visibility:', { panel: { display: this.panel.style.display }, icon: { display: this.minimizedIcon.style.display } }); } catch (e) { customConsole.error('Error in ensureVisibility:', e); } } createPanel() { customConsole.log('Creating panel'); try { this.panel = document.createElement('div'); this.panel.id = 'enhanced-panel'; GM_addStyle(` #enhanced-panel { position: fixed; z-index: 9999; top: ${this.panelSettings.position.y}px; left: ${this.panelSettings.position.x}px; width: ${this.panelSettings.width}px; min-height: ${this.panelSettings.minHeight}px; max-height: ${this.panelSettings.maxHeight}; background: rgba(255,255,255,0.98); border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.1); transition: all 0.3s ease; transform: scale(${this.panelSettings.scale}); contain: strict; display: none; opacity: 1; visibility: visible; height: auto; } #minimized-icon { position: fixed; z-index: 9999; top: ${this.panelSettings.position.y}px; left: ${this.panelSettings.position.x}px; width: 32px; height: 32px; background: #ffffff; border-radius: 50%; box-shadow: 0 4px 16px rgba(0,0,0,0.2); display: block; cursor: pointer; text-align: center; line-height: 32px; font-size: 16px; color: #007bff; overflow: hidden; } .panel-header { padding: 16px; border-bottom: 1px solid #eee; user-select: none; display: flex; justify-content: space-between; align-items: center; cursor: move; } .panel-content { padding: 16px; overflow-y: auto; overscroll-behavior: contain; height: auto; max-height: calc(90vh - 80px); } .resize-handle { position: absolute; bottom: 0; right: 0; width: 20px; height: 20px; cursor: nwse-resize; background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTUgNUwxNSAxNSIgc3Ryb2tlPSIjOTk5OTk5IiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNNSAxNUwxNSAxNSIgc3Ryb2tlPSIjOTk5OTk5IiBzdHJva2Utd2lkdGg9IjIiLz48L3N2Zz4=') no-repeat center; } .minimize-btn, .scale-btn { cursor: pointer; padding: 0 8px; } .minimize-btn:hover, .scale-btn:hover { color: #007bff; } .setting-group { display: flex; align-items: center; padding: 10px 0; gap: 10px; } .toggle-switch { position: relative; width: 40px; height: 20px; flex-shrink: 0; } .toggle-switch input { opacity: 0; width: 0; height: 0; } .toggle-slider { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: #ccc; border-radius: 10px; cursor: pointer; transition: background 0.3s; } .toggle-slider:before { position: absolute; content: ""; height: 16px; width: 16px; left: 2px; top: 2px; background: white; border-radius: 50%; transition: transform 0.3s; box-shadow: 0 1px 2px rgba(0,0,0,0.2); } .toggle-switch input:checked + .toggle-slider { background: #34c759; } .toggle-switch input:checked + .toggle-slider:before { transform: translateX(20px); } .setting-label { flex: 1; font-size: 14px; color: #333; } body.dark-mode .setting-label { color: #ddd !important; } select { padding: 4px; border: 1px solid #ddd; border-radius: 4px; } .divider { height: 1px; background: #eee; margin: 16px 0; } .tool-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; } .tool-card { padding: 12px; background: #f8f9fa; border: 1px solid #eee; border-radius: 8px; cursor: pointer; transition: all 0.2s; } .tool-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); } .metric-grid { display: grid; gap: 12px; } .progress-bar { height: 4px; background: #e9ecef; border-radius: 2px; overflow: hidden; } .progress-fill { height: 100%; background: #28a745; width: 0%; transition: width 0.3s ease; } .block-modal, .keyword-modal, .log-modal, .search-modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 10000; pointer-events: auto; } .modal-content { background: white; padding: 20px; border-radius: 8px; width: 400px; max-height: 80vh; overflow-y: auto; pointer-events: auto; } .modal-content p { color: #666; margin: 5px 0 10px; font-size: 12px; } textarea, input[type="text"] { width: 100%; margin: 10px 0; padding: 8px; border: 1px solid #ddd; resize: vertical; } .modal-actions { text-align: right; } .btn-cancel, .btn-save, .btn-block, .btn-undo, .btn-confirm, .btn-search { padding: 6px 12px; margin: 0 5px; border: none; border-radius: 4px; cursor: pointer; pointer-events: auto; } .btn-cancel { background: #eee; } .btn-save, .btn-block, .btn-undo, .btn-confirm, .btn-search { background: #34c759; color: white; } .btn-block.active { background: #ff4444; } .btn-undo { background: #ff9800; } .hover-highlight { outline: 2px solid #ff4444; outline-offset: 2px; } .blocked-item { display: flex; justify-content: space-between; align-items: center; padding: 5px 0; border-bottom: 1px solid #eee; } .blocked-item button { padding: 4px 8px; font-size: 12px; } .cursor-circle { position: fixed; width: 20px; height: 20px; background: rgba(128, 128, 128, 0.5); border-radius: 50%; pointer-events: none; z-index: 10001; transition: transform 0.2s ease; } .cursor-circle.confirm { background: rgba(52, 199, 89, 0.8); transform: scale(1.5); transition: transform 0.3s ease, background 0.3s ease; } body.blocking-mode * { cursor: none !important; } .performance-info { display: none; } .highlight-match { background-color: yellow; } .image-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 10000; display: flex; justify-content: center; align-items: center; } .large-image { max-width: 90%; max-height: 90%; cursor: move; } body.dark-mode, body.dark-mode .wrap1, body.dark-mode .l_container, body.dark-mode .pb_content, body.dark-mode .d_post_content, body.dark-mode .left_section, body.dark-mode .right_section { background: #222 !important; color: #ddd !important; } body.dark-mode #enhanced-panel { background: rgba(50,50,50,0.98) !important; color: #ddd !important; } body.dark-mode a { color: #66b3ff !important; } `); this.panel.innerHTML = `
贴吧增强控制台
缩小还原
`; } catch (e) { customConsole.error('Error in createPanel:', e); } } createMinimizedIcon() { customConsole.log('Creating minimized icon'); try { this.minimizedIcon = document.createElement('div'); this.minimizedIcon.id = 'minimized-icon'; this.minimizedIcon.textContent = '⚙️'; this.minimizedIcon.addEventListener('click', e => { const now = Date.now(); if (now - this.lastClickTime > 300 && !this.dragOccurred) { customConsole.log('Minimized icon clicked, toggling panel'); this.toggleMinimize(); this.lastClickTime = now; } this.dragOccurred = false; e.stopPropagation(); }); } catch (e) { customConsole.error('Error in createMinimizedIcon:', e); } } loadContent() { customConsole.log('Loading panel content'); try { this.panel.querySelector('.panel-content').innerHTML = `

📊 智能过滤设置

启用调试模式
隐藏无效楼层
屏蔽水贴内容
自动展开图片
自动屏蔽广告
按时间排序帖子
图片交互优化
视频链接跳转
黑夜模式
显示隐藏楼层

⚙️ 高级工具

💻 系统监控

内存占用0 MB
处理速度0 ms
`; this.bindEvents(); setTimeout(() => this.adjustPanelHeight(), 50); } catch (e) { customConsole.error('Error in loadContent:', e); } } adjustPanelHeight() { try { if (this.panelSettings.minimized) return; const content = this.panel.querySelector('.panel-content'); const headerHeight = this.panel.querySelector('.panel-header').offsetHeight; const maxHeight = Math.min(content.scrollHeight + headerHeight + 32, window.innerHeight * 0.9); this.panel.style.height = `${maxHeight}px`; customConsole.log('Adjusted panel height:', maxHeight); } catch (e) { customConsole.error('Error in adjustPanelHeight:', e); } } bindEvents() { customConsole.log('Binding events'); try { this.panel.querySelectorAll('input[type="checkbox"]').forEach(checkbox => { checkbox.addEventListener('change', () => { if (checkbox.dataset.setting === 'debugMode') { CONFIG.debugMode = checkbox.checked; GM_setValue('debugMode', CONFIG.debugMode); } else if (checkbox.dataset.setting === 'darkMode') { this.settings.darkMode = checkbox.checked; GM_setValue('settings', this.settings); this.applyDarkMode(checkbox.checked); } else { this.settings[checkbox.dataset.setting] = checkbox.checked; GM_setValue('settings', this.settings); this.postFilter.updateFilters(); } logWrapper('userActions', 'LOG', `Toggled ${checkbox.dataset.setting} to ${checkbox.checked}`); this.adjustPanelHeight(); }); }); this.panel.querySelector('[data-setting="blockType"]').addEventListener('change', (e) => { this.settings.blockType = e.target.value; GM_setValue('settings', this.settings); logWrapper('userActions', 'LOG', `Set block type to ${this.settings.blockType}`); }); const actions = { editKeywords: () => this.showKeywordEditor(), toggleBlockMode: () => this.toggleBlockMode(), showUndoList: () => this.showUndoList(), exportSettings: () => this.exportConfig(), importSettings: () => this.importConfig(), performanceChart: () => { const perfInfo = this.panel.querySelector('.performance-info'); perfInfo.style.display = perfInfo.style.display === 'block' ? 'none' : 'block'; logWrapper('userActions', 'LOG', `Toggled performance chart: ${perfInfo.style.display}`); this.adjustPanelHeight(); customConsole.log('Toggling performance chart'); }, quickSearch: () => this.toggleSearch(), saveLogs: () => this.showLogSaveDialog() }; this.panel.querySelectorAll('[data-action]').forEach(btn => { btn.addEventListener('click', () => { actions[btn.dataset.action](); logWrapper('userActions', 'LOG', `Clicked button: ${btn.dataset.action}`); }); }); this.panel.querySelector('.minimize-btn').addEventListener('click', e => { this.toggleMinimize(); logWrapper('userActions', 'LOG', 'Clicked minimize button'); e.stopPropagation(); }); } catch (e) { customConsole.error('Error in bindEvents:', e); } } setupPanelInteractions() { customConsole.log('Setting up panel interactions'); try { const header = this.panel.querySelector('.panel-header'); const resizeHandle = this.panel.querySelector('.resize-handle'); let startX, startY, startWidth, startHeight; const onDragStart = (e, target) => { this.isDragging = true; this.dragOccurred = false; startX = e.clientX - this.panelSettings.position.x; startY = e.clientY - this.panelSettings.position.y; e.preventDefault(); }; const onDragMove = (e) => { if (this.isDragging) { this.dragOccurred = true; const panelWidth = this.panel.offsetWidth; const panelHeight = this.panel.offsetHeight; this.panelSettings.position.x = Math.max(0, Math.min(e.clientX - startX, window.innerWidth - panelWidth)); this.panelSettings.position.y = Math.max(0, Math.min(e.clientY - startY, window.innerHeight - panelHeight)); const target = this.panelSettings.minimized ? this.minimizedIcon : this.panel; target.style.left = `${this.panelSettings.position.x}px`; target.style.top = `${this.panelSettings.position.y}px`; logWrapper('userActions', 'LOG', `Dragged ${this.panelSettings.minimized ? 'minimized icon' : 'panel'} to (${this.panelSettings.position.x}, ${this.panelSettings.position.y})`); } if (this.isResizing) { const newWidth = startWidth + (e.clientX - startX); const newHeight = startHeight + (e.clientY - startY); this.panelSettings.width = Math.max(200, newWidth); this.panel.style.width = `${this.panelSettings.width}px`; this.panel.style.height = `${Math.max(200, newHeight)}px`; logWrapper('userActions', 'LOG', `Resized panel to ${this.panelSettings.width}x${Math.max(200, newHeight)}`); } }; const onDragEnd = () => { if (this.isDragging || this.isResizing) { GM_setValue('panelSettings', this.panelSettings); this.isDragging = false; this.isResizing = false; this.adjustPanelHeight(); setTimeout(() => { this.dragOccurred = false; }, 100); customConsole.log('Drag or resize ended'); } }; header.addEventListener('mousedown', e => onDragStart(e, this.panel)); this.minimizedIcon.addEventListener('mousedown', e => onDragStart(e, this.minimizedIcon)); document.addEventListener('mousemove', onDragMove); document.addEventListener('mouseup', onDragEnd); resizeHandle.addEventListener('mousedown', e => { this.isResizing = true; startX = e.clientX; startY = e.clientY; startWidth = this.panelSettings.width; startHeight = parseInt(this.panel.style.height) || this.panel.offsetHeight; e.preventDefault(); }); this.panel.querySelectorAll('.scale-btn').forEach(btn => { btn.addEventListener('click', e => { this.panelSettings.scale = parseFloat(btn.dataset.scale); this.panel.style.transform = `scale(${this.panelSettings.scale})`; GM_setValue('panelSettings', this.panelSettings); this.ensureVisibility(); this.adjustPanelHeight(); logWrapper('userActions', 'LOG', `Scaled panel to ${this.panelSettings.scale}`); e.stopPropagation(); }); }); } catch (e) { customConsole.error('Error in setupPanelInteractions:', e); } } setupUserActionListeners() { try { document.addEventListener('click', e => { if (!this.panel.contains(e.target) && !this.minimizedIcon.contains(e.target)) { logWrapper('userActions', 'LOG', `Clicked on page at (${e.clientX}, ${e.clientY}), Target: ${e.target.tagName}.${e.target.className || ''}`); } }); document.addEventListener('input', e => { logWrapper('userActions', 'LOG', `Input in ${e.target.tagName}, Value: ${e.target.value}`); }); document.addEventListener('scroll', _.debounce(() => { logWrapper('userActions', 'LOG', `Scrolled to (${window.scrollX}, ${window.scrollY})`); }, 200)); } catch (e) { customConsole.error('Error in setupUserActionListeners:', e); } } startPerformanceMonitoring() { const perfMonitor = PerformanceMonitor.getInstance(); const updatePerformance = () => { perfMonitor.recordMemory(); perfMonitor.recordNetwork(); const memUsage = perfMonitor.metrics.memoryUsage.length > 0 ? Math.round(_.mean(perfMonitor.metrics.memoryUsage) / 1024 / 1024) : 0; const processSpeed = perfMonitor.metrics.processSpeed.length > 0 ? _.mean(perfMonitor.metrics.processSpeed).toFixed(2) : 0; const memElement = document.getElementById('mem-usage'); const progElement = document.getElementById('mem-progress'); const speedElement = document.getElementById('process-speed'); if (memElement && progElement) { memElement.textContent = `${memUsage} MB`; progElement.style.width = `${Math.min(memUsage / 100 * 100, 100)}%`; } if (speedElement) { speedElement.textContent = `${processSpeed} ms`; } requestAnimationFrame(updatePerformance); }; requestAnimationFrame(updatePerformance); } toggleMinimize() { try { customConsole.log('Toggling minimize, current state:', this.panelSettings.minimized); if (this.panelSettings.minimized) { this.restorePanel(); } else { this.minimizePanel(); } GM_setValue('panelSettings', this.panelSettings); this.ensureVisibility(); customConsole.log('Toggle minimize completed, minimized:', this.panelSettings.minimized); } catch (e) { customConsole.error('Error in toggleMinimize:', e); } } minimizePanel() { try { this.panel.style.display = 'none'; this.minimizedIcon.style.display = 'block'; this.minimizedIcon.style.left = `${this.panelSettings.position.x}px`; this.minimizedIcon.style.top = `${this.panelSettings.position.y}px`; this.panelSettings.minimized = true; customConsole.log('Minimized panel'); } catch (e) { customConsole.error('Error in minimizePanel:', e); } } restorePanel() { try { this.panel.style.display = 'block'; this.minimizedIcon.style.display = 'none'; this.panel.style.left = `${this.panelSettings.position.x}px`; this.panel.style.top = `${this.panelSettings.position.y}px`; this.panel.style.transform = `scale(${this.panelSettings.scale})`; this.panelSettings.minimized = false; this.adjustPanelHeight(); customConsole.log('Restored panel'); } catch (e) { customConsole.error('Error in restorePanel:', e); } } toggleBlockMode(event) { try { this.isBlockingMode = !this.isBlockingMode; const blockBtn = this.panel.querySelector('.btn-block'); blockBtn.textContent = `🛡️ ${this.isBlockingMode ? '停止选择屏蔽' : '开始选择屏蔽元素'}`; blockBtn.classList.toggle('active', this.isBlockingMode); if (this.isBlockingMode) { document.body.classList.add('blocking-mode'); this.createCursorCircle(); this.listeners = { move: this.moveCursorCircle.bind(this), click: this.handleBlockClick.bind(this) }; document.addEventListener('mousemove', this.listeners.move); document.addEventListener('click', this.listeners.click); } else { document.body.classList.remove('blocking-mode'); this.removeCursorCircle(); if (this.listeners) { document.removeEventListener('mousemove', this.listeners.move); document.removeEventListener('click', this.listeners.click); this.listeners = null; } this.removeHighlight(); this.selectedTarget = null; } customConsole.log('Block mode:', this.isBlockingMode); if (event) event.stopPropagation(); this.adjustPanelHeight(); } catch (e) { customConsole.error('Error in toggleBlockMode:', e); } } createCursorCircle() { try { this.cursorCircle = document.createElement('div'); this.cursorCircle.className = 'cursor-circle'; document.body.appendChild(this.cursorCircle); } catch (e) { customConsole.error('Error in createCursorCircle:', e); } } moveCursorCircle(event) { if (!this.isBlockingMode || !this.cursorCircle) return; try { this.cursorCircle.style.left = `${event.clientX - 10}px`; this.cursorCircle.style.top = `${event.clientY - 10}px`; this.highlightElement(event); } catch (e) { customConsole.error('Error in moveCursorCircle:', e); } } removeCursorCircle() { try { if (this.cursorCircle && document.body.contains(this.cursorCircle)) { document.body.removeChild(this.cursorCircle); } this.cursorCircle = null; } catch (e) { customConsole.error('Error in removeCursorCircle:', e); } } highlightElement(event) { if (!this.isBlockingMode) return; try { this.removeHighlight(); const target = event.target; if (target === this.panel || this.panel.contains(target) || target.classList.contains('block-modal') || target.closest('.block-modal')) return; target.classList.add('hover-highlight'); customConsole.log('Highlighting:', target.tagName + '.' + (target.className || '')); } catch (e) { customConsole.error('Error in highlightElement:', e); } } removeHighlight() { try { const highlighted = document.querySelector('.hover-highlight'); if (highlighted) highlighted.classList.remove('hover-highlight'); } catch (e) { customConsole.error('Error in removeHighlight:', e); } } handleBlockClick(event) { if (!this.isBlockingMode) return; try { event.preventDefault(); event.stopPropagation(); const target = event.target; if (target === this.panel || this.panel.contains(target) || target.classList.contains('block-modal') || target.closest('.block-modal')) return; this.selectedTarget = target; this.showConfirmDialog(event.clientX, event.clientY); } catch (e) { customConsole.error('Error in handleBlockClick:', e); } } showConfirmDialog(x, y) { try { const modal = document.createElement('div'); modal.className = 'block-modal'; modal.innerHTML = ` `; const confirmBtn = modal.querySelector('.btn-confirm'); const cancelBtn = modal.querySelector('.btn-cancel'); confirmBtn.addEventListener('click', e => { e.stopPropagation(); if (this.selectedTarget) this.blockElement(this.selectedTarget, x, y); document.body.removeChild(modal); logWrapper('userActions', 'LOG', 'Confirmed block element'); }, { once: true }); cancelBtn.addEventListener('click', e => { e.stopPropagation(); document.body.removeChild(modal); this.toggleBlockMode(); logWrapper('userActions', 'LOG', 'Canceled block element'); }, { once: true }); document.body.appendChild(modal); } catch (e) { customConsole.error('Error in showConfirmDialog:', e); } } blockElement(target, x, y) { try { const selector = this.getUniqueSelector(target); const blockList = this.settings.blockType === 'temp' ? this.settings.tempBlockedElements : this.settings.blockedElements; if (!blockList.includes(selector)) { blockList.push(selector); GM_setValue('settings', this.settings); this.postFilter.updateFilters(); customConsole.log(`Blocked element (${this.settings.blockType}) with selector:`, selector); logWrapper('userActions', 'LOG', `Blocked element (${this.settings.blockType}): ${selector}`); } target.classList.add('spam-hidden'); target.style.display = 'none'; if (this.cursorCircle) { this.cursorCircle.style.left = `${x - 10}px`; this.cursorCircle.style.top = `${y - 10}px`; this.cursorCircle.classList.add('confirm'); setTimeout(() => { this.cursorCircle.classList.remove('confirm'); this.toggleBlockMode(); }, 300); } else { this.toggleBlockMode(); } this.adjustPanelHeight(); } catch (e) { customConsole.error('Error in blockElement:', e); } } getUniqueSelector(element) { try { if (element.id) return `#${element.id}`; const path = []; let current = element; while (current && current.nodeType === Node.ELEMENT_NODE && current !== document.body) { let selector = current.tagName.toLowerCase(); if (current.className) selector += `.${current.className.trim().split(/\s+/).join('.')}`; const siblings = Array.from(current.parentNode.children).filter(child => child.tagName === current.tagName); if (siblings.length > 1) selector += `:nth-child(${siblings.indexOf(current) + 1})`; path.unshift(selector); current = current.parentNode; } return path.join(' > '); } catch (e) { customConsole.error('Error in getUniqueSelector:', e); return ''; } } showKeywordEditor() { customConsole.log('Showing keyword editor'); try { const modal = document.createElement('div'); modal.className = 'keyword-modal'; modal.innerHTML = ` `; modal.querySelector('.btn-save').addEventListener('click', () => { this.settings.spamKeywords = modal.querySelector('textarea').value.split('\n').map(k => k.trim()).filter(k => k.length > 0); GM_setValue('settings', this.settings); this.postFilter.updateFilters(); document.body.removeChild(modal); logWrapper('userActions', 'LOG', `Updated spam keywords: ${this.settings.spamKeywords.join(', ')}`); this.adjustPanelHeight(); }); modal.querySelector('.btn-cancel').addEventListener('click', () => { document.body.removeChild(modal); logWrapper('userActions', 'LOG', 'Canceled keyword editor'); }); document.body.appendChild(modal); } catch (e) { customConsole.error('Error in showKeywordEditor:', e); } } showUndoList() { customConsole.log('Showing undo list'); try { const modal = document.createElement('div'); modal.className = 'block-modal'; const permItems = this.settings.blockedElements.length > 0 ? this.settings.blockedElements.map((sel, i) => `
[永久] ${sel}
`).join('') : ''; const tempItems = this.settings.tempBlockedElements.length > 0 ? this.settings.tempBlockedElements.map((sel, i) => `
[临时] ${sel}
`).join('') : ''; const listItems = permItems + tempItems || '

暂无屏蔽元素

'; modal.innerHTML = ` `; modal.querySelectorAll('.btn-undo').forEach(btn => { btn.addEventListener('click', () => { const index = parseInt(btn.dataset.index); const type = btn.dataset.type; this.undoBlockElement(index, type); document.body.removeChild(modal); this.showUndoList(); logWrapper('userActions', 'LOG', `Undid block (${type}) at index ${index}`); }); }); modal.querySelector('.btn-cancel').addEventListener('click', () => { document.body.removeChild(modal); logWrapper('userActions', 'LOG', 'Closed undo list'); }); document.body.appendChild(modal); } catch (e) { customConsole.error('Error in showUndoList:', e); } } undoBlockElement(index, type) { try { const blockList = type === 'temp' ? this.settings.tempBlockedElements : this.settings.blockedElements; if (index < 0 || index >= blockList.length) { customConsole.warn('Invalid undo index:', index); return; } const selector = blockList[index]; blockList.splice(index, 1); GM_setValue('settings', this.settings); this.postFilter.updateFilters(); document.querySelectorAll(selector).forEach(el => el.classList.remove('spam-hidden')); customConsole.log(`Undid block (${type}) for selector:`, selector); this.adjustPanelHeight(); } catch (e) { customConsole.error('Error in undoBlockElement:', e); } } exportConfig() { customConsole.log('Exporting config'); try { const config = { filter: this.settings, panel: this.panelSettings }; const configJson = JSON.stringify(config, null, 2); const blob = new Blob([configJson], { type: 'application/json;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `tieba_enhance_config_${new Date().toISOString().replace(/[:.]/g, '-')}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); logWrapper('userActions', 'LOG', 'Exported configuration'); } catch (e) { customConsole.error('Error in exportConfig:', e); } } importConfig() { customConsole.log('Importing config'); try { const modal = document.createElement('div'); modal.className = 'keyword-modal'; modal.innerHTML = ` `; const fileInput = modal.querySelector('#configFileInput'); modal.querySelector('.btn-save').addEventListener('click', () => { const file = fileInput.files[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { try { const importedConfig = JSON.parse(e.target.result); this.settings = { ...CONFIG.defaultSettings.filter, ...importedConfig.filter }; this.panelSettings = { ...CONFIG.defaultSettings.panel, ...importedConfig.panel }; GM_setValue('settings', this.settings); GM_setValue('panelSettings', this.panelSettings); this.postFilter.updateFilters(); this.loadContent(); if (this.panelSettings.minimized) { this.minimizePanel(); } else { this.restorePanel(); } this.applyDarkMode(this.settings.darkMode); logWrapper('userActions', 'LOG', 'Imported configuration'); } catch (err) { customConsole.error('Invalid config file:', err); alert('配置文件无效,请检查格式'); } document.body.removeChild(modal); }; reader.readAsText(file); } else { alert('请选择一个配置文件'); } }); modal.querySelector('.btn-cancel').addEventListener('click', () => { document.body.removeChild(modal); logWrapper('userActions', 'LOG', 'Canceled config import'); }); document.body.appendChild(modal); } catch (e) { customConsole.error('Error in importConfig:', e); } } toggleSearch() { customConsole.log('Toggling quick search'); try { const modal = document.createElement('div'); modal.className = 'search-modal'; modal.innerHTML = ` `; const searchInput = modal.querySelector('#searchInput'); modal.querySelector('.btn-search').addEventListener('click', () => { const keyword = searchInput.value.trim(); if (keyword) { this.performSearch(keyword); logWrapper('userActions', 'LOG', `Searched for: ${keyword}`); } }); modal.querySelector('.btn-cancel').addEventListener('click', () => { document.body.removeChild(modal); logWrapper('userActions', 'LOG', 'Closed quick search'); }); document.body.appendChild(modal); searchInput.focus(); } catch (e) { customConsole.error('Error in toggleSearch:', e); } } performSearch(keyword) { try { document.querySelectorAll('.highlight-match').forEach(el => { el.classList.remove('highlight-match'); el.replaceWith(el.textContent); }); const posts = document.querySelectorAll('.d_post_content'); let regex; try { regex = new RegExp(keyword, 'gi'); } catch (e) { regex = new RegExp(keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'); } posts.forEach(post => { if (regex.test(post.textContent)) { post.innerHTML = post.innerHTML.replace(regex, match => `${match}`); post.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }); } catch (e) { customConsole.error('Error in performSearch:', e); } } showLogSaveDialog() { customConsole.log('Showing log save dialog'); try { const modal = document.createElement('div'); modal.className = 'log-modal'; modal.innerHTML = ` `; modal.querySelector('.btn-save').addEventListener('click', () => { try { customConsole.log('Preparing log content'); const fullLog = [ '=== 脚本运行日志 ===', ...logBuffer.script, '\n=== 网页运行状态 ===', ...logBuffer.pageState, '\n=== 网页行为 ===', ...logBuffer.pageBehavior, '\n=== 用户操作 ===', ...logBuffer.userActions ].join('\n'); customConsole.log('Creating Blob'); const blob = new Blob([fullLog], { type: 'text/plain;charset=utf-8' }); customConsole.log('Generating download URL'); const url = URL.createObjectURL(blob); customConsole.log('Initiating download'); const a = document.createElement('a'); a.href = url; a.download = CONFIG.defaultSettings.logPath; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); customConsole.log('Log file download completed'); document.body.removeChild(modal); logWrapper('userActions', 'LOG', 'Saved logs to file'); } catch (e) { customConsole.error('Error saving logs:', e); alert('保存日志失败,请检查控制台错误信息'); } }); modal.querySelector('.btn-cancel').addEventListener('click', () => { document.body.removeChild(modal); logWrapper('userActions', 'LOG', 'Canceled log save'); }); document.body.appendChild(modal); this.adjustPanelHeight(); } catch (e) { customConsole.error('Error in showLogSaveDialog:', e); alert('打开日志保存对话框失败'); } } setupCleanup() { try { window.addEventListener('beforeunload', () => { this.panel.remove(); this.minimizedIcon.remove(); this.observer?.disconnect(); if (this.listeners) { document.removeEventListener('mousemove', this.listeners.move); document.removeEventListener('click', this.listeners.click); } customConsole.log('Cleaned up resources'); }); } catch (e) { customConsole.error('Error in setupCleanup:', e); } } applyDarkMode(enable) { customConsole.log('Applying dark mode:', enable); try { if (enable) { document.body.classList.add('dark-mode'); } else { document.body.classList.remove('dark-mode'); } logWrapper('pageBehavior', 'LOG', `Applying dark mode: ${enable}`); } catch (e) { customConsole.error('Error in applyDarkMode:', e); } } } document.addEventListener('DOMContentLoaded', () => { if (GM_getValue('authToken') !== SCRIPT_AUTH_TOKEN) { alert('脚本验证失败,请重新安装正版脚本!'); return; } customConsole.log('DOM content loaded, initializing'); const perfMonitor = PerformanceMonitor.getInstance(); new DynamicPanel(); perfMonitor.recordPageTiming(); }); if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(() => { if (!document.getElementById('enhanced-panel') && !document.getElementById('minimized-icon')) { customConsole.log('Fallback initialization'); const perfMonitor = PerformanceMonitor.getInstance(); new DynamicPanel(); } }, 50); } })();