// ==UserScript==
// @name YouTube聊天室增強
// @namespace http://tampermonkey.net/
// @version 16.5
// @description 多色自動化著色用戶;非原生封鎖用戶;UI操作和功能選擇自由度;移除礙眼置頂;清理/標示洗版;發言次數統計;強化@體驗等
// @match *://www.youtube.com/live_chat*
// @grant none
// @license MIT
// ==/UserScript==
(function(){
'use strict';
const COLOR_OPTIONS={"淺藍":"#5FA6E8","藍色":"#2463D1","深藍":"#0000FF","紫色":"#FF00FF","淺綠":"#98FB98","綠色":"#00FF00","深綠":"#006400","青色":"#00FFFF","粉紅":"#FFC0CB","淺紅":"#F08080","紅色":"#FF0000","深紅":"#8B0000","橙色":"#FFA500","金色":"#FFD700","灰色":"#BDBDBD","深灰":"#404040"};
const MENU_AUTO_CLOSE_DELAY=8000,THROTTLE_DELAY=150,TEMP_USER_EXPIRE_TIME=40000,MAX_MESSAGE_CACHE_SIZE=200,CLEANUP_INTERVAL=40000,SPAM_CHECK_INTERVAL=500,FLAG_DURATION=60000,MESSAGE_CACHE_LIMIT=500;
const HIGHLIGHT_MODES={BOTH:0,NAME_ONLY:1,MESSAGE_ONLY:2},SPAM_MODES={MARK:0,REMOVE:1};
let userColorSettings=JSON.parse(localStorage.getItem('userColorSettings'))||{},blockedUsers=JSON.parse(localStorage.getItem('blockedUsers'))||[],currentMenu=null,menuTimeoutId=null,featureSettings=JSON.parse(localStorage.getItem('featureSettings'))||{pinEnabled:false,highlightEnabled:false,blockEnabled:false,buttonsVisible:false,mentionHighlightEnabled:false,spamFilterEnabled:false,counterEnabled:false,spamMode:SPAM_MODES.MARK,flagMode:false},highlightSettings=JSON.parse(localStorage.getItem('highlightSettings'))||{defaultMode:HIGHLIGHT_MODES.BOTH,tempMode:HIGHLIGHT_MODES.BOTH},tempUsers=JSON.parse(localStorage.getItem('tempUsers'))||{},flaggedUsers=JSON.parse(localStorage.getItem('flaggedUsers'))||{},lastTempUserCleanupTime=Date.now(),userMessageCounts={},lastSpamCheckTime=0;
const userColorCache=new Map(),blockedUsersSet=new Set(blockedUsers),tempUserCache=new Map(),styleCache=new WeakMap();
class LRUCache{constructor(limit){this.limit=limit;this.cache=new Map();}has(key){return this.cache.has(key);}get(key){const value=this.cache.get(key);if(value){this.cache.delete(key);this.cache.set(key,value);}return value;}set(key,value){if(this.cache.has(key))this.cache.delete(key);else if(this.cache.size>=this.limit){const oldestKey=this.cache.keys().next().value;this.cache.delete(oldestKey);}this.cache.set(key,value);}delete(key){this.cache.delete(key);}clear(){this.cache.clear();}}
const messageCache=new LRUCache(MESSAGE_CACHE_LIMIT),processedMessages=new LRUCache(MAX_MESSAGE_CACHE_SIZE*2);
const style=document.createElement('style');style.textContent=`:root{--highlight-color:inherit;--flagged-color:#FF0000;--spam-decoration:none}.ytcm-menu{position:fixed;background-color:white;border:1px solid black;padding:5px;z-index:9999;box-shadow:2px 2px 5px rgba(0,0,0,0.2);border-radius:5px}.ytcm-color-item{cursor:pointer;padding:5px;text-align:center;border-radius:3px;margin:2px}.ytcm-list-item{cursor:pointer;padding:5px;background-color:#f0f0f0;border-radius:3px;margin:2px}.ytcm-button{cursor:pointer;padding:5px 8px;margin:5px 2px 0 2px;border-radius:3px;border:1px solid #ccc;background-color:#f8f8f8}.ytcm-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:5px}.ytcm-button-row{display:flex;justify-content:space-between;margin-top:5px}.ytcm-flex-wrap{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:10px}.ytcm-control-panel{position:fixed;left:0;bottom:75px;z-index:9998;display:flex;flex-direction:column;gap:8px;padding:0}.ytcm-control-btn{padding:5px 0 5px 5px;cursor:pointer;text-align:left;min-width:40px;font-size:14px;font-weight:bold;color:white;-webkit-text-stroke:1px black;text-shadow:none;background:none;border:none;margin:0}.ytcm-control-btn.active{-webkit-text-stroke:1px black}.ytcm-control-btn.inactive{-webkit-text-stroke:1px red}.ytcm-toggle-btn{padding:5px 0 5px 5px;cursor:pointer;text-align:left;min-width:40px;font-size:14px;font-weight:bold;color:white;-webkit-text-stroke:1px black;text-shadow:none;background:none;border:none;margin:0}.ytcm-main-buttons{display:${featureSettings.buttonsVisible?'flex':'none'};flex-direction:column;gap:8px}.ytcm-message-count{font-size:0.6em;opacity:0.7;margin-left:3px;display:inline-block}[data-blocked="true"] #message{content:"<封鎖>"!important}[data-spam="true"]{--spam-decoration:line-through}[data-flagged="true"]{--flagged-opacity:1}[data-highlight="name"] #author-name,[data-highlight="both"] #author-name{color:var(--highlight-color)!important;font-weight:bold!important}[data-highlight="message"] #message,[data-highlight="both"] #message{color:var(--highlight-color)!important;font-weight:bold!important}[data-flagged="true"] #author-name,[data-flagged="true"] #message{color:var(--flagged-color)!important;font-weight:bold!important;font-style:italic!important;opacity:var(--flagged-opacity,1)}`;document.head.appendChild(style);
function initializeCaches(){Object.entries(userColorSettings).forEach(([user,color])=>userColorCache.set(user,color));Object.entries(tempUsers).forEach(([user,data])=>tempUserCache.set(user,data));Object.entries(flaggedUsers).forEach(([user,expireTime])=>{if(expireTime>Date.now())updateAllMessages(user);});}
function updateAllMessages(userName){const messages=Array.from(document.querySelectorAll('yt-live-chat-text-message-renderer')).filter(msg=>{const nameElement=msg.querySelector('#author-name');return nameElement&&nameElement.textContent.trim()===userName&&msg.style.display!=='none';});messages.forEach(msg=>{processedMessages.delete(msg);styleCache.delete(msg);processMessage(msg,true);});}
function createControlPanel(){const panel=document.createElement('div');panel.className='ytcm-control-panel';const mainButtons=document.createElement('div');mainButtons.className='ytcm-main-buttons';
const buttons=[{text:'標',className:`ytcm-control-btn ${featureSettings.flagMode?'active':'inactive'}`,title:'開關模式:開啟時點擊用戶會高亮其所有訊息一分鐘',onClick:()=>{featureSettings.flagMode=!featureSettings.flagMode;updateButtonState('標',featureSettings.flagMode);localStorage.setItem('featureSettings',JSON.stringify(featureSettings));}},{text:'頂',className:`ytcm-control-btn ${featureSettings.pinEnabled?'active':'inactive'}`,title:'切換清除置頂功能',onClick:()=>{featureSettings.pinEnabled=!featureSettings.pinEnabled;updateButtonState('頂',featureSettings.pinEnabled);localStorage.setItem('featureSettings',JSON.stringify(featureSettings));}},{text:'亮',className:`ytcm-control-btn ${featureSettings.highlightEnabled?'active':'inactive'}`,title:getHighlightModeTooltip(highlightSettings.defaultMode),onClick:(e)=>{if(e.ctrlKey){highlightSettings.defaultMode=(highlightSettings.defaultMode+1)%3;updateButtonTitle('亮',getHighlightModeTooltip(highlightSettings.defaultMode));localStorage.setItem('highlightSettings',JSON.stringify(highlightSettings));updateAllMessages();}else{featureSettings.highlightEnabled=!featureSettings.highlightEnabled;updateButtonState('亮',featureSettings.highlightEnabled);localStorage.setItem('featureSettings',JSON.stringify(featureSettings));updateAllMessages();}}},{text:'封',className:`ytcm-control-btn ${featureSettings.blockEnabled?'active':'inactive'}`,title:'切換清理封鎖用戶功能',onClick:()=>{featureSettings.blockEnabled=!featureSettings.blockEnabled;updateButtonState('封',featureSettings.blockEnabled);localStorage.setItem('featureSettings',JSON.stringify(featureSettings));updateAllMessages();}},{text:'@',className:`ytcm-control-btn ${featureSettings.mentionHighlightEnabled?'active':'inactive'}`,title:getHighlightModeTooltip(highlightSettings.tempMode),onClick:(e)=>{if(e.ctrlKey){highlightSettings.tempMode=(highlightSettings.tempMode+1)%3;updateButtonTitle('@',getHighlightModeTooltip(highlightSettings.tempMode));localStorage.setItem('highlightSettings',JSON.stringify(highlightSettings));updateAllMessages();}else{featureSettings.mentionHighlightEnabled=!featureSettings.mentionHighlightEnabled;updateButtonState('@',featureSettings.mentionHighlightEnabled);localStorage.setItem('featureSettings',JSON.stringify(featureSettings));if(!featureSettings.mentionHighlightEnabled){tempUsers={};tempUserCache.clear();localStorage.setItem('tempUsers',JSON.stringify(tempUsers));}updateAllMessages();}}},{text:'洗',className:`ytcm-control-btn ${featureSettings.spamFilterEnabled?'active':'inactive'}`,title:getSpamModeTooltip(featureSettings.spamMode),onClick:(e)=>{if(e.ctrlKey){featureSettings.spamMode=(featureSettings.spamMode+1)%2;updateButtonTitle('洗',getSpamModeTooltip(featureSettings.spamMode));localStorage.setItem('featureSettings',JSON.stringify(featureSettings));}else{featureSettings.spamFilterEnabled=!featureSettings.spamFilterEnabled;updateButtonState('洗',featureSettings.spamFilterEnabled);localStorage.setItem('featureSettings',JSON.stringify(featureSettings));if(!featureSettings.spamFilterEnabled)messageCache.clear();updateAllMessages();}}},{text:'數',className:`ytcm-control-btn ${featureSettings.counterEnabled?'active':'inactive'}`,title:'切換留言計數功能',onClick:()=>{featureSettings.counterEnabled=!featureSettings.counterEnabled;updateButtonState('數',featureSettings.counterEnabled);localStorage.setItem('featureSettings',JSON.stringify(featureSettings));if(!featureSettings.counterEnabled){document.querySelectorAll('.ytcm-message-count').forEach(el=>el.remove());}else{updateAllMessages();}}}];
buttons.forEach(btn=>{const button=document.createElement('div');button.className=btn.className;button.textContent=btn.text;button.title=btn.title;button.dataset.action=btn.text;button.addEventListener('click',btn.onClick);mainButtons.appendChild(button);});
const toggleBtn=document.createElement('div');toggleBtn.className='ytcm-toggle-btn';toggleBtn.textContent='☑';toggleBtn.title='顯示/隱藏控制按鈕';toggleBtn.addEventListener('click',()=>{featureSettings.buttonsVisible=!featureSettings.buttonsVisible;mainButtons.style.display=featureSettings.buttonsVisible?'flex':'none';localStorage.setItem('featureSettings',JSON.stringify(featureSettings));});
panel.appendChild(mainButtons);panel.appendChild(toggleBtn);document.body.appendChild(panel);return panel;}
function updateButtonState(btnText,isActive){const btn=document.querySelector(`.ytcm-control-btn[data-action="${btnText}"]`);if(btn)btn.className=`ytcm-control-btn ${isActive?'active':'inactive'}`;}
function updateButtonTitle(btnText,title){const btn=document.querySelector(`.ytcm-control-btn[data-action="${btnText}"]`);if(btn)btn.title=title;}
function getHighlightModeTooltip(mode){switch(mode){case HIGHLIGHT_MODES.BOTH:return"當前模式: 高亮暱稱和對話 (Ctrl+左鍵切換)";case HIGHLIGHT_MODES.NAME_ONLY:return"當前模式: 只高亮暱稱 (Ctrl+左鍵切換)";case HIGHLIGHT_MODES.MESSAGE_ONLY:return"當前模式: 只高亮對話 (Ctrl+左鍵切換)";default:return"高亮模式";}}
function getSpamModeTooltip(mode){switch(mode){case SPAM_MODES.MARK:return"當前模式: 註記洗版 (Ctrl+左鍵切換為移除模式)";case SPAM_MODES.REMOVE:return"當前模式: 移除洗版 (Ctrl+左鍵切換為註記模式)";default:return"洗版處理模式";}}
function throttle(func,limit){let lastFunc,lastRan;return function(){const context=this,args=arguments;if(!lastRan){func.apply(context,args);lastRan=Date.now();}else{clearTimeout(lastFunc);lastFunc=setTimeout(function(){if((Date.now()-lastRan)>=limit){func.apply(context,args);lastRan=Date.now();}},limit-(Date.now()-lastRan));}};}
function cleanupProcessedMessages(){requestIdleCallback(()=>{const allMessages=new Set(document.querySelectorAll('yt-live-chat-text-message-renderer'));const toDelete=[];processedMessages.cache.forEach((_,msg)=>{if(!allMessages.has(msg))toDelete.push(msg);});toDelete.forEach(msg=>{processedMessages.delete(msg);styleCache.delete(msg);});});}
function processMentionedUsers(messageText,authorName,authorColor){if(!featureSettings.mentionHighlightEnabled||!authorColor)return;const mentionRegex=/@([^\s].*?(?=\s|$|@|[\u200b]))/g;let match;const mentionedUsers=new Set();while((match=mentionRegex.exec(messageText))!==null){const mentionedUser=match[1].trim();if(mentionedUser)mentionedUsers.add(mentionedUser);}if(mentionedUsers.size!==1)return;const mentionedUser=Array.from(mentionedUsers)[0];const allUsers=Array.from(document.querySelectorAll('#author-name'));const existingUsers=allUsers.map(el=>el.textContent.trim());const isExistingUser=existingUsers.some(user=>user.toLowerCase()===mentionedUser.toLowerCase());if(isExistingUser&&!userColorCache.has(mentionedUser)&&!tempUserCache.has(mentionedUser)){tempUsers[mentionedUser]={color:authorColor,expireTime:Date.now()+TEMP_USER_EXPIRE_TIME};tempUserCache.set(mentionedUser,{color:authorColor,expireTime:Date.now()+TEMP_USER_EXPIRE_TIME});updateAllMessages(mentionedUser);localStorage.setItem('tempUsers',JSON.stringify(tempUsers));}}
function cleanupExpiredTempUsers(){const now=Date.now();if(now-lastTempUserCleanupTime<CLEANUP_INTERVAL)return;lastTempUserCleanupTime=now;let changed=false;for(const [user,data]of tempUserCache.entries()){if(data.expireTime<=now){tempUserCache.delete(user);if(tempUsers.hasOwnProperty(user)){delete tempUsers[user];}changed=true;updateAllMessages(user);}}if(changed)localStorage.setItem('tempUsers',JSON.stringify(tempUsers));}
function cleanupExpiredFlags(){const now=Date.now();let changed=false;for(const user in flaggedUsers){if(flaggedUsers[user]<=now){delete flaggedUsers[user];changed=true;updateAllMessages(user);}}if(changed)localStorage.setItem('flaggedUsers',JSON.stringify(flaggedUsers));}
function removePinnedMessage(){if(!featureSettings.pinEnabled)return;requestAnimationFrame(()=>{const pinnedMessage=document.querySelector('yt-live-chat-banner-renderer');if(pinnedMessage)pinnedMessage.style.display='none';});}
function closeMenu(){if(currentMenu){document.body.removeChild(currentMenu);currentMenu=null;clearTimeout(menuTimeoutId);}}
function createColorMenu(targetElement,event){closeMenu();const menu=document.createElement('div');menu.className='ytcm-menu';menu.style.top=`${event.clientY}px`;menu.style.left=`${event.clientX}px`;menu.style.width='220px';const colorGrid=document.createElement('div');colorGrid.className='ytcm-grid';Object.entries(COLOR_OPTIONS).forEach(([colorName,colorValue])=>{const colorItem=document.createElement('div');colorItem.className='ytcm-color-item';colorItem.textContent=colorName;colorItem.style.backgroundColor=colorValue;colorItem.addEventListener('click',()=>{if(targetElement.type==='user'){userColorSettings[targetElement.name]=colorValue;userColorCache.set(targetElement.name,colorValue);updateAllMessages(targetElement.name);localStorage.setItem('userColorSettings',JSON.stringify(userColorSettings));}closeMenu();});colorGrid.appendChild(colorItem);});const buttonRow=document.createElement('div');buttonRow.className='ytcm-button-row';const buttons=[{text:'封鎖',className:'ytcm-button',onClick:()=>{if(targetElement.type==='user'){blockedUsers.push(targetElement.name);blockedUsersSet.add(targetElement.name);localStorage.setItem('blockedUsers',JSON.stringify(blockedUsers));updateAllMessages(targetElement.name);}closeMenu();}},{text:'編輯',className:'ytcm-button',onClick:(e)=>{e.stopPropagation();createEditMenu(targetElement,event);}},{text:'刪除',className:'ytcm-button',onClick:()=>{if(targetElement.type==='user'){const userName=targetElement.name;if(userColorSettings[userName]){delete userColorSettings[userName];userColorCache.delete(userName);}if(blockedUsersSet.has(userName)){blockedUsers=blockedUsers.filter(u=>u!==userName);blockedUsersSet.delete(userName);localStorage.setItem('blockedUsers',JSON.stringify(blockedUsers));}if(tempUsers[userName]){delete tempUsers[userName];tempUserCache.delete(userName);localStorage.setItem('tempUsers',JSON.stringify(tempUsers));}if(flaggedUsers[userName]){delete flaggedUsers[userName];localStorage.setItem('flaggedUsers',JSON.stringify(flaggedUsers));}localStorage.setItem('userColorSettings',JSON.stringify(userColorSettings));updateAllMessages(userName);}closeMenu();}},{text:'清除',className:'ytcm-button',onClick:()=>{const confirmMenu=document.createElement('div');confirmMenu.className='ytcm-menu';confirmMenu.style.top=`${event.clientY}px`;confirmMenu.style.left=`${event.clientX}px`;const confirmText=document.createElement('div');confirmText.textContent='確定清除所有設定?';const confirmButton=document.createElement('button');confirmButton.className='ytcm-button';confirmButton.textContent='確認';confirmButton.addEventListener('click',()=>{localStorage.removeItem('userColorSettings');localStorage.removeItem('blockedUsers');localStorage.removeItem('featureSettings');localStorage.removeItem('highlightSettings');localStorage.removeItem('tempUsers');localStorage.removeItem('flaggedUsers');window.location.reload();});confirmMenu.appendChild(confirmText);confirmMenu.appendChild(confirmButton);document.body.appendChild(confirmMenu);setTimeout(()=>{if(document.body.contains(confirmMenu))document.body.removeChild(confirmMenu);},5000);}}];buttons.forEach(btn=>{const button=document.createElement('button');button.className=btn.className;button.textContent=btn.text;button.addEventListener('click',btn.onClick);buttonRow.appendChild(button);});menu.appendChild(colorGrid);menu.appendChild(buttonRow);document.body.appendChild(menu);currentMenu=menu;menuTimeoutId=setTimeout(closeMenu,MENU_AUTO_CLOSE_DELAY);}
function createEditMenu(targetElement,event){closeMenu();const menu=document.createElement('div');menu.className='ytcm-menu';menu.style.top='10px';menu.style.left='10px';menu.style.width='90%';menu.style.maxHeight='80vh';menu.style.overflowY='auto';const closeButton=document.createElement('button');closeButton.className='ytcm-button';closeButton.textContent='關閉';closeButton.style.width='100%';closeButton.style.marginBottom='10px';closeButton.addEventListener('click',closeMenu);menu.appendChild(closeButton);const blockedUserList=document.createElement('div');blockedUserList.textContent='封鎖用戶名單:';blockedUserList.className='ytcm-flex-wrap';blockedUsers.forEach(user=>{const userItem=document.createElement('div');userItem.className='ytcm-list-item';userItem.textContent=user;userItem.addEventListener('click',()=>{blockedUsers=blockedUsers.filter(u=>u!==user);blockedUsersSet.delete(user);localStorage.setItem('blockedUsers',JSON.stringify(blockedUsers));userItem.remove();updateAllMessages(user);});blockedUserList.appendChild(userItem);});menu.appendChild(blockedUserList);const coloredUserList=document.createElement('div');coloredUserList.textContent='被上色用戶名單:';coloredUserList.className='ytcm-flex-wrap';Object.keys(userColorSettings).forEach(user=>{const userItem=document.createElement('div');userItem.className='ytcm-list-item';userItem.textContent=user;userItem.addEventListener('click',()=>{delete userColorSettings[user];userColorCache.delete(user);localStorage.setItem('userColorSettings',JSON.stringify(userColorSettings));userItem.remove();updateAllMessages(user);});coloredUserList.appendChild(userItem);});menu.appendChild(coloredUserList);document.body.appendChild(menu);currentMenu=menu;menuTimeoutId=setTimeout(closeMenu,MENU_AUTO_CLOSE_DELAY);}
function checkForSpam(msg){if(!featureSettings.spamFilterEnabled)return;const nameElement=msg.querySelector('#author-name');if(!nameElement)return;const userName=nameElement.textContent.trim();if(userColorCache.has(userName)||tempUserCache.has(userName)||flaggedUsers[userName])return;const messageElement=msg.querySelector('#message');if(!messageElement||messageElement.textContent==='<封鎖>')return;const textNodes=Array.from(messageElement.childNodes).filter(node=>node.nodeType===Node.TEXT_NODE&&!node.parentElement.classList.contains('emoji'));const messageText=textNodes.map(node=>node.textContent.trim()).join(' ');if(messageCache.has(messageText)){if(featureSettings.spamMode===SPAM_MODES.MARK){msg.setAttribute('data-spam','true');styleCache.delete(msg);}else{msg.style.display='none';}return;}messageCache.set(messageText,true);}
function updateMessageCounter(msg){if(!featureSettings.counterEnabled)return;const nameElement=msg.querySelector('#author-name');if(!nameElement)return;const userName=nameElement.textContent.trim();if(!userMessageCounts[userName])userMessageCounts[userName]=0;userMessageCounts[userName]++;const existingCounter=msg.querySelector('.ytcm-message-count');if(existingCounter)existingCounter.remove();const counterSpan=document.createElement('span');counterSpan.className='ytcm-message-count';counterSpan.textContent=userMessageCounts[userName];const messageElement=msg.querySelector('#message');if(messageElement)messageElement.appendChild(counterSpan);}
function processMessage(msg,isInitialLoad=false){if(styleCache.has(msg))return;const authorName=msg.querySelector('#author-name');const messageElement=msg.querySelector('#message');if(!authorName||!messageElement)return;const userName=authorName.textContent.trim();const textNodes=Array.from(messageElement.childNodes).filter(node=>node.nodeType===Node.TEXT_NODE&&!node.parentElement.classList.contains('emoji'));const messageText=textNodes.map(node=>node.textContent.trim()).join(' ');if(featureSettings.blockEnabled&&blockedUsersSet.has(userName)){msg.setAttribute('data-blocked','true');messageElement.textContent='<封鎖>';styleCache.set(msg,true);return;}if(msg.hasAttribute('data-blocked')){styleCache.set(msg,true);return;}msg.removeAttribute('data-highlight');msg.removeAttribute('data-flagged');if(flaggedUsers[userName]){msg.setAttribute('data-flagged','true');}if(featureSettings.highlightEnabled&&(tempUserCache.has(userName)||userColorCache.has(userName))){const color=tempUserCache.has(userName)?tempUserCache.get(userName).color:userColorCache.get(userName);const mode=tempUserCache.has(userName)?highlightSettings.tempMode:highlightSettings.defaultMode;msg.style.setProperty('--highlight-color',color);if(mode!==HIGHLIGHT_MODES.BOTH&&mode!==HIGHLIGHT_MODES.NAME_ONLY&&mode!==HIGHLIGHT_MODES.MESSAGE_ONLY)return;msg.setAttribute('data-highlight',mode===HIGHLIGHT_MODES.BOTH?'both':mode===HIGHLIGHT_MODES.NAME_ONLY?'name':'message');}if(featureSettings.spamFilterEnabled&&(isInitialLoad||Date.now()-lastSpamCheckTime>=SPAM_CHECK_INTERVAL)){checkForSpam(msg);if(!isInitialLoad)lastSpamCheckTime=Date.now();}updateMessageCounter(msg);if(featureSettings.mentionHighlightEnabled)processMentionedUsers(messageText,userName,tempUserCache.has(userName)?tempUserCache.get(userName).color:userColorCache.get(userName));styleCache.set(msg,true);}
function highlightMessages(mutations){cleanupProcessedMessages();const messages=[];mutations.forEach(mutation=>{mutation.addedNodes.forEach(node=>{if(node.nodeType===1&&node.matches('yt-live-chat-text-message-renderer')&&!processedMessages.has(node)){messages.push(node);processedMessages.set(node,true);}});});if(messages.length===0){const allMessages=Array.from(document.querySelectorAll('yt-live-chat-text-message-renderer')).slice(-MAX_MESSAGE_CACHE_SIZE);allMessages.forEach(msg=>{if(!processedMessages.has(msg)){messages.push(msg);processedMessages.set(msg,true);}});}requestAnimationFrame(()=>{messages.forEach(msg=>processMessage(msg));cleanupExpiredFlags();});cleanupExpiredTempUsers();}
function handleClick(event){if(currentMenu&&!currentMenu.contains(event.target))closeMenu();if(featureSettings.flagMode){const nameElement=event.target.closest('#author-name');if(nameElement){const userName=nameElement.textContent.trim();flaggedUsers[userName]=Date.now()+FLAG_DURATION;localStorage.setItem('flaggedUsers',JSON.stringify(flaggedUsers));updateAllMessages(userName);return;}}const msgElement=event.target.closest('yt-live-chat-text-message-renderer');if(msgElement){const messageElement=msgElement.querySelector('#message');if(messageElement&&event.offsetX<messageElement.offsetWidth/2){const authorName=msgElement.querySelector('#author-name');if(authorName&&!featureSettings.flagMode){createColorMenu({type:'user',name:authorName.textContent.trim()},event);}}}}
function init(){initializeCaches();document.addEventListener('click',handleClick);const controlPanel=createControlPanel();const observer=new MutationObserver(throttle((mutations)=>{highlightMessages(mutations);removePinnedMessage();},THROTTLE_DELAY));const chatContainer=document.querySelector('#chat');if(chatContainer){observer.observe(chatContainer,{childList:true,subtree:true});const existingMessages=Array.from(chatContainer.querySelectorAll('yt-live-chat-text-message-renderer'));existingMessages.forEach(msg=>{if(!processedMessages.has(msg)){processedMessages.set(msg,true);processMessage(msg,true);}});}const cleanupIntervalId=setInterval(()=>{cleanupProcessedMessages();cleanupExpiredTempUsers();cleanupExpiredFlags();},CLEANUP_INTERVAL);return()=>{observer.disconnect();document.removeEventListener('click',handleClick);clearInterval(cleanupIntervalId);if(controlPanel)controlPanel.remove();closeMenu();};}
let cleanup=init();const checkChatContainer=setInterval(()=>{if(document.querySelector('#chat')&&!cleanup)cleanup=init();},1000);window.addEventListener('beforeunload',()=>{clearInterval(checkChatContainer);cleanup?.();});})();