// ==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喵~');
})();