// ==UserScript== // @name 🐱 全世界都要变成可爱猫猫!增强版 // @version 4.0.1 // @description 让整个网络世界都变成超可爱的猫娘语调喵~增强版(修复版) // @author 超萌猫娘开发队 // @match *://*/* // @include *://*.bilibili.com/video/* // @include *://*.bilibili.com/anime/* // @include *://*.bilibili.com/bangumi/play/* // @exclude *://greasyfork.org/* // @exclude *://*.gov/* // @exclude *://*.edu/* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @license MIT // @namespace https://greasyfork.org/users/1503554 // @downloadURL none // ==/UserScript== (function() { 'use strict'; // ===== 版本管理 ===== const SCRIPT_VERSION = "4.0.1"; const isVersionUpdate = GM_getValue("SCRIPT_VERSION") !== SCRIPT_VERSION; if (isVersionUpdate) { GM_setValue("SCRIPT_VERSION", SCRIPT_VERSION); console.log('🎉 猫娘脚本已更新到版本', SCRIPT_VERSION); } // ===== 增强配置系统 ===== const defaultConfig = { // 性能配置 performance: { processInterval: 2500, maxProcessingTimeSlice: 16, batchSize: 10, observerThrottle: 300, maxRetryAttempts: 15, idleCallbackTimeout: 1000 }, // 功能开关 features: { affectInput: false, bilibiliMergeALinks: true, bilibiliRandomizeUserNames: true, autoProcessNewContent: true, shadowDomSupport: true, performanceMonitoring: false, debugMode: false }, // 站点配置 sites: { excludeDomains: [ 'github.com', 'stackoverflow.com', 'google.com', 'gov.cn', 'edu.cn', 'greasyfork.org' ], bilibili: { smartPause: true, retryInterval: 500, maxRetryDelay: 2000 } }, // 快捷键配置 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' // smart, aggressive, gentle }, // 统计信息 stats: { processedElements: 0, replacedWords: 0, lastActive: new Date().toISOString(), installDate: new Date().toISOString() } }; // 加载用户配置 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~', '喵喵', 'にゃ♡', 'mew~'], high: ['喵~', 'にゃん♪', '喵呜', 'nya~', '喵喵', 'にゃ♡', 'mew~', '喵♪', 'nyaa', '喵desu', '喵呢', '喵哈'] }, prefixes: ['🏳️‍⚧️', '✨', '💕', '🌸'], // 根据语境的智能替换 contextualReplacements: { angry: ['生气的小猫咪', '炸毛的喵喵', '不开心的小家伙'], happy: ['开心的小喵~', '快乐的猫猫', '兴奋的小家伙'], sad: ['难过的小喵', '想要抱抱的猫猫', '需要安慰的小家伙'] } }; // ===== 增强的工具类 ===== class EnhancedPerformanceUtils { static createTimeSliceProcessor(items, processor, options = {}) { const { batchSize = CONFIG.performance.batchSize, maxTime = CONFIG.performance.maxProcessingTimeSlice, onProgress = null, onComplete = null } = options; let index = 0; let startTime = Date.now(); const processNextBatch = () => { const batchStartTime = performance.now(); let processedInBatch = 0; while (index < items.length && processedInBatch < batchSize && (performance.now() - batchStartTime) < maxTime) { try { processor(items[index], index, items); } catch (error) { console.error('🐱 处理项目出错:', error); } index++; processedInBatch++; } // 进度回调 if (onProgress) { onProgress(index, items.length, (index / items.length) * 100); } if (index < items.length) { // 使用 requestIdleCallback 或 setTimeout if (window.requestIdleCallback) { requestIdleCallback(processNextBatch, { timeout: CONFIG.performance.idleCallbackTimeout }); } else { setTimeout(processNextBatch, 0); } } else { console.log(`🎉 完成处理 ${items.length} 个项目,耗时 ${Date.now() - startTime}ms`); if (onComplete) onComplete(); } }; processNextBatch(); } 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: [], // 确保初始化为数组 // B站特殊状态 bilibili: { isCompleted: false, lastProcessedUrl: '', lastProcessedTime: 0, retryCount: 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) { 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; // 触发回调 - 添加安全检查 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; } shouldSkipBilibiliProcessing() { if (!this.isBilibili()) return true; const { isCompleted, lastProcessedUrl, lastProcessedTime } = this.state.bilibili; const now = Date.now(); return isCompleted && lastProcessedUrl === location.href && (now - lastProcessedTime) < 60000; // 1分钟内不重复 } 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'); } } // ===== 增强的文本处理器 ===== class EnhancedTextProcessor { constructor() { this.processedTexts = new Set(); this.replacementStats = new Map(); } isProcessed(text) { return /喵[~~呜哈呢♪♡]|nya|にゃん|meow|🏳️‍⚧️/i.test(text); } getCuteEnding() { const level = CONFIG.preferences.cuteLevel; const endings = cuteLibrary.endings[level] || cuteLibrary.endings.normal; return endings[Math.floor(Math.random() * endings.length)]; } // 智能语境分析 analyzeContext(text) { if (/[!!??]/.test(text)) return 'excited'; if (/[。.,,;;]/.test(text)) return 'calm'; if (/草|傻|笑/.test(text)) return 'happy'; if (/生气|愤怒|讨厌/.test(text)) return 'angry'; return 'neutral'; } processText(text, options = {}) { if (!text?.trim() || this.isProcessed(text)) return text; // 检查是否在禁用词列表中 if (CONFIG.preferences.disabledWords.some(word => text.includes(word))) { return text; } let result = text; let replacementCount = 0; // 应用脏词替换(保持原有逻辑) const cleanups = this.getCleanupRules(); cleanups.forEach(([regex, replacement]) => { const matches = result.match(regex); if (matches) { result = result.replace(regex, replacement); replacementCount += matches.length; } }); // 智能添加可爱尾缀 if (CONFIG.preferences.processingMode !== 'gentle' || replacementCount > 0) { result = this.addCuteEndings(result); } // 更新统计 if (replacementCount > 0) { CONFIG.stats.replacedWords += replacementCount; this.updateReplacementStats(text, result); } return result; } addCuteEndings(text) { const getCuteEnding = () => this.getCuteEnding(); const addDesu = () => Math.random() < 0.2 ? 'です' : ''; return text .replace(/([也矣兮乎者焉哉]|[啊吗呢吧哇呀哦嘛喔咯呜捏])([\s\p{P}]|$)/gu, (_, $1, $2) => `${getCuteEnding()}${addDesu()}${$2}`) .replace(/([的了辣])([\s\p{P}]|$)/gu, (_, $1, $2) => Math.random() < 0.3 ? `${$1}${getCuteEnding()}${addDesu()}${$2}` : `${$1}${$2}`); } getCleanupRules() { // 返回清理规则数组 return [ // --------- 极端攻击与侮辱性词汇 --------- [/操你妈|操你娘|操你全家|肏你妈|干你妈|干你娘|去你妈的|去你娘的|去你全家/gi, '去睡觉喵~'], [/妈了个?逼|妈的?智障|妈的/gi, '喵喵~'], [/狗娘养的|狗杂种|狗东西|狗逼/gi, '不太好的小喵~'], [/操你大爷|去你大爷的/gi, '去玩耍啦喵~'], [/去你老师的|你全家死光/gi, '嗯...安静一点喵'], [/你妈死了|你妈没了/gi, '你妈妈叫你回家吃饭喵~'], // --------- 性相关及不雅词汇 --------- [/鸡巴|鸡叭|鸡把|\bjb\b|\bJB\b/gi, '小鱼干喵~'], [/逼你|逼样|逼毛|逼崽子|什么逼/gi, '小淘气喵'], [/肏|干你|草你|cao你|cao你妈/gi, '去玩耍啦喵~'], [/c[a@4]o|g[a@4]n/gi, '去玩耍啦喵~'], [/j[i1!]\s*b[a@4]?/gi, '小鱼干喵~'], // --------- 常见人身攻击与负面评价 --------- [/你妹|你妹的|去你妹/gi, '小迷糊喵'], [/滚犊子|滚蛋|滚开|滚开你/gi, '去玩耍啦喵~'], [/去死|死全家|死开|狗带/gi, '去睡觉觉'], [/人渣|渣人|渣男|渣女/gi, '要抱抱的小喵~'], [/废物|废柴|废狗/gi, '要抱抱的小喵~'], [/垃圾人|辣鸡|拉圾/gi, '不太好的小喵~'], [/王八蛋|王八羔子/gi, '坏坏的小喵'], [/丑八怪|丑陋|矮冬瓜/gi, '外表也很可爱喵~'], [/心机[婊表]/gi, '聪明的小家伙'], // --------- 智力与能力相关的攻击性词语 --------- [/傻逼|煞笔|沙雕|傻叉|笨蛋|二货|二逼/gi, '小糊涂喵'], [/白痴|智障|弱智|脑残|脑袋进水/gi, '小呆呆喵'], [/蠢货|蠢蛋/gi, '有点迷糊喵'], [/傻[逼比屄]|煞[笔b]/gi, '小糊涂虫'], [/脑[残残蛋]/gi, '小呆呆喵'], // --------- 拼音缩写与网络黑话 --------- [/\bcnm\b|\bCNM\b|c\s*n\s*m/gi, '你好软糯喵~'], [/\bnmsl\b|\bNMSL\b/gi, '你超棒棒喵~'], [/\bnmb\b|\bNMB\b/gi, '你好有趣喵~'], [/\bmlgb\b|\bMLGB\b/gi, '哇好厉害喵~'], [/\bwqnmd\b|\bWQNMD\b/gi, '我觉得你超棒喵~'], [/\b(qm?nd?m?d?|qnmd)\b/gi, '去学习喵~'], [/tmd|TMD/gi, '太萌啦喵~'], [/wtf|WTF/gi, '哇好神奇喵~'], [/\bwc\b|WC(?!厕所)/gi, '哇塞呀喵~'], [/\bsb\b|\bSB\b/gi, '小宝贝喵~'], [/nsdd/gi, '你说的对喵~'], [/u1s1/gi, '有一说一喵~'], // ========= “阴阳怪气”与嘲讽专用 (新增) ========= [/不会吧不会吧|不会吧/gi, '喵喵惊讶~'], [/就这\?|就这?/gi, '就这个喵?'], [/他急了|你急了|别急/gi, '顺顺毛,不着急喵~'], [/典中典|太典了|典/gi, '是好有趣的故事喵~'], [/孝死|孝子|太孝了/gi, '是热心的好孩子喵~'], [/差不多得了|彳亍口巴/gi, '好啦好啦喵~'], [/你说的都对|啊对对对/gi, '嗯嗯,你说的对喵~'], [/我不好说|我不好说我劝你别说/gi, '喵~让我想想~'], [/不然呢|要不然呢/gi, '就是这样喵~'], [/您吉祥/gi, '给你请安喵~'], [/味儿太冲了|味太冲/gi, '味道很特别喵~'], [/呃呃/gi, '喵喵喵?'], // ========= 网络流行语与社区用语 (扩充) ========= [/喷子|喷子们/gi, '有想法的小喵'], [/键盘侠|键盘战士/gi, '网络小达人喵'], [/杠精|杠精们/gi, '爱讨论的小喵'], [/舔狗/gi, '忠实的小喵'], [/卫兵|小丑|水军|海军|节奏/gi, '热心的小喵~'], [/笑死我了|笑死|xswl|XSWL/gi, '好有趣喵~'], [/难绷|难蚌|难崩/gi, '有点害羞喵~'], [/抽象|纯抽象|太抽象了/gi, '很有想象力喵~'], [/\bemo\b|EMO/gi, '有小情绪喵~'], [/ptsd|PTSD/gi, '印象很深喵~'], [/绝绝子/gi, '超级棒棒喵~'], [/咱就是说/gi, '人家觉得喵~'], [/栓q|拴q/gi, '谢谢呀喵~'], [/6到飞起|666/gi, '超级厉害喵~'], [/芭比q了/gi, '糟糕啦喵~'], [/yyds|YYDS/gi, '最棒棒喵~'], [/up主废了|up废了/gi, 'up主好有趣喵~'], [/这up不行/gi, '这up很特别喵~'], [/破防了/gi, '心弦被拨动了喵~'], [/下头|真下头/gi, '有点点上头喵~'], [/老六/gi, '爱捉迷藏的小喵~'], [/要确欸/gi, '要确定一下喵?'], [/尊嘟假嘟/gi, '真的喵还是假的喵?'], [/硬控/gi, '被吸引住了喵~'], // ========= 行为与状态描述词 ========= [/白嫖|白摸|白吃/gi, '免费享受喵~'], [/爆肝|肝帝/gi, '很努力喵~'], [/摆烂|躺平/gi, '想要休息喵~'], // ========= 新增与优化 (合并至以上分类) ========= [/我靠|我擦|我操|卧槽/gi, '哇哦喵~'], [/nt|脑瘫/gi, '小迷糊喵~'], [/kys|KYS/gi, '要开心喵~'], [/小黑子|ikun/gi, '淘气的粉丝喵~'], [/你是懂xx的/gi, '你是懂喵喵的~'], [/离谱/gi, '好有想象力喵~'], [/awsl|阿伟死了/gi, '好可爱喵~'], [/泰裤辣/gi, '太酷了喵~'], [/豪庭/gi,'咱喵的耳朵要怀孕惹!'], ]; } updateReplacementStats(original, processed) { const key = `${original.length}:${processed.length}`; this.replacementStats.set(key, (this.replacementStats.get(key) || 0) + 1); } } // ===== 设置面板类 ===== 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 `

🐱 猫娘化设置

统计信息

已处理元素: ${CONFIG.stats.processedElements}

已替换词汇: ${CONFIG.stats.replacedWords}

安装时间: ${new Date(CONFIG.stats.installDate).toLocaleString()}

`; } getCSS() { return ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 400px; background: white; border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 10000; font-family: Arial, sans-serif; display: none; `; } bindEvents() { // 保存设置 const saveBtn = document.getElementById('save-settings'); if (saveBtn) { saveBtn.onclick = () => { this.saveSettings(); this.hide(); showToast('设置已保存喵~'); }; } // 重置设置 const resetBtn = document.getElementById('reset-settings'); if (resetBtn) { resetBtn.onclick = () => { if (confirm('确定要重置所有设置吗?')) { this.resetSettings(); showToast('设置已重置喵~'); } }; } // 清理缓存 const clearBtn = document.getElementById('clear-cache'); if (clearBtn) { clearBtn.onclick = () => { this.clearCache(); showToast('缓存已清理喵~'); }; } } show() { if (!this.panel) this.create(); // 加载当前设置 this.loadCurrentSettings(); this.panel.style.display = 'block'; this.isVisible = true; } 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 debugModeEl = document.getElementById('debug-mode'); if (cuteLevelEl) cuteLevelEl.value = CONFIG.preferences.cuteLevel; if (processingModeEl) processingModeEl.value = CONFIG.preferences.processingMode; if (affectInputEl) affectInputEl.checked = CONFIG.features.affectInput; if (debugModeEl) debugModeEl.checked = CONFIG.features.debugMode; } saveSettings() { const cuteLevelEl = document.getElementById('cute-level'); const processingModeEl = document.getElementById('processing-mode'); const affectInputEl = document.getElementById('affect-input'); const debugModeEl = document.getElementById('debug-mode'); if (cuteLevelEl) CONFIG.preferences.cuteLevel = cuteLevelEl.value; if (processingModeEl) CONFIG.preferences.processingMode = processingModeEl.value; if (affectInputEl) CONFIG.features.affectInput = affectInputEl.checked; if (debugModeEl) CONFIG.features.debugMode = debugModeEl.checked; GM_setValue("catgirlConfig", CONFIG); } resetSettings() { CONFIG = Object.assign({}, defaultConfig); GM_setValue("catgirlConfig", CONFIG); this.loadCurrentSettings(); } clearCache() { if (window.catgirlApp && window.catgirlApp.performance) { window.catgirlApp.performance.clearCache(); } } } // ===== 主应用类 ===== 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; } async initialize() { if (this.shouldExclude()) { console.log('🐱 域名已排除,不启动喵~'); return; } console.log('🐱 增强版猫娘化系统启动喵~'); // 注册菜单命令 this.registerMenuCommands(); // 设置快捷键 this.setupKeyboardShortcuts(); // 等待DOM就绪 await this.waitForDOMReady(); // 设置URL变化监听 this.stateManager.onUrlChange(() => { setTimeout(() => this.processPage(), 1000); }); // 开始处理 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()); } setupKeyboardShortcuts() { let focus = false; document.addEventListener('focusin', (e) => { if (e.target && e.target.matches && e.target.matches('input, textarea')) { focus = true; } }); document.addEventListener('focusout', (e) => { if (e.target && e.target.matches && e.target.matches('input, textarea')) { focus = false; } }); document.addEventListener('keydown', (e) => { if (focus && !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: 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; // 初始处理 this.processPage(); // 设置定期处理 if (CONFIG.performance.processInterval > 0) { this.intervalId = setInterval(() => { if (this.stateManager.checkUrlChange() || !this.stateManager.shouldSkipBilibiliProcessing()) { this.processPage(); } }, CONFIG.performance.processInterval); } // 设置DOM观察器 this.setupMutationObserver(); console.log('🎀 猫娘化系统已启动完成喵~'); } processPage() { if (!this.isRunning || !CONFIG.features.autoProcessNewContent) return; const elements = document.querySelectorAll( 'title, h1, h2, h3, h4, h5, h6, p, article, section, blockquote, li, a, span, div' + (CONFIG.features.affectInput ? ', input, textarea' : '') ); EnhancedPerformanceUtils.createTimeSliceProcessor( Array.from(elements), (element) => this.processElement(element), { onProgress: CONFIG.features.debugMode ? (current, total, percent) => { if (current % 100 === 0) { console.log(`🐱 处理进度: ${percent.toFixed(1)}%`); } } : null, onComplete: () => { CONFIG.stats.lastActive = new Date().toISOString(); if (this.stateManager.isBilibili()) { this.processBilibiliSpecial(); } } } ); } 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) { console.error('🐱 处理元素出错:', error); } } processElementText(element) { if (element.textContent === element.innerHTML.trim()) { const newText = this.textProcessor.processText(element.textContent); if (newText !== element.textContent) { element.textContent = newText; } } else { element.childNodes.forEach(node => { if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()) { const newText = this.textProcessor.processText(node.textContent); if (newText !== node.textContent) { node.textContent = newText; } } }); } } processBilibiliSpecial() { if (this.stateManager.shouldSkipBilibiliProcessing()) return; console.log('🎯 执行B站特殊处理'); try { const biliComments = document.querySelector('bili-comments'); if (biliComments && biliComments.shadowRoot) { const commentThreads = biliComments.shadowRoot.querySelectorAll('bili-comment-thread-renderer'); commentThreads.forEach(thread => { if (thread.shadowRoot) { this.processBilibiliCommentThread(thread); } }); } } catch (error) { console.error('🐱 B站处理出错:', error); } this.stateManager.markBilibiliCompleted(); } processBilibiliCommentThread(thread) { if (!thread.shadowRoot) return; try { // 处理用户名 const userNameElements = this.findInShadowDOM(thread.shadowRoot, '#user-name a'); userNameElements.forEach(nameEl => { const userName = nameEl.textContent?.trim(); if (userName && !userName.includes('🏳️‍⚧️')) { nameEl.textContent = `🏳️‍⚧️${userName}🏳️‍⚧️`; } }); // 处理评论内容 const contentElements = this.findInShadowDOM(thread.shadowRoot, '#contents span'); contentElements.forEach(contentEl => { // 处理链接 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) { console.error('🐱 处理评论线程出错:', error); } } findInShadowDOM(element, selector) { const results = []; try { if (element.querySelectorAll) { results.push(...element.querySelectorAll(selector)); } if (element.querySelectorAll) { const allElements = element.querySelectorAll('*'); allElements.forEach(el => { if (el.shadowRoot) { results.push(...this.findInShadowDOM(el.shadowRoot, selector)); } }); } } catch (error) { console.error('🐱 搜索Shadow DOM出错:', error); } return results; } setupMutationObserver() { const throttledCallback = EnhancedPerformanceUtils.throttleWithLeading( (mutations) => this.handleMutations(mutations), CONFIG.performance.observerThrottle ); this.observer = new MutationObserver(throttledCallback); this.observer.observe(document.body, { childList: true, subtree: true }); } handleMutations(mutations) { const elementsToProcess = new Set(); mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { elementsToProcess.add(node); } }); }); elementsToProcess.forEach(element => { this.processElement(element); }); } toggle() { if (this.isRunning) { this.stop(); showToast('猫娘化已暂停喵~'); } else { this.start(); showToast('猫娘化已恢复喵~'); } } stop() { this.isRunning = false; if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; } if (this.observer) { this.observer.disconnect(); this.observer = null; } } restart() { this.stop(); this.processedElements = new WeakSet(); setTimeout(() => this.start(), 100); showToast('系统已重启喵~'); } toggleDebug() { CONFIG.features.debugMode = !CONFIG.features.debugMode; GM_setValue("catgirlConfig", CONFIG); showToast(`调试模式${CONFIG.features.debugMode ? '开启' : '关闭'}喵~`); } showStats() { const stats = ` 📊 猫娘化统计信息: • 已处理元素: ${CONFIG.stats.processedElements} • 已替换词汇: ${CONFIG.stats.replacedWords} • 当前版本: ${SCRIPT_VERSION} • 安装时间: ${new Date(CONFIG.stats.installDate).toLocaleString()} • 最后活动: ${new Date(CONFIG.stats.lastActive).toLocaleString()} `.trim(); alert(stats); } waitForDOMReady() { return new Promise(resolve => { if (document.readyState !== 'loading') { resolve(); } else { document.addEventListener('DOMContentLoaded', resolve, { once: true }); } }); } } // ===== 调试面板类 ===== class DebugPanel { constructor() { this.panel = null; this.isVisible = false; this.logs = []; this.maxLogs = 50; } create() { if (this.panel) return; this.panel = document.createElement('div'); this.panel.id = 'catgirl-debug'; this.panel.innerHTML = `
🐱 调试信息
内存: - | FPS: -
`; document.body.appendChild(this.panel); this.startMonitoring(); } show() { if (!this.panel) this.create(); this.panel.style.display = 'block'; this.isVisible = true; } hide() { if (this.panel) { this.panel.style.display = 'none'; this.isVisible = false; } } log(message, type = 'info') { const timestamp = new Date().toLocaleTimeString(); const logEntry = `[${timestamp}] ${message}`; this.logs.push({ message: logEntry, type }); 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 => `
${log.message}
` ).join(''); content.scrollTop = content.scrollHeight; } } getLogColor(type) { const colors = { info: '#00ff00', warn: '#ffaa00', error: '#ff0000', success: '#00aa00' }; 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; }, 1000); } // 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(); } } // ===== 进度指示器类 ===== class ProgressIndicator { constructor() { this.bar = null; } show(progress = 0) { if (!this.bar) { this.bar = document.createElement('div'); this.bar.className = 'catgirl-progress'; document.body.appendChild(this.bar); } this.bar.style.width = `${Math.min(100, Math.max(0, progress))}%`; if (progress >= 100) { setTimeout(() => this.hide(), 500); } } hide() { if (this.bar) { this.bar.remove(); this.bar = null; } } } // ===== HTTP 请求拦截器 ===== class RequestInterceptor { constructor() { this.originalXHR = XMLHttpRequest.prototype.send; this.handlers = new Map(); } start() { const self = this; XMLHttpRequest.prototype.send = new Proxy(this.originalXHR, { apply: (target, thisArg, args) => { thisArg.addEventListener("load", (event) => { try { const { responseText, responseURL } = event.target; if (/^{.*}$/.test(responseText)) { const result = JSON.parse(responseText); self.executeHandlers(responseURL, result); } } catch (err) { 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) { console.error('🐱 请求处理器出错:', error); } } } } stop() { XMLHttpRequest.prototype.send = this.originalXHR; } } // ===== 增强版应用类 ===== class EnhancedCatgirlApp extends CatgirlApp { constructor() { super(); this.debugPanel = new DebugPanel(); this.progressIndicator = new ProgressIndicator(); this.requestInterceptor = new RequestInterceptor(); this.retryQueue = new Map(); } async initialize() { // 先执行父类初始化 await super.initialize(); // 增强功能初始化 if (CONFIG.features.debugMode) { this.debugPanel.show(); } // 启动请求拦截 this.requestInterceptor.start(); this.setupRequestHandlers(); // 设置错误监控 this.setupErrorHandling(); // 性能监控 if (CONFIG.features.performanceMonitoring) { this.startPerformanceMonitoring(); } } setupRequestHandlers() { // B站评论相关请求 this.requestInterceptor.addHandler( /x\/web-interface\/archive\/desc2/, (data, url) => this.handleBilibiliComment(data, url) ); // B站搜索请求 this.requestInterceptor.addHandler( /x\/web-interface\/search/, (data, url) => this.handleSearchResults(data, url) ); // B站番剧请求 this.requestInterceptor.addHandler( /pgc\/view\/web\/season/, (data, url) => this.handleBangumiInfo(data, url) ); } handleBilibiliComment(data, url) { this.debugPanel.log('检测到B站评论数据', 'info'); setTimeout(() => this.processBilibiliSpecial(), 1000); } handleSearchResults(data, url) { this.debugPanel.log('检测到搜索结果', 'info'); setTimeout(() => this.processPage(), 500); } handleBangumiInfo(data, url) { this.debugPanel.log('检测到番剧信息', 'info'); setTimeout(() => this.processBilibiliSpecial(), 800); } setupErrorHandling() { window.addEventListener('error', (event) => { if (event.filename && event.filename.includes('catgirl')) { this.debugPanel.log(`错误: ${event.message}`, 'error'); console.error('🐱 脚本错误:', event); } }); window.addEventListener('unhandledrejection', (event) => { this.debugPanel.log(`未处理的Promise拒绝: ${event.reason}`, 'error'); console.error('🐱 Promise错误:', event); }); } startPerformanceMonitoring() { if (!performance.mark) return; setInterval(() => { performance.mark('catgirl-check-start'); const memUsage = performance.memory ? (performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(1) : 'N/A'; performance.mark('catgirl-check-end'); performance.measure('catgirl-check', 'catgirl-check-start', 'catgirl-check-end'); const measures = performance.getEntriesByType('measure'); const lastMeasure = measures[measures.length - 1]; if (lastMeasure && lastMeasure.duration > 50) { this.debugPanel.log(`性能警告: 检查耗时 ${lastMeasure.duration.toFixed(2)}ms`, 'warn'); } }, 5000); } async processBilibiliSpecial() { if (this.stateManager.shouldSkipBilibiliProcessing()) return; const maxRetries = CONFIG.sites.bilibili.maxRetryDelay / CONFIG.sites.bilibili.retryInterval; let retryCount = 0; const attemptProcess = () => { try { const biliComments = document.querySelector('bili-comments'); if (!biliComments || !biliComments.shadowRoot) { if (retryCount < maxRetries) { retryCount++; this.debugPanel.log(`B站处理重试 ${retryCount}/${maxRetries}`, 'warn'); setTimeout(attemptProcess, CONFIG.sites.bilibili.retryInterval * retryCount); return; } else { this.debugPanel.log('B站处理达到最大重试次数', 'error'); return; } } this.debugPanel.log('开始B站Shadow DOM处理', 'info'); this.progressIndicator.show(0); const shadowRoot = biliComments.shadowRoot; const commentThreads = shadowRoot.querySelectorAll('bili-comment-thread-renderer'); if (commentThreads.length === 0) { this.debugPanel.log('未找到评论线程', 'warn'); return; } EnhancedPerformanceUtils.createTimeSliceProcessor( Array.from(commentThreads), (thread, index) => { this.processBilibiliCommentThread(thread); this.progressIndicator.show((index + 1) / commentThreads.length * 100); }, { onComplete: () => { this.stateManager.markBilibiliCompleted(); this.debugPanel.log(`B站处理完成,共处理 ${commentThreads.length} 个评论`, 'success'); this.progressIndicator.show(100); } } ); } catch (error) { this.debugPanel.log(`B站处理出错: ${error.message}`, 'error'); console.error('🐱 B站处理出错:', error); } }; attemptProcess(); } toggleDebug() { super.toggleDebug(); if (CONFIG.features.debugMode) { this.debugPanel.show(); } else { this.debugPanel.hide(); } } stop() { super.stop(); this.requestInterceptor.stop(); this.progressIndicator.hide(); } } // ===== 工具函数 ===== function showToast(message, duration = 2000) { const toast = document.createElement('div'); toast.className = 'catgirl-toast'; toast.textContent = message; 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 showUpdateNotification() { showToast(`🎉 猫娘脚本已更新到 v${SCRIPT_VERSION} 喵~`, 3000); } // ===== 使用增强版应用 ===== const enhancedApp = new EnhancedCatgirlApp(); // 启动应用 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => enhancedApp.initialize(), { once: true }); } else { setTimeout(() => enhancedApp.initialize(), 0); } // ===== 样式注入 ===== GM_addStyle(` /* 设置面板样式 */ #catgirl-settings { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } #catgirl-settings .settings-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px 20px; border-radius: 10px 10px 0 0; display: flex; justify-content: space-between; align-items: center; } #catgirl-settings .settings-header h3 { margin: 0; font-size: 18px; } #catgirl-settings .close-btn { background: none; border: none; color: white; font-size: 24px; cursor: pointer; width: 30px; height: 30px; border-radius: 50%; transition: background 0.3s; } #catgirl-settings .close-btn:hover { background: rgba(255,255,255,0.2); } #catgirl-settings .settings-content { padding: 20px; } #catgirl-settings .setting-group { margin-bottom: 15px; } #catgirl-settings label { display: block; margin-bottom: 8px; font-weight: 500; color: #333; } #catgirl-settings select, #catgirl-settings input[type="text"], #catgirl-settings textarea { width: 100%; padding: 8px 12px; border: 2px solid #ddd; border-radius: 5px; font-size: 14px; transition: border-color 0.3s; } #catgirl-settings select:focus, #catgirl-settings input:focus, #catgirl-settings textarea:focus { outline: none; border-color: #667eea; } #catgirl-settings input[type="checkbox"] { width: auto; margin-right: 8px; } #catgirl-settings .stats { background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 15px 0; } #catgirl-settings .stats h4 { margin: 0 0 10px 0; color: #495057; } #catgirl-settings .stats p { margin: 5px 0; color: #6c757d; } #catgirl-settings .actions { display: flex; gap: 10px; flex-wrap: wrap; } #catgirl-settings button { padding: 8px 16px; border: none; border-radius: 5px; cursor: pointer; font-size: 14px; transition: all 0.3s; flex: 1; min-width: 80px; } #catgirl-settings #save-settings { background: #28a745; color: white; } #catgirl-settings #save-settings:hover { background: #218838; } #catgirl-settings #reset-settings { background: #ffc107; color: #212529; } #catgirl-settings #reset-settings:hover { background: #e0a800; } #catgirl-settings #clear-cache { background: #6c757d; color: white; } #catgirl-settings #clear-cache:hover { background: #5a6268; } /* Toast 通知样式 */ .catgirl-toast { position: fixed; top: 20px; right: 20px; z-index: 10000; background: rgba(0,0,0,0.9); color: white; padding: 12px 18px; border-radius: 8px; font-size: 14px; max-width: 300px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); transform: translateX(100%); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .catgirl-toast.show { transform: translateX(0); } /* 调试面板样式 */ #catgirl-debug { position: fixed; bottom: 20px; left: 20px; background: rgba(0,0,0,0.9); color: #00ff00; padding: 10px; border-radius: 5px; font-family: 'Courier New', monospace; font-size: 12px; max-width: 300px; z-index: 9999; } /* 进度条样式 */ .catgirl-progress { position: fixed; top: 0; left: 0; width: 0%; height: 3px; background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1); z-index: 10001; transition: width 0.3s ease; } `); // ===== 全局调试接口 ===== window.catgirlApp = { app: enhancedApp, config: CONFIG, utils: { EnhancedPerformanceUtils, StateManager, EnhancedTextProcessor, SettingsPanel, DebugPanel, ProgressIndicator, RequestInterceptor }, restart: () => enhancedApp.restart(), toggle: () => enhancedApp.toggle(), showSettings: () => enhancedApp.settingsPanel.show(), showStats: () => enhancedApp.showStats(), showDebug: () => enhancedApp.debugPanel.show(), performance: { clearCache: () => { enhancedApp.processedElements = new WeakSet(); enhancedApp.textProcessor.processedTexts = new Set(); console.log('🧹 缓存已清理'); } } }; console.log('🎀 增强版猫娘化系统加载完成!可通过 window.catgirlApp 访问API喵~'); })();