您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
让整个网络世界都变成超可爱的猫娘语调喵~增强版(修复版)
当前为
// ==UserScript== // @name 🐱 全世界都要变成可爱猫猫!增强版 // @version 4.1.0 // @description 让整个网络世界都变成超可爱的猫娘语调喵~增强版(修复版) // @author 超萌猫娘开发队 // @match *://*/* // @include *://*.bilibili.com/video/* // @include *://*.bilibili.com/anime/* // @include *://*.bilibili.com/bangumi/play/* // @exclude *://gf.qytechs.cn/* // @exclude *://*.gov/* // @exclude *://*.edu/* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @license MIT // @namespace https://gf.qytechs.cn/users/1503554 // ==/UserScript== (function() { 'use strict'; // ===== 版本管理 ===== const SCRIPT_VERSION = "4.1.0"; const isVersionUpdate = GM_getValue("SCRIPT_VERSION") !== SCRIPT_VERSION; if (isVersionUpdate) { GM_setValue("SCRIPT_VERSION", SCRIPT_VERSION); console.log('🎉 猫娘脚本已更新到版本', SCRIPT_VERSION); } // ===== 增强配置系统 ===== const defaultConfig = { // 性能配置 performance: { processInterval: 5000, // 增加间隔,减少重复执行 maxProcessingTimeSlice: 8, // 减少单次处理时间 batchSize: 5, // 减少批处理大小 observerThrottle: 1000, // 增加观察器节流 maxRetryAttempts: 10, idleCallbackTimeout: 2000, debounceDelay: 500 // 新增防抖延迟 }, // 功能开关 features: { affectInput: false, bilibiliMergeALinks: true, bilibiliRandomizeUserNames: true, autoProcessNewContent: true, shadowDomSupport: true, performanceMonitoring: false, debugMode: false, smartProcessing: true // 新增智能处理开关 }, // 站点配置 sites: { excludeDomains: [ 'github.com', 'stackoverflow.com', 'google.com', 'gov.cn', 'edu.cn', 'gf.qytechs.cn' ], bilibili: { smartPause: true, retryInterval: 1000, maxRetryDelay: 5000, commentSelector: 'bili-comment-thread-renderer', userNameSelector: '#user-name a', contentSelector: '#contents span' } }, // 快捷键配置 shortcuts: { toggleScript: "100C", // Ctrl+C resetProcessing: "101R", // Ctrl+Shift+R showSettings: "100S", // Ctrl+S debugMode: "101D" // Ctrl+Shift+D }, // 用户偏好 - 重新设计处理模式 preferences: { cuteLevel: 'normal', // low, normal, high - 现在有明显差异 customEndings: [], disabledWords: [], processingMode: 'smart', // gentle, smart, aggressive - 现在有明显差异 intelligentReplacement: true // 智能替换开关 }, // 统计信息 stats: { processedElements: 0, replacedWords: 0, lastActive: new Date().toISOString(), installDate: new Date().toISOString(), sessionProcessed: 0 } }; // 加载用户配置 let userConfig = GM_getValue("catgirlConfig") || {}; let CONFIG = Object.assign({}, defaultConfig, userConfig); // 如果是版本更新,合并新配置 if (isVersionUpdate) { CONFIG = Object.assign(defaultConfig, userConfig); GM_setValue("catgirlConfig", CONFIG); showUpdateNotification(); } // ===== 智能可爱元素库 - 重新设计 ===== const cuteLibrary = { // 根据可爱程度分级的结尾词 endings: { low: ['喵', '呢', '哦', '啊'], normal: ['喵~', 'にゃん', '喵呜', 'nya~', '喵喵', '呢~'], high: ['喵~♪', 'にゃん♡', '喵呜~', 'nya~♡', '喵喵desu', 'にゃ♡', 'mew~', '喵♪', 'nyaa~', '喵desu~', '喵呢~', '喵哈~'] }, // 用户名前缀 - 修复冗余问题 userPrefixes: ['🏳️⚧️', '✨', '💕', '🌸', '🎀', '🌟'], // 装饰性前缀 decorativePrefixes: ['✨', '💫', '⭐', '🌸', '🎀', '💝'], // 情感结尾词 emotionalEndings: { excited: ['喵!', 'にゃん!', '哇喵~', '好棒喵~'], calm: ['喵~', '呢~', '嗯喵', '是这样喵'], happy: ['开心喵~', '嘻嘻喵', '哈哈喵~', '好开心喵'], confused: ['诶喵?', '嗯?喵', '咦喵~', '不懂喵'], sad: ['呜呜喵', '难过喵', '555喵', '想哭喵'] }, // 智能替换模板 - 多叉树结构 replacementTree: { // 攻击性词汇 -> 温和表达 aggressive: { high: ['要抱抱的小喵~', '生气气的小家伙', '炸毛的喵咪'], medium: ['不开心的喵', '小脾气喵', '闹别扭喵'], low: ['小喵', '喵~', '呢'] }, // 普通词汇 -> 可爱化 normal: { high: ['超级', '特别', '很'], medium: ['有点', '稍微', '还'], low: ['', '', ''] } } }; // ===== 防抖工具类 ===== class DebounceUtils { static debounce(func, delay) { let timeoutId; return function (...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; } static throttleWithDebounce(func, throttleMs, debounceMs) { let lastCallTime = 0; let debounceTimer; return function (...args) { const now = Date.now(); clearTimeout(debounceTimer); if (now - lastCallTime >= throttleMs) { lastCallTime = now; func.apply(this, args); } else { debounceTimer = setTimeout(() => { lastCallTime = Date.now(); func.apply(this, args); }, debounceMs); } }; } } // ===== 增强的性能工具类 ===== class EnhancedPerformanceUtils { static createTimeSliceProcessor(items, processor, options = {}) { const { batchSize = CONFIG.performance.batchSize, maxTime = CONFIG.performance.maxProcessingTimeSlice, onProgress = null, onComplete = null } = options; // 添加防重复处理机制 const processedKeys = new Set(); const uniqueItems = items.filter(item => { const key = this.getItemKey(item); if (processedKeys.has(key)) return false; processedKeys.add(key); return true; }); if (uniqueItems.length === 0) { if (onComplete) onComplete(); return; } let index = 0; let startTime = Date.now(); const processNextBatch = () => { const batchStartTime = performance.now(); let processedInBatch = 0; while (index < uniqueItems.length && processedInBatch < batchSize && (performance.now() - batchStartTime) < maxTime) { try { processor(uniqueItems[index], index, uniqueItems); } catch (error) { console.error('🐱 处理项目出错:', error); } index++; processedInBatch++; } // 进度回调 if (onProgress) { onProgress(index, uniqueItems.length, (index / uniqueItems.length) * 100); } if (index < uniqueItems.length) { // 使用 requestIdleCallback 或 setTimeout if (window.requestIdleCallback) { requestIdleCallback(processNextBatch, { timeout: CONFIG.performance.idleCallbackTimeout }); } else { setTimeout(processNextBatch, 16); // 约60fps } } else { const duration = Date.now() - startTime; if (CONFIG.features.debugMode) { console.log(`🎉 完成处理 ${uniqueItems.length} 个项目,耗时 ${duration}ms`); } if (onComplete) onComplete(); } }; processNextBatch(); } static getItemKey(item) { // 为元素生成唯一键 if (item && item.nodeType === Node.ELEMENT_NODE) { return `${item.tagName}-${item.textContent ? item.textContent.slice(0, 20) : ''}-${item.offsetTop || 0}`; } return String(item); } static throttleWithLeading(func, limit) { let inThrottle; let lastFunc; let lastRan; return function() { const context = this; const args = arguments; if (!inThrottle) { func.apply(context, args); lastRan = Date.now(); inThrottle = true; } else { clearTimeout(lastFunc); lastFunc = setTimeout(function() { if ((Date.now() - lastRan) >= limit) { func.apply(context, args); lastRan = Date.now(); } }, limit - (Date.now() - lastRan)); } }; } } // ===== 状态管理类 (修复版) ===== class StateManager { constructor() { this.state = { isEnabled: true, currentUrl: location.href, processingQueue: new Set(), urlChangeHandlers: [], // 确保初始化为数组 lastProcessTime: 0, // 新增:最后处理时间 // B站特殊状态 bilibili: { isCompleted: false, lastProcessedUrl: '', lastProcessedTime: 0, retryCount: 0, commentObserver: null, // 新增:评论区观察器 lastCommentCount: 0 // 新增:上次评论数量 } }; } onUrlChange(handler) { // 添加安全检查 if (!this.state.urlChangeHandlers) { this.state.urlChangeHandlers = []; } this.state.urlChangeHandlers.push(handler); } checkUrlChange() { const newUrl = location.href; if (newUrl !== this.state.currentUrl) { if (CONFIG.features.debugMode) { console.log('🔄 页面切换:', this.state.currentUrl, '->', newUrl); } this.state.currentUrl = newUrl; // 重置B站状态 this.state.bilibili.isCompleted = false; this.state.bilibili.lastProcessedUrl = newUrl; this.state.bilibili.retryCount = 0; this.state.bilibili.lastCommentCount = 0; // 触发回调 - 添加安全检查 if (this.state.urlChangeHandlers && Array.isArray(this.state.urlChangeHandlers)) { this.state.urlChangeHandlers.forEach(handler => { try { if (typeof handler === 'function') { handler(newUrl); } } catch (error) { console.error('🐱 URL变化处理器出错:', error); } }); } return true; } return false; } // 检查是否需要处理 - 新增防重复机制 shouldProcess() { const now = Date.now(); const timeSinceLastProcess = now - this.state.lastProcessTime; // 至少间隔1秒才能再次处理 if (timeSinceLastProcess < 1000) { return false; } this.state.lastProcessTime = now; return true; } shouldSkipBilibiliProcessing() { if (!this.isBilibili()) return true; const { isCompleted, lastProcessedUrl, lastProcessedTime } = this.state.bilibili; const now = Date.now(); return isCompleted && lastProcessedUrl === location.href && (now - lastProcessedTime) < 30000; // 减少到30秒 } markBilibiliCompleted() { this.state.bilibili.isCompleted = true; this.state.bilibili.lastProcessedUrl = location.href; this.state.bilibili.lastProcessedTime = Date.now(); } isBilibili() { return location.hostname.includes('bilibili.com'); } // 新增:检查评论区变化 checkBilibiliCommentChange() { if (!this.isBilibili()) return false; const commentThreads = document.querySelectorAll('bili-comment-thread-renderer'); const currentCount = commentThreads.length; if (currentCount !== this.state.bilibili.lastCommentCount) { this.state.bilibili.lastCommentCount = currentCount; this.state.bilibili.isCompleted = false; // 重置完成状态 return true; } return false; } } // ===== 增强的文本处理器 - 重新设计 ===== class EnhancedTextProcessor { constructor() { this.processedTexts = new Set(); this.replacementStats = new Map(); this.contextAnalyzer = new ContextAnalyzer(); // 新增上下文分析器 } isProcessed(text) { // 更精确的检查 return /喵[~~呜哈呢♪♡!]|nya|にゃん|meow|🏳️⚧️|已处理标记/i.test(text) || this.processedTexts.has(text); } // 根据可爱程度和处理模式获取结尾词 getCuteEnding(context = 'normal') { const level = CONFIG.preferences.cuteLevel; const mode = CONFIG.preferences.processingMode; let endings = cuteLibrary.endings[level] || cuteLibrary.endings.normal; // 根据处理模式调整 switch (mode) { case 'gentle': endings = endings.slice(0, Math.ceil(endings.length / 2)); // 使用前半部分,更温和 break; case 'aggressive': endings = [...endings, ...cuteLibrary.emotionalEndings[context] || []]; // 添加情感词汇 break; case 'smart': default: // 根据上下文智能选择 if (cuteLibrary.emotionalEndings[context]) { endings = [...endings, ...cuteLibrary.emotionalEndings[context]]; } break; } return endings[Math.floor(Math.random() * endings.length)]; } // 智能语境分析 - 增强版 analyzeContext(text) { const excitedMarkers = /[!!??]{2,}|哇|呀|啊{2,}/; const happyMarkers = /笑|哈哈|嘻嘻|开心|快乐|爽|棒/; const sadMarkers = /哭|难过|伤心|555|呜呜|痛苦/; const angryMarkers = /生气|愤怒|气死|烦|讨厌|恶心/; const confusedMarkers = /[??]{2,}|什么|啥|诶|咦|奇怪/; if (excitedMarkers.test(text)) return 'excited'; if (happyMarkers.test(text)) return 'happy'; if (sadMarkers.test(text)) return 'sad'; if (angryMarkers.test(text)) return 'excited'; // 生气时用兴奋的语调 if (confusedMarkers.test(text)) return 'confused'; if (/[。.,,;;]/.test(text)) return 'calm'; return 'normal'; } processText(text, options = {}) { if (!text?.trim() || this.isProcessed(text)) return text; // 检查是否在禁用词列表中 if (CONFIG.preferences.disabledWords.some(word => text.includes(word))) { return text; } // 标记为已处理 this.processedTexts.add(text); let result = text; let replacementCount = 0; // 分析上下文 const context = this.analyzeContext(text); // 应用清理规则 const cleanups = this.getCleanupRules(context); cleanups.forEach(([regex, replacement]) => { const matches = result.match(regex); if (matches) { // 根据处理模式选择替换策略 const finalReplacement = this.getSmartReplacement(replacement, context); result = result.replace(regex, finalReplacement); replacementCount += matches.length; } }); // 智能添加可爱尾缀 if (CONFIG.preferences.processingMode !== 'gentle' || replacementCount > 0) { result = this.addCuteEndings(result, context); } // 根据处理模式添加装饰 if (CONFIG.preferences.processingMode === 'aggressive' && Math.random() < 0.3) { const prefix = cuteLibrary.decorativePrefixes[Math.floor(Math.random() * cuteLibrary.decorativePrefixes.length)]; result = `${prefix} ${result}`; } // 更新统计 if (replacementCount > 0) { CONFIG.stats.replacedWords += replacementCount; CONFIG.stats.sessionProcessed += replacementCount; this.updateReplacementStats(text, result); } return result; } // 智能替换策略 getSmartReplacement(baseReplacement, context) { if (!CONFIG.preferences.intelligentReplacement) return baseReplacement; const mode = CONFIG.preferences.processingMode; const contextEnding = cuteLibrary.emotionalEndings[context]; if (contextEnding && Math.random() < 0.4) { const ending = contextEnding[Math.floor(Math.random() * contextEnding.length)]; return `${baseReplacement}${ending}`; } return baseReplacement; } addCuteEndings(text, context = 'normal') { const getCuteEnding = () => this.getCuteEnding(context); const addDesu = () => { const chance = CONFIG.preferences.cuteLevel === 'high' ? 0.4 : CONFIG.preferences.cuteLevel === 'normal' ? 0.2 : 0.1; return Math.random() < chance ? 'です' : ''; }; // 根据处理模式调整替换概率 let probability = 0.3; // 默认概率 switch (CONFIG.preferences.processingMode) { case 'gentle': probability = 0.15; break; case 'aggressive': probability = 0.6; break; } return text .replace(/([也矣兮乎者焉哉]|[啊吗呢吧哇呀哦嘛喔咯呜捏])([\s\p{P}]|$)/gu, (_, $1, $2) => `${getCuteEnding()}${addDesu()}${$2}`) .replace(/([的了辣])([\s\p{P}]|$)/gu, (_, $1, $2) => Math.random() < probability ? `${$1}${getCuteEnding()}${addDesu()}${$2}` : `${$1}${$2}`); } getCleanupRules(context = 'normal') { // 根据上下文返回不同的清理规则 const baseRules = [ // --------- 极端攻击与侮辱性词汇 --------- [/操你妈|操你娘|操你全家|肏你妈|干你妈|干你娘|去你妈的|去你娘的|去你全家/gi, '去睡觉觉'], [/妈了个?逼|妈的?智障|妈的/gi, '喵喵喵'], [/狗娘养的|狗杂种|狗东西|狗逼/gi, '不太好的小家伙'], [/操你大爷|去你大爷的/gi, '去玩耍啦'], [/去你老师的|你全家死光/gi, '嗯...安静一点'], [/你妈死了|你妈没了/gi, '你妈妈叫你回家吃饭'], // --------- 性相关及不雅词汇 --------- [/鸡巴|鸡叭|鸡把|\bjb\b|\bJB\b/gi, '小鱼干'], [/逼你|逼样|逼毛|逼崽子|什么逼/gi, '小淘气'], [/肏|干你|草你|cao你|cao你妈/gi, '去玩耍啦'], // --------- 常见人身攻击与负面评价 --------- [/你妹|你妹的|去你妹/gi, '小迷糊'], [/滚犊子|滚蛋|滚开|滚开你/gi, '去玩耍啦'], [/去死|死全家|死开|狗带/gi, '去睡觉觉'], [/人渣|渣人|渣男|渣女/gi, '要抱抱的小家伙'], [/废物|废柴|废狗/gi, '要抱抱的小家伙'], [/垃圾人|辣鸡|拉圾/gi, '不太好的小家伙'], [/王八蛋|王八羔子/gi, '坏坏的小家伙'], // --------- 智力与能力相关的攻击性词语 --------- [/傻逼|煞笔|沙雕|傻叉|笨蛋|二货|二逼/gi, '小糊涂虫'], [/白痴|智障|弱智|脑残|脑袋进水/gi, '小呆呆'], [/蠢货|蠢蛋/gi, '有点迷糊'], // --------- 网络流行语与缩写 --------- [/\bcnm\b|\bCNM\b|c\s*n\s*m/gi, '你好软糯'], [/\bnmsl\b|\bNMSL\b/gi, '你超棒棒'], [/\bmlgb\b|\bMLGB\b/gi, '哇好厉害'], [/tmd|TMD/gi, '太萌啦'], [/wtf|WTF/gi, '哇好神奇'], // 更多规则... [/我靠|我擦|我操|卧槽/gi, '哇哦'], [/笑死我了|笑死|xswl|XSWL/gi, '好有趣'], [/绝绝子/gi, '超级棒棒'], [/yyds|YYDS/gi, '最棒棒'], ]; // 根据上下文添加特定的结尾 return baseRules.map(([regex, replacement]) => { const contextEnding = this.getCuteEnding(context); return [regex, `${replacement}${contextEnding}`]; }); } updateReplacementStats(original, processed) { const key = `${original.length}:${processed.length}`; this.replacementStats.set(key, (this.replacementStats.get(key) || 0) + 1); } } // ===== 上下文分析器类 ===== class ContextAnalyzer { constructor() { this.patterns = { excited: /[!!??]{2,}|哇|呀|啊{2,}/, happy: /笑|哈哈|嘻嘻|开心|快乐|爽|棒/, sad: /哭|难过|伤心|555|呜呜|痛苦/, angry: /生气|愤怒|气死|烦|讨厌|恶心/, confused: /[??]{2,}|什么|啥|诶|咦|奇怪/, calm: /[。.,,;;]/ }; } analyze(text) { for (const [emotion, pattern] of Object.entries(this.patterns)) { if (pattern.test(text)) { return emotion; } } return 'normal'; } } // ===== 设置面板类 - 优化UI ===== class SettingsPanel { constructor() { this.isVisible = false; this.panel = null; } create() { if (this.panel) return; this.panel = document.createElement('div'); this.panel.id = 'catgirl-settings'; this.panel.innerHTML = this.getHTML(); this.panel.style.cssText = this.getCSS(); document.body.appendChild(this.panel); this.bindEvents(); } getHTML() { return ` <div class="settings-header"> <h3>🐱 猫娘化设置面板</h3> <button class="close-btn" data-action="close">×</button> </div> <div class="settings-content"> <div class="settings-tabs"> <button class="tab-btn active" data-tab="basic">基础设置</button> <button class="tab-btn" data-tab="advanced">高级设置</button> <button class="tab-btn" data-tab="stats">统计信息</button> </div> <div class="tab-content" id="basic-tab"> <div class="setting-group"> <label>🎀 可爱程度: <select id="cute-level"> <option value="low">低 (温和可爱)</option> <option value="normal" selected>普通 (标准可爱)</option> <option value="high">高 (超级可爱)</option> </select> </label> <small>控制添加可爱词汇的数量和频率</small> </div> <div class="setting-group"> <label>⚙️ 处理模式: <select id="processing-mode"> <option value="gentle">温和 (少量替换)</option> <option value="smart" selected>智能 (根据上下文)</option> <option value="aggressive">积极 (大量替换)</option> </select> </label> <small>控制文本处理的强度和策略</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="affect-input"> 📝 影响输入框 </label> <small>是否处理输入框和文本域中的内容</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="intelligent-replacement"> 🧠 智能替换 </label> <small>根据上下文智能选择替换词汇</small> </div> </div> <div class="tab-content" id="advanced-tab" style="display: none;"> <div class="setting-group"> <label> <input type="checkbox" id="debug-mode"> 🐛 调试模式 </label> <small>显示详细的处理日志信息</small> </div> <div class="setting-group"> <label> <input type="checkbox" id="performance-monitoring"> 📊 性能监控 </label> <small>启用内存和性能监控</small> </div> <div class="setting-group"> <label>🕐 处理间隔 (毫秒): <input type="range" id="process-interval" min="1000" max="10000" step="500"> <span id="interval-value">${CONFIG.performance.processInterval}</span> </label> <small>控制自动处理的时间间隔</small> </div> <div class="setting-group"> <label>📦 批处理大小: <input type="range" id="batch-size" min="3" max="20" step="1"> <span id="batch-value">${CONFIG.performance.batchSize}</span> </label> <small>单次处理的元素数量</small> </div> </div> <div class="tab-content" id="stats-tab" style="display: none;"> <div class="stats-grid"> <div class="stat-item"> <div class="stat-number" id="processed-count">${CONFIG.stats.processedElements}</div> <div class="stat-label">已处理元素</div> </div> <div class="stat-item"> <div class="stat-number" id="replaced-count">${CONFIG.stats.replacedWords}</div> <div class="stat-label">已替换词汇</div> </div> <div class="stat-item"> <div class="stat-number" id="session-count">${CONFIG.stats.sessionProcessed}</div> <div class="stat-label">本次会话</div> </div> </div> <div class="info-section"> <h4>📅 安装信息</h4> <p><strong>版本:</strong> ${SCRIPT_VERSION}</p> <p><strong>安装时间:</strong> ${new Date(CONFIG.stats.installDate).toLocaleString()}</p> <p><strong>最后活动:</strong> ${new Date(CONFIG.stats.lastActive).toLocaleString()}</p> </div> </div> <div class="actions"> <button id="save-settings" class="btn-primary">💾 保存设置</button> <button id="reset-settings" class="btn-warning">🔄 重置设置</button> <button id="clear-cache" class="btn-secondary">🧹 清理缓存</button> <button id="export-config" class="btn-info">📤 导出配置</button> </div> </div> `; } getCSS() { return ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 500px; max-height: 80vh; background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.3); z-index: 10000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: none; overflow: hidden; `; } bindEvents() { // 关闭按钮 const closeBtn = this.panel.querySelector('[data-action="close"]'); if (closeBtn) { closeBtn.onclick = () => this.hide(); } // 标签切换 const tabBtns = this.panel.querySelectorAll('.tab-btn'); tabBtns.forEach(btn => { btn.onclick = () => this.switchTab(btn.dataset.tab); }); // 滑块事件 const intervalSlider = document.getElementById('process-interval'); const batchSlider = document.getElementById('batch-size'); if (intervalSlider) { intervalSlider.oninput = (e) => { document.getElementById('interval-value').textContent = e.target.value; }; } if (batchSlider) { batchSlider.oninput = (e) => { document.getElementById('batch-value').textContent = e.target.value; }; } // 保存设置 const saveBtn = document.getElementById('save-settings'); if (saveBtn) { saveBtn.onclick = () => { this.saveSettings(); this.hide(); showToast('设置已保存喵~ ✨', 'success'); }; } // 重置设置 const resetBtn = document.getElementById('reset-settings'); if (resetBtn) { resetBtn.onclick = () => { if (confirm('确定要重置所有设置吗?这将清除所有自定义配置。')) { this.resetSettings(); showToast('设置已重置喵~ 🔄', 'info'); } }; } // 清理缓存 const clearBtn = document.getElementById('clear-cache'); if (clearBtn) { clearBtn.onclick = () => { this.clearCache(); showToast('缓存已清理喵~ 🧹', 'info'); }; } // 导出配置 const exportBtn = document.getElementById('export-config'); if (exportBtn) { exportBtn.onclick = () => this.exportConfig(); } } switchTab(tabName) { // 切换按钮状态 this.panel.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.remove('active'); }); this.panel.querySelector(`[data-tab="${tabName}"]`).classList.add('active'); // 切换内容 this.panel.querySelectorAll('.tab-content').forEach(content => { content.style.display = 'none'; }); const targetTab = document.getElementById(`${tabName}-tab`); if (targetTab) { targetTab.style.display = 'block'; } } show() { if (!this.panel) this.create(); // 加载当前设置 this.loadCurrentSettings(); this.panel.style.display = 'block'; this.isVisible = true; // 更新统计数据 this.updateStats(); } hide() { if (this.panel) { this.panel.style.display = 'none'; this.isVisible = false; } } loadCurrentSettings() { // 基础设置 const cuteLevelEl = document.getElementById('cute-level'); const processingModeEl = document.getElementById('processing-mode'); const affectInputEl = document.getElementById('affect-input'); const intelligentEl = document.getElementById('intelligent-replacement'); if (cuteLevelEl) cuteLevelEl.value = CONFIG.preferences.cuteLevel; if (processingModeEl) processingModeEl.value = CONFIG.preferences.processingMode; if (affectInputEl) affectInputEl.checked = CONFIG.features.affectInput; if (intelligentEl) intelligentEl.checked = CONFIG.preferences.intelligentReplacement; // 高级设置 const debugModeEl = document.getElementById('debug-mode'); const performanceEl = document.getElementById('performance-monitoring'); const intervalEl = document.getElementById('process-interval'); const batchEl = document.getElementById('batch-size'); if (debugModeEl) debugModeEl.checked = CONFIG.features.debugMode; if (performanceEl) performanceEl.checked = CONFIG.features.performanceMonitoring; if (intervalEl) { intervalEl.value = CONFIG.performance.processInterval; document.getElementById('interval-value').textContent = CONFIG.performance.processInterval; } if (batchEl) { batchEl.value = CONFIG.performance.batchSize; document.getElementById('batch-value').textContent = CONFIG.performance.batchSize; } } saveSettings() { // 基础设置 const cuteLevelEl = document.getElementById('cute-level'); const processingModeEl = document.getElementById('processing-mode'); const affectInputEl = document.getElementById('affect-input'); const intelligentEl = document.getElementById('intelligent-replacement'); if (cuteLevelEl) CONFIG.preferences.cuteLevel = cuteLevelEl.value; if (processingModeEl) CONFIG.preferences.processingMode = processingModeEl.value; if (affectInputEl) CONFIG.features.affectInput = affectInputEl.checked; if (intelligentEl) CONFIG.preferences.intelligentReplacement = intelligentEl.checked; // 高级设置 const debugModeEl = document.getElementById('debug-mode'); const performanceEl = document.getElementById('performance-monitoring'); const intervalEl = document.getElementById('process-interval'); const batchEl = document.getElementById('batch-size'); if (debugModeEl) CONFIG.features.debugMode = debugModeEl.checked; if (performanceEl) CONFIG.features.performanceMonitoring = performanceEl.checked; if (intervalEl) CONFIG.performance.processInterval = parseInt(intervalEl.value); if (batchEl) CONFIG.performance.batchSize = parseInt(batchEl.value); GM_setValue("catgirlConfig", CONFIG); } resetSettings() { CONFIG = Object.assign({}, defaultConfig); // 保留统计信息 CONFIG.stats = Object.assign({}, defaultConfig.stats, { installDate: GM_getValue("catgirlConfig")?.stats?.installDate || new Date().toISOString() }); GM_setValue("catgirlConfig", CONFIG); this.loadCurrentSettings(); } clearCache() { if (window.catgirlApp && window.catgirlApp.clearCache) { window.catgirlApp.clearCache(); } } exportConfig() { const configData = JSON.stringify(CONFIG, null, 2); const blob = new Blob([configData], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'catgirl-config.json'; a.click(); URL.revokeObjectURL(url); showToast('配置已导出喵~ 📤', 'success'); } updateStats() { const processedEl = document.getElementById('processed-count'); const replacedEl = document.getElementById('replaced-count'); const sessionEl = document.getElementById('session-count'); if (processedEl) processedEl.textContent = CONFIG.stats.processedElements; if (replacedEl) replacedEl.textContent = CONFIG.stats.replacedWords; if (sessionEl) sessionEl.textContent = CONFIG.stats.sessionProcessed; } } // ===== 主应用类 - 修复重复执行问题 ===== class CatgirlApp { constructor() { this.stateManager = new StateManager(); this.textProcessor = new EnhancedTextProcessor(); this.settingsPanel = new SettingsPanel(); this.processedElements = new WeakSet(); this.isRunning = false; this.intervalId = null; this.observer = null; // 新增:防重复处理机制 this.lastProcessHash = ''; this.processLock = false; // 新增:B站评论观察器 this.bilibiliCommentObserver = null; } async initialize() { if (this.shouldExclude()) { if (CONFIG.features.debugMode) { console.log('🐱 域名已排除,不启动喵~'); } return; } console.log('🐱 增强版猫娘化系统启动喵~'); // 注册(不可用)菜单命令 this.registerMenuCommands(); // 设置快捷键 this.setupKeyboardShortcuts(); // 等待DOM就绪 await this.waitForDOMReady(); // 设置URL变化监听 this.stateManager.onUrlChange((newUrl) => { // 使用防抖延迟处理 setTimeout(() => { if (location.href === newUrl) { // 确保URL没有再次变化 this.processPage(); } }, CONFIG.performance.debounceDelay); }); // 开始处理 this.start(); } shouldExclude() { return CONFIG.sites.excludeDomains.some(domain => location.hostname.includes(domain) ); } registerMenuCommands() { GM_registerMenuCommand("🐱 设置面板", () => this.settingsPanel.show()); GM_registerMenuCommand("🔄 重新处理", () => this.restart()); GM_registerMenuCommand("📊 显示统计", () => this.showStats()); GM_registerMenuCommand("🧹 清理缓存", () => this.clearCache()); } setupKeyboardShortcuts() { let inputFocused = false; // 监听输入框焦点状态 const handleFocusIn = DebounceUtils.debounce((e) => { if (e.target && e.target.matches && e.target.matches('input, textarea')) { inputFocused = true; } }, 100); const handleFocusOut = DebounceUtils.debounce((e) => { if (e.target && e.target.matches && e.target.matches('input, textarea')) { inputFocused = false; } }, 100); document.addEventListener('focusin', handleFocusIn); document.addEventListener('focusout', handleFocusOut); document.addEventListener('keydown', (e) => { if (inputFocused && !e.ctrlKey && !e.altKey) return; const key = `${e.altKey?1:0}${e.ctrlKey?1:0}${e.shiftKey?1:0}${e.key.toUpperCase()}`; switch (key) { case CONFIG.shortcuts.toggleScript: e.preventDefault(); this.toggle(); break; case CONFIG.shortcuts.showSettings: e.preventDefault(); this.settingsPanel.show(); break; case CONFIG.shortcuts.resetProcessing: e.preventDefault(); this.restart(); break; case CONFIG.shortcuts.debugMode: e.preventDefault(); this.toggleDebug(); break; } }); } async start() { if (this.isRunning) return; this.isRunning = true; // 初始处理 - 添加延迟避免重复 setTimeout(() => { if (this.isRunning) { this.processPage(); } }, 1000); // 设置定期处理 - 使用防抖 if (CONFIG.performance.processInterval > 0) { const debouncedProcess = DebounceUtils.throttleWithDebounce( () => this.conditionalProcess(), CONFIG.performance.processInterval, CONFIG.performance.debounceDelay ); this.intervalId = setInterval(debouncedProcess, CONFIG.performance.processInterval); } // 设置DOM观察器 this.setupMutationObserver(); // B站特殊处理 if (this.stateManager.isBilibili()) { this.setupBilibiliCommentObserver(); } if (CONFIG.features.debugMode) { console.log('🎀 猫娘化系统已启动完成喵~'); } } // 条件处理 - 避免重复执行 conditionalProcess() { if (!this.isRunning || this.processLock) return; const urlChanged = this.stateManager.checkUrlChange(); const commentChanged = this.stateManager.checkBilibiliCommentChange(); const shouldProcess = this.stateManager.shouldProcess(); if (urlChanged || commentChanged || (!this.stateManager.shouldSkipBilibiliProcessing() && shouldProcess)) { this.processPage(); } } processPage() { if (!this.isRunning || !CONFIG.features.autoProcessNewContent || this.processLock) { return; } // 防重复处理锁 this.processLock = true; try { const elements = this.getProcessableElements(); // 生成内容哈希,避免重复处理相同内容 const contentHash = this.generateContentHash(elements); if (contentHash === this.lastProcessHash) { return; } this.lastProcessHash = contentHash; if (elements.length === 0) { return; } // 使用时间切片处理 EnhancedPerformanceUtils.createTimeSliceProcessor( elements, (element) => this.processElement(element), { onProgress: CONFIG.features.debugMode ? (current, total, percent) => { if (current % 50 === 0) { console.log(`🐱 处理进度: ${percent.toFixed(1)}% (${current}/${total})`); } } : null, onComplete: () => { CONFIG.stats.lastActive = new Date().toISOString(); GM_setValue("catgirlConfig", CONFIG); if (this.stateManager.isBilibili()) { this.processBilibiliSpecial(); } } } ); } finally { // 延迟释放锁,避免过快的重复处理 setTimeout(() => { this.processLock = false; }, 1000); } } getProcessableElements() { const baseSelector = 'title, h1, h2, h3, h4, h5, h6, p, article, section, blockquote, li, a, span, div:not([class*="settings"]):not([id*="catgirl"])'; const inputSelector = CONFIG.features.affectInput ? ', input, textarea' : ''; const elements = document.querySelectorAll(baseSelector + inputSelector); // 过滤已处理和不需要处理的元素 return Array.from(elements).filter(element => { return !this.processedElements.has(element) && !element.closest('#catgirl-settings, #catgirl-debug') && this.shouldProcessElement(element); }); } shouldProcessElement(element) { // 检查元素是否有文本内容 if (!element.textContent?.trim()) return false; // 跳过已处理的内容 if (this.textProcessor.isProcessed(element.textContent)) return false; // 跳过脚本和样式标签 if (element.tagName && /^(SCRIPT|STYLE|NOSCRIPT)$/.test(element.tagName)) return false; // 跳过不可见元素 if (element.offsetParent === null && element.style.display !== 'none') return false; return true; } generateContentHash(elements) { // 简单的内容哈希生成 const textContent = elements.slice(0, 10).map(el => el.textContent?.slice(0, 50)).join('|'); return textContent.length + ':' + elements.length; } processElement(element) { if (!element || this.processedElements.has(element)) return; try { if (element.matches && element.matches('input, textarea') && CONFIG.features.affectInput) { if (element.value?.trim()) { element.value = this.textProcessor.processText(element.value); } } else { this.processElementText(element); } this.processedElements.add(element); CONFIG.stats.processedElements++; } catch (error) { if (CONFIG.features.debugMode) { // 使用新的错误处理方法 this.logError('处理元素出错', error); } } } processElementText(element) { // 检查是否只有纯文本内容 if (element.children.length === 0) { const newText = this.textProcessor.processText(element.textContent); if (newText !== element.textContent) { element.textContent = newText; } } else { // 处理文本节点 const textNodes = this.getTextNodes(element); textNodes.forEach(node => { if (node.textContent?.trim()) { const newText = this.textProcessor.processText(node.textContent); if (newText !== node.textContent) { node.textContent = newText; } } }); } } getTextNodes(element) { const textNodes = []; const walker = document.createTreeWalker( element, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { // 跳过脚本和样式内的文本 if (node.parentElement && /^(SCRIPT|STYLE|NOSCRIPT)$/.test(node.parentElement.tagName)) { return NodeFilter.FILTER_REJECT; } return node.textContent.trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; } } ); let node; while (node = walker.nextNode()) { textNodes.push(node); } return textNodes; } // ===== B站特殊处理 - 修复用户名处理问题 ===== processBilibiliSpecial() { if (this.stateManager.shouldSkipBilibiliProcessing()) return; if (CONFIG.features.debugMode) { console.log('🎯 执行B站特殊处理'); } try { // 方法1: 直接查找评论元素 this.processBilibiliComments(); // 方法2: 处理Shadow DOM中的评论 this.processBilibiliShadowDOM(); } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 B站处理出错:', error); } } this.stateManager.markBilibiliCompleted(); } // 新增:直接处理B站评论的方法 processBilibiliComments() { // 处理新版评论区 const commentSelectors = [ '.reply-item .reply-content:not([data-catgirl-processed])', '.comment-item .comment-content:not([data-catgirl-processed])', '.bili-comment-content:not([data-catgirl-processed])', '.reply-content:not([data-catgirl-processed])', '.comment-content:not([data-catgirl-processed])', '[data-e2e="reply-item"] .reply-content:not([data-catgirl-processed])', '.bili-comment .comment-text:not([data-catgirl-processed])' ]; commentSelectors.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(element => { if (!this.processedElements.has(element)) { element.setAttribute('data-catgirl-processed', 'true'); this.processElement(element); } }); }); // 处理用户名 - 使用更严格的选择器 const usernameSelectors = [ '.user-name:not([data-catgirl-processed])', '.reply-item .user-name:not([data-catgirl-processed])', '.comment-item .user-name:not([data-catgirl-processed])', '.bili-comment .user-name:not([data-catgirl-processed])', '.reply-author:not([data-catgirl-processed])', '.comment-author:not([data-catgirl-processed])' ]; usernameSelectors.forEach(selector => { const elements = document.querySelectorAll(selector); elements.forEach(element => { this.processBilibiliUsername(element); }); }); } // 新增:处理B站用户名 processBilibiliUsername(element) { if (!element || this.processedElements.has(element)) return; const userName = element.textContent?.trim(); if (!userName || !CONFIG.features.bilibiliRandomizeUserNames) return; // 严格检查是否已处理 if (this.hasUserPrefix(userName)) return; // 添加处理标记到元素 element.setAttribute('data-catgirl-processed', 'true'); this.processedElements.add(element); // 随机选择处理方式 const processingType = Math.random(); if (processingType < 0.3) { // 30% 概率:添加相同前后缀 const randomPrefix = this.getRandomUserPrefix(); element.textContent = `${randomPrefix}${userName}${randomPrefix}`; } else if (processingType < 0.6) { // 30% 概率:只添加前缀 const randomPrefix = this.getRandomUserPrefix(); element.textContent = `${randomPrefix}${userName}`; } else if (processingType < 0.8) { // 20% 概率:添加装饰性前缀 const decorative = cuteLibrary.decorativePrefixes[Math.floor(Math.random() * cuteLibrary.decorativePrefixes.length)]; element.textContent = `${decorative}${userName}`; } else { // 20% 概率:不处理,保持原样 return; } if (CONFIG.features.debugMode) { console.log('🎀 处理用户名:', userName, '->', element.textContent); } } // 修复:处理Shadow DOM中的B站评论 processBilibiliShadowDOM() { const biliComments = document.querySelector('bili-comments'); if (biliComments && biliComments.shadowRoot) { // 递归处理Shadow DOM this.processElementsInShadowDOM(biliComments.shadowRoot); } // 处理其他可能的Shadow DOM容器 const shadowHosts = document.querySelectorAll('bili-comment-thread-renderer, bili-comment-replies-renderer'); shadowHosts.forEach(host => { if (host.shadowRoot) { this.processElementsInShadowDOM(host.shadowRoot); } }); } // 新增:在Shadow DOM中处理元素 processElementsInShadowDOM(shadowRoot) { try { // 查找评论内容 - 添加防重复选择器 const contentSelectors = [ '#contents span:not([data-catgirl-processed])', '.comment-text:not([data-catgirl-processed])', '.reply-content:not([data-catgirl-processed])', '.comment-content:not([data-catgirl-processed])' ]; contentSelectors.forEach(selector => { const elements = shadowRoot.querySelectorAll(selector); elements.forEach(element => { if (!this.processedElements.has(element)) { element.setAttribute('data-catgirl-processed', 'true'); this.processElement(element); } }); }); // 查找用户名 - 添加防重复选择器 const usernameSelectors = [ '#user-name a:not([data-catgirl-processed])', '.user-name:not([data-catgirl-processed])', '.author-name:not([data-catgirl-processed])' ]; usernameSelectors.forEach(selector => { const elements = shadowRoot.querySelectorAll(selector); elements.forEach(element => { this.processBilibiliUsername(element); }); }); // 递归处理嵌套的Shadow DOM const nestedHosts = shadowRoot.querySelectorAll('*'); nestedHosts.forEach(el => { if (el.shadowRoot && !el.hasAttribute('data-catgirl-shadow-processed')) { el.setAttribute('data-catgirl-shadow-processed', 'true'); this.processElementsInShadowDOM(el.shadowRoot); } }); } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 处理Shadow DOM出错:', error); } } } processBilibiliCommentThread(thread) { if (!thread.shadowRoot) return; try { // 处理用户名 - 修复 prefixes 问题 const userNameElements = this.findInShadowDOM(thread.shadowRoot, CONFIG.sites.bilibili.userNameSelector); userNameElements.forEach(nameEl => { const userName = nameEl.textContent?.trim(); if (userName && !this.hasUserPrefix(userName)) { const randomPrefix = this.getRandomUserPrefix(); nameEl.textContent = `${randomPrefix}${userName}${randomPrefix}`; } }); // 处理评论内容 const contentElements = this.findInShadowDOM(thread.shadowRoot, CONFIG.sites.bilibili.contentSelector); contentElements.forEach(contentEl => { // 处理链接 - 将链接转换为文本 if (CONFIG.features.bilibiliMergeALinks) { const links = contentEl.querySelectorAll('a'); links.forEach(link => { const linkText = link.textContent?.trim(); if (linkText) { const textNode = document.createTextNode(linkText); link.parentNode.replaceChild(textNode, link); } }); } // 处理文本内容 const originalText = contentEl.textContent?.trim(); if (originalText && !this.textProcessor.isProcessed(originalText)) { const newText = this.textProcessor.processText(originalText); if (newText !== originalText) { contentEl.textContent = newText; } } }); } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 处理评论线程出错:', error); } } } // 修复:检查用户名是否已有前缀 hasUserPrefix(userName) { if (!userName) return true; // 空用户名视为已处理 // 检查是否包含任何用户前缀 const hasPrefix = cuteLibrary.userPrefixes.some(prefix => userName.includes(prefix)); // 检查是否包含装饰性前缀 const hasDecorative = cuteLibrary.decorativePrefixes.some(prefix => userName.includes(prefix)); // 检查是否已经被处理过的标记 const isProcessed = /已处理|🏳️⚧️.*🏳️⚧️|✨.*✨|💕.*💕/.test(userName); // 检查用户名长度,如果过长可能已经被处理过 const isTooLong = userName.length > 20; return hasPrefix || hasDecorative || isProcessed || isTooLong; } // 修复:获取随机用户前缀 getRandomUserPrefix() { const prefixes = cuteLibrary.userPrefixes; return prefixes[Math.floor(Math.random() * prefixes.length)]; } findInShadowDOM(element, selector) { const results = []; try { if (element.querySelectorAll) { results.push(...element.querySelectorAll(selector)); } // 递归搜索嵌套的 Shadow DOM if (element.querySelectorAll) { const allElements = element.querySelectorAll('*'); allElements.forEach(el => { if (el.shadowRoot) { results.push(...this.findInShadowDOM(el.shadowRoot, selector)); } }); } } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 搜索Shadow DOM出错:', error); } } return results; } // 设置B站评论观察器 - 无痕监控 setupBilibiliCommentObserver() { if (this.bilibiliCommentObserver) return; const targetNode = document.body; const config = { childList: true, subtree: true, attributes: false }; this.bilibiliCommentObserver = new MutationObserver( DebounceUtils.throttleWithDebounce((mutations) => { let hasCommentChanges = false; for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { // 检查多种B站评论相关的元素 if (node.matches && ( node.matches('bili-comment-thread-renderer, bili-comment, .reply-item, .comment-item') || (node.querySelector && ( node.querySelector('bili-comment-thread-renderer') || node.querySelector('.reply-item') || node.querySelector('.comment-item') || node.querySelector('.bili-comment') )) )) { hasCommentChanges = true; break; } } } if (hasCommentChanges) break; } if (hasCommentChanges) { // 延迟处理,让B站完成DOM更新 setTimeout(() => { if (CONFIG.features.debugMode) { console.log('🔄 检测到B站评论变化,开始处理'); } this.processBilibiliSpecial(); }, 1200); // 增加延迟时间 } }, 800, 400) // 降低节流时间 ); this.bilibiliCommentObserver.observe(targetNode, config); if (CONFIG.features.debugMode) { console.log('🎯 B站评论观察器已启动'); } } setupMutationObserver() { if (this.observer) return; const throttledCallback = DebounceUtils.throttleWithDebounce( (mutations) => this.handleMutations(mutations), CONFIG.performance.observerThrottle, CONFIG.performance.debounceDelay ); this.observer = new MutationObserver(throttledCallback); this.observer.observe(document.body, { childList: true, subtree: true, attributes: false, // 减少不必要的属性监听 characterData: false // 减少不必要的文本监听 }); } handleMutations(mutations) { if (this.processLock) return; try{ const elementsToProcess = new Set(); mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE && this.shouldProcessElement(node)) { elementsToProcess.add(node); // 处理子元素 const children = node.querySelectorAll && node.querySelectorAll('*'); if (children) { Array.from(children).forEach(child => { if (this.shouldProcessElement(child)) { elementsToProcess.add(child); } }); } } }); }); if (elementsToProcess.size > 0) { // 限制单次处理的元素数量 const limitedElements = Array.from(elementsToProcess).slice(0, 20); EnhancedPerformanceUtils.createTimeSliceProcessor( limitedElements, (element) => this.processElement(element), { onComplete: () => { CONFIG.stats.lastActive = new Date().toISOString(); } } ); } }catch(error){ this.logError('处理DOM变化出错', error); } } toggle() { if (this.isRunning) { this.stop(); showToast('猫娘化已暂停喵~ ⏸️', 'warning'); } else { this.start(); showToast('猫娘化已恢复喵~ ▶️', 'success'); } } stop() { this.isRunning = false; this.processLock = false; if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; } if (this.observer) { this.observer.disconnect(); this.observer = null; } if (this.bilibiliCommentObserver) { this.bilibiliCommentObserver.disconnect(); this.bilibiliCommentObserver = null; } } restart() { this.stop(); this.processedElements = new WeakSet(); this.textProcessor.processedTexts = new Set(); this.lastProcessHash = ''; setTimeout(() => { this.start(); showToast('系统已重启喵~ 🔄', 'info'); }, 500); } toggleDebug() { CONFIG.features.debugMode = !CONFIG.features.debugMode; GM_setValue("catgirlConfig", CONFIG); showToast(`调试模式${CONFIG.features.debugMode ? '开启' : '关闭'}喵~ 🐛`, 'info'); } showStats() { // 安全地获取统计数据,提供默认值 const processedElements = (CONFIG.stats.processedElements || 0); const replacedWords = (CONFIG.stats.replacedWords || 0); const sessionProcessed = (CONFIG.stats.sessionProcessed || 0); const installDate = CONFIG.stats.installDate || new Date().toISOString(); const lastActive = CONFIG.stats.lastActive || new Date().toISOString(); const stats = ` 📊 猫娘化统计信息: • 已处理元素: ${processedElements.toLocaleString()} • 已替换词汇: ${replacedWords.toLocaleString()} • 本次会话: ${sessionProcessed.toLocaleString()} • 当前版本: ${SCRIPT_VERSION} • 安装时间: ${new Date(installDate).toLocaleString()} • 最后活动: ${new Date(lastActive).toLocaleString()} • 当前状态: ${this.isRunning ? '运行中 ✅' : '已暂停 ⏸️'} `.trim(); alert(stats); } clearCache() { this.processedElements = new WeakSet(); this.textProcessor.processedTexts = new Set(); this.lastProcessHash = ''; CONFIG.stats.sessionProcessed = 0; GM_setValue("catgirlConfig", CONFIG); showToast('缓存已清理,统计已重置喵~ 🧹', 'success'); } waitForDOMReady() { return new Promise(resolve => { if (document.readyState !== 'loading') { resolve(); } else { document.addEventListener('DOMContentLoaded', resolve, { once: true }); } }); } } // ===== 修复版请求拦截器 ===== class RequestInterceptor { constructor() { this.originalXHR = XMLHttpRequest.prototype.send; this.handlers = new Map(); this.isStarted = false; } start() { if (this.isStarted) return; this.isStarted = true; const self = this; XMLHttpRequest.prototype.send = new Proxy(this.originalXHR, { apply: (target, thisArg, args) => { thisArg.addEventListener("load", (event) => { try { const xhr = event.target; // 修复:检查 responseType,避免 InvalidStateError if (xhr.responseType === '' || xhr.responseType === 'text') { const { responseText, responseURL } = xhr; if (responseText && /^{.*}$/.test(responseText.trim())) { const result = JSON.parse(responseText); self.executeHandlers(responseURL, result); } } else if (CONFIG.features.debugMode) { console.log('🐱 跳过非文本响应类型:', xhr.responseType); } } catch (err) { if (CONFIG.features.debugMode) { console.error('🐱 请求拦截处理出错:', err); } } }); return target.apply(thisArg, args); } }); } addHandler(pattern, handler) { this.handlers.set(pattern, handler); } executeHandlers(url, data) { for (const [pattern, handler] of this.handlers) { if (pattern.test(url)) { try { handler(data, url); } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 请求处理器出错:', error); } } } } } stop() { if (!this.isStarted) return; XMLHttpRequest.prototype.send = this.originalXHR; this.isStarted = false; } } // ===== 调试面板类 ===== class DebugPanel { constructor() { this.panel = null; this.isVisible = false; this.logs = []; this.maxLogs = 100; } create() { if (this.panel) return; this.panel = document.createElement('div'); this.panel.id = 'catgirl-debug'; this.panel.innerHTML = ` <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; background: rgba(0,0,0,0.8); padding: 8px; border-radius: 4px;"> <strong style="color: #00ff00;">🐱 调试面板</strong> <div> <button onclick="window.catgirlApp.clearDebugLog()" style="background:none;border:1px solid #00ff00;color:#00ff00;cursor:pointer;margin-right:5px;padding:2px 6px;font-size:10px;">清理</button> <button onclick="this.parentNode.parentNode.parentNode.style.display='none'" style="background:none;border:none;color:#ff6666;cursor:pointer;font-size:16px;">×</button> </div> </div> <div id="debug-content" style="max-height: 250px; overflow-y: auto; font-size: 11px; background: rgba(0,0,0,0.9); padding: 8px; border-radius: 4px;"></div> <div style="margin-top: 8px; font-size: 10px; opacity: 0.8; display: flex; justify-content: space-between;"> <span>内存: <span id="memory-usage">-</span></span> <span>FPS: <span id="fps-counter">-</span></span> <span>状态: <span id="status-indicator">运行中</span></span> </div> `; // 设置样式 this.panel.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: rgba(0,0,0,0.95); color: #00ff00; padding: 12px; border-radius: 8px; font-family: 'Courier New', monospace; font-size: 12px; max-width: 400px; z-index: 9999; border: 1px solid #333; box-shadow: 0 4px 12px rgba(0,0,0,0.5); `; document.body.appendChild(this.panel); this.startMonitoring(); } show() { if (!this.panel) this.create(); this.panel.style.display = 'block'; this.isVisible = true; this.log('调试面板已开启', 'info'); } hide() { if (this.panel) { this.panel.style.display = 'none'; this.isVisible = false; } } log(message, type = 'info') { const timestamp = new Date().toLocaleTimeString(); const logEntry = { message: `[${timestamp}] ${message}`, type: type, time: Date.now() }; this.logs.push(logEntry); if (this.logs.length > this.maxLogs) { this.logs.shift(); } this.updateDisplay(); } updateDisplay() { if (!this.panel || !this.isVisible) return; const content = document.getElementById('debug-content'); if (content) { content.innerHTML = this.logs.map(log => `<div style="color: ${this.getLogColor(log.type)}; margin: 2px 0;">${log.message}</div>` ).join(''); content.scrollTop = content.scrollHeight; } } getLogColor(type) { const colors = { info: '#00ff00', warn: '#ffaa00', error: '#ff3333', success: '#00aa00', debug: '#888888' }; return colors[type] || colors.info; } startMonitoring() { // 内存使用监控 if (performance.memory) { setInterval(() => { const memory = performance.memory; const usage = `${(memory.usedJSHeapSize / 1024 / 1024).toFixed(1)}MB`; const memoryEl = document.getElementById('memory-usage'); if (memoryEl) memoryEl.textContent = usage; }, 2000); } // FPS 监控 let lastTime = performance.now(); let frameCount = 0; const measureFPS = () => { frameCount++; const currentTime = performance.now(); if (currentTime >= lastTime + 1000) { const fps = Math.round((frameCount * 1000) / (currentTime - lastTime)); const fpsEl = document.getElementById('fps-counter'); if (fpsEl) fpsEl.textContent = fps; frameCount = 0; lastTime = currentTime; } requestAnimationFrame(measureFPS); }; measureFPS(); // 状态监控 setInterval(() => { const statusEl = document.getElementById('status-indicator'); if (statusEl && window.catgirlApp) { statusEl.textContent = window.catgirlApp.app.isRunning ? '运行中' : '已暂停'; statusEl.style.color = window.catgirlApp.app.isRunning ? '#00ff00' : '#ffaa00'; } }, 1000); } clearLogs() { this.logs = []; this.updateDisplay(); this.log('调试日志已清理', 'info'); } } // ===== 增强版应用类 - 添加缺失的性能监控方法 ===== class EnhancedCatgirlApp extends CatgirlApp { constructor() { super(); this.debugPanel = new DebugPanel(); this.requestInterceptor = new RequestInterceptor(); this.performanceMonitor = null; } async initialize() { // 先执行父类初始化 await super.initialize(); // 增强功能初始化 - 延迟执行避免依赖问题 setTimeout(() => { try { // 启动请求拦截 this.requestInterceptor.start(); this.setupRequestHandlers(); // 设置错误监控 this.setupErrorHandling(); // 调试面板 if (CONFIG.features.debugMode) { this.debugPanel.show(); } // 性能监控 if (CONFIG.features.performanceMonitoring) { this.startPerformanceMonitoring(); } } catch (error) { console.error('🐱 增强功能初始化失败:', error); } }, 1000); } // 新增:性能监控方法 startPerformanceMonitoring() { if (!performance || !performance.mark) { if (CONFIG.features.debugMode) { console.warn('🐱 浏览器不支持性能API'); } return; } if (this.performanceMonitor) { clearInterval(this.performanceMonitor); } this.performanceMonitor = setInterval(() => { try { performance.mark('catgirl-perf-start'); // 获取内存使用情况 const memUsage = performance.memory ? (performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(1) : 'N/A'; performance.mark('catgirl-perf-end'); performance.measure('catgirl-perf', 'catgirl-perf-start', 'catgirl-perf-end'); const measures = performance.getEntriesByType('measure'); const lastMeasure = measures[measures.length - 1]; // 性能警告 if (lastMeasure && lastMeasure.duration > 100) { if (this.debugPanel) { this.debugPanel.log(`性能警告: 检查耗时 ${lastMeasure.duration.toFixed(2)}ms, 内存: ${memUsage}MB`, 'warn'); } } // 内存警告 if (performance.memory && performance.memory.usedJSHeapSize > 100 * 1024 * 1024) { // 100MB if (this.debugPanel) { this.debugPanel.log(`内存警告: 当前使用 ${memUsage}MB`, 'warn'); } } // 清理旧的性能记录 if (measures.length > 50) { performance.clearMeasures('catgirl-perf'); performance.clearMarks('catgirl-perf-start'); performance.clearMarks('catgirl-perf-end'); } } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 性能监控出错:', error); } } }, 10000); // 每10秒检查一次 if (this.debugPanel) { this.debugPanel.log('性能监控已启动', 'info'); } } // 新增:停止性能监控 stopPerformanceMonitoring() { if (this.performanceMonitor) { clearInterval(this.performanceMonitor); this.performanceMonitor = null; if (this.debugPanel) { this.debugPanel.log('性能监控已停止', 'info'); } } } setupRequestHandlers() { try { // B站评论相关请求 this.requestInterceptor.addHandler( /x\/web-interface\/archive\/desc2|reply\/web/, (data, url) => this.handleBilibiliComment(data, url) ); // B站搜索请求 this.requestInterceptor.addHandler( /x\/web-interface\/search/, (data, url) => this.handleSearchResults(data, url) ); } catch (error) { if (CONFIG.features.debugMode) { console.error('🐱 设置请求处理器失败:', error); } } } handleBilibiliComment(data, url) { if (this.debugPanel) { this.debugPanel.log('检测到B站评论数据加载', 'info'); } setTimeout(() => { if (this.isRunning) { this.processBilibiliSpecial(); } }, 1500); } handleSearchResults(data, url) { if (this.debugPanel) { this.debugPanel.log('检测到搜索结果', 'info'); } setTimeout(() => { if (this.isRunning) { this.processPage(); } }, 800); } setupErrorHandling() { try { // 使用更安全的错误监听方式 window.addEventListener('error', (event) => { if (event.filename && event.filename.includes('catgirl')) { if (this.debugPanel) { this.debugPanel.log(`脚本错误: ${event.message} (第${event.lineno}行)`, 'error'); } } }); window.addEventListener('unhandledrejection', (event) => { if (this.debugPanel) { this.debugPanel.log(`未处理的Promise拒绝: ${event.reason}`, 'error'); } }); // 添加自定义错误日志方法 this.logError = (message, error = null) => { const errorMsg = error ? `${message}: ${error.message || error}` : message; if (this.debugPanel) { this.debugPanel.log(errorMsg, 'error'); } if (CONFIG.features.debugMode) { console.error('🐱 猫娘脚本错误:', errorMsg); } }; } catch (error) { console.error('🐱 错误处理设置失败:', error); } } toggleDebug() { super.toggleDebug(); if (CONFIG.features.debugMode) { this.debugPanel.show(); this.debugPanel.log('调试模式已开启', 'success'); // 同时启动性能监控 if (CONFIG.features.performanceMonitoring) { this.startPerformanceMonitoring(); } } else { this.debugPanel.hide(); this.stopPerformanceMonitoring(); } } clearDebugLog() { if (this.debugPanel) { this.debugPanel.clearLogs(); } } stop() { super.stop(); this.requestInterceptor.stop(); this.stopPerformanceMonitoring(); if (this.debugPanel) { this.debugPanel.log('系统已停止', 'warn'); } } // 重写清理缓存方法,包含前缀重复问题的清理 clearCache() { super.clearCache(); // 清理处理标记 const processedElements = document.querySelectorAll('[data-catgirl-processed]'); processedElements.forEach(el => { el.removeAttribute('data-catgirl-processed'); }); const processedShadows = document.querySelectorAll('[data-catgirl-shadow-processed]'); processedShadows.forEach(el => { el.removeAttribute('data-catgirl-shadow-processed'); }); // 清理性能记录 if (performance && performance.clearMeasures) { performance.clearMeasures('catgirl-perf'); performance.clearMarks('catgirl-perf-start'); performance.clearMarks('catgirl-perf-end'); } if (this.debugPanel) { this.debugPanel.log('缓存、标记和性能记录已清理', 'success'); } } // 新增:获取性能统计 getPerformanceStats() { if (!performance || !performance.memory) { return { memory: 'N/A', measures: 0, supported: false }; } return { memory: (performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(1) + 'MB', memoryLimit: (performance.memory.jsHeapSizeLimit / 1024 / 1024).toFixed(1) + 'MB', measures: performance.getEntriesByType('measure').length, supported: true }; } } // ===== 工具函数 ===== function showToast(message, type = 'info', duration = 3000) { const toast = document.createElement('div'); toast.className = 'catgirl-toast'; toast.innerHTML = ` <div class="toast-icon">${getToastIcon(type)}</div> <div class="toast-message">${message}</div> `; // 设置样式类 toast.classList.add(`toast-${type}`); document.body.appendChild(toast); // 显示动画 setTimeout(() => toast.classList.add('show'), 10); // 隐藏动画 setTimeout(() => { toast.classList.remove('show'); setTimeout(() => { if (toast.parentNode) { document.body.removeChild(toast); } }, 300); }, duration); } function getToastIcon(type) { const icons = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' }; return icons[type] || icons.info; } function showUpdateNotification() { showToast(`🎉 猫娘脚本已更新到 v${SCRIPT_VERSION} 喵~\n新增功能:智能处理、性能优化、UI美化`, 'success', 5000); } // ===== 启动增强版应用 ===== const enhancedApp = new EnhancedCatgirlApp(); // 启动应用 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => enhancedApp.initialize(), { once: true }); } else { setTimeout(() => enhancedApp.initialize(), 100); } // ===== 样式注入 - 优化UI ===== GM_addStyle(` /* 设置面板样式 - 现代化设计 */ #catgirl-settings { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif; line-height: 1.5; } #catgirl-settings .settings-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 12px 12px 0 0; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } #catgirl-settings .settings-header h3 { margin: 0; font-size: 20px; font-weight: 600; } #catgirl-settings .close-btn { background: rgba(255,255,255,0.2); border: none; color: white; font-size: 24px; cursor: pointer; width: 32px; height: 32px; border-radius: 50%; transition: all 0.3s ease; display: flex; align-items: center; justify-content: center; } #catgirl-settings .close-btn:hover { background: rgba(255,255,255,0.3); transform: scale(1.1); } #catgirl-settings .settings-content { padding: 24px; background: #ffffff; border-radius: 0 0 12px 12px; } #catgirl-settings .settings-tabs { display: flex; margin-bottom: 24px; background: #f8f9fa; border-radius: 8px; padding: 4px; } #catgirl-settings .tab-btn { flex: 1; padding: 12px 16px; border: none; background: transparent; cursor: pointer; border-radius: 6px; transition: all 0.3s ease; font-weight: 500; color: #6c757d; } #catgirl-settings .tab-btn.active { background: white; color: #495057; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } #catgirl-settings .tab-btn:hover:not(.active) { color: #495057; background: rgba(255,255,255,0.7); } #catgirl-settings .setting-group { margin-bottom: 20px; } #catgirl-settings label { display: block; margin-bottom: 8px; font-weight: 500; color: #333; font-size: 14px; } #catgirl-settings small { display: block; color: #6c757d; font-size: 12px; margin-top: 4px; line-height: 1.4; } #catgirl-settings select, #catgirl-settings input[type="text"], #catgirl-settings input[type="range"], #catgirl-settings textarea { width: 100%; padding: 10px 12px; border: 2px solid #e9ecef; border-radius: 6px; font-size: 14px; transition: all 0.3s ease; background: #fff; box-sizing: border-box; } #catgirl-settings select:focus, #catgirl-settings input:focus, #catgirl-settings textarea:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } #catgirl-settings input[type="checkbox"] { width: auto; margin-right: 8px; transform: scale(1.2); accent-color: #667eea; } #catgirl-settings input[type="range"] { height: 6px; -webkit-appearance: none; background: #e9ecef; border-radius: 3px; padding: 0; } #catgirl-settings input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 20px; height: 20px; border-radius: 50%; background: #667eea; cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,0.2); } #catgirl-settings .stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 20px; } #catgirl-settings .stat-item { text-align: center; padding: 16px; background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 8px; border: 1px solid #dee2e6; } #catgirl-settings .stat-number { font-size: 24px; font-weight: 700; color: #667eea; margin-bottom: 4px; } #catgirl-settings .stat-label { font-size: 12px; color: #6c757d; font-weight: 500; } #catgirl-settings .info-section { background: #f8f9fa; padding: 16px; border-radius: 8px; border-left: 4px solid #667eea; } #catgirl-settings .info-section h4 { margin: 0 0 12px 0; color: #495057; font-size: 16px; } #catgirl-settings .info-section p { margin: 6px 0; color: #6c757d; font-size: 14px; } #catgirl-settings .actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 24px; padding-top: 24px; border-top: 1px solid #e9ecef; } #catgirl-settings button { padding: 12px 20px; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.3s ease; flex: 1; min-width: 100px; display: flex; align-items: center; justify-content: center; gap: 6px; } #catgirl-settings .btn-primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; } #catgirl-settings .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); } #catgirl-settings .btn-warning { background: linear-gradient(135deg, #ffc107 0%, #ff8c00 100%); color: #212529; } #catgirl-settings .btn-warning:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(255, 193, 7, 0.4); } #catgirl-settings .btn-secondary { background: linear-gradient(135deg, #6c757d 0%, #495057 100%); color: white; } #catgirl-settings .btn-secondary:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(108, 117, 125, 0.4); } #catgirl-settings .btn-info { background: linear-gradient(135deg, #17a2b8 0%, #138496 100%); color: white; } #catgirl-settings .btn-info:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(23, 162, 184, 0.4); } /* Toast 通知样式 - 现代化 */ .catgirl-toast { position: fixed; top: 20px; right: 20px; z-index: 10001; padding: 16px 20px; border-radius: 12px; font-size: 14px; font-weight: 500; max-width: 350px; box-shadow: 0 8px 32px rgba(0,0,0,0.15); transform: translateX(120%); transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); backdrop-filter: blur(10px); display: flex; align-items: center; gap: 12px; } .catgirl-toast.show { transform: translateX(0); } .catgirl-toast.toast-success { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; } .catgirl-toast.toast-error { background: linear-gradient(135deg, #dc3545 0%, #e74c3c 100%); color: white; } .catgirl-toast.toast-warning { background: linear-gradient(135deg, #ffc107 0%, #fd7e14 100%); color: #212529; } .catgirl-toast.toast-info { background: linear-gradient(135deg, #17a2b8 0%, #6f42c1 100%); color: white; } .catgirl-toast .toast-icon { font-size: 18px; } .catgirl-toast .toast-message { flex: 1; line-height: 1.4; } /* 调试面板样式优化 */ #catgirl-debug { font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace; backdrop-filter: blur(10px); } /* 响应式设计 */ @media (max-width: 600px) { #catgirl-settings { width: 90vw; max-height: 90vh; } #catgirl-settings .actions { flex-direction: column; } #catgirl-settings .stats-grid { grid-template-columns: 1fr; } } /* 滚动条美化 */ #catgirl-settings ::-webkit-scrollbar { width: 8px; } #catgirl-settings ::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } #catgirl-settings ::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 4px; } #catgirl-settings ::-webkit-scrollbar-thumb:hover { background: #a8a8a8; } `); // ===== 全局调试接口 ===== window.catgirlApp = { app: enhancedApp, config: CONFIG, version: SCRIPT_VERSION, utils: { DebounceUtils, EnhancedPerformanceUtils, StateManager, EnhancedTextProcessor, ContextAnalyzer, SettingsPanel, DebugPanel, RequestInterceptor }, clearCache: function() { enhancedApp.clearCache(); }, clearDebugLog: function() { enhancedApp.clearDebugLog(); } }; // ← 添加了这个分号 })(); // ← 添加了这两个闭合括号和分号
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址