// ==UserScript==
// @name YouTube 聊天室增強
// @namespace http://tampermonkey.net/
// @version 14.1
// @description 聊天室管理;多種可選色彩用以自動化著色特定用戶訊息,非YT原生的封鎖用戶、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;
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'))||{},flaggedUser=null,flagExpireTime=0,lastTempUserCleanupTime=Date.now(),messageCache=new Set(),userMessageCounts={},lastSpamCheckTime=0;
const userColorCache=new Map(),blockedUsersSet=new Set(blockedUsers),tempUserCache=new Map(),processedMessages=new Set(),styleCache=new WeakMap();
const style=document.createElement('style');
style.textContent=`.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-bg-highlight{padding:2px 4px;border-radius:3px}.ytcm-message-count{font-size:0.8em;opacity:0.7;vertical-align:super}.ytcm-highlight-name{color:var(--highlight-color)!important;font-weight:bold!important}.ytcm-highlight-msg{color:var(--highlight-color)!important;font-weight:bold!important}[data-blocked="true"] #message{content:"<封鎖>"!important}[data-spam="true"] #message{content:"<洗版>"!important}.ytcm-flagged-name{color:red!important;font-weight:bold!important;border:2px solid yellow!important}.ytcm-flagged-msg{color:red!important;font-weight:bold!important;border:2px solid yellow!important}`;
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));}
function createControlPanel(){const panel=document.createElement('div');panel.className='ytcm-control-panel';const mainButtons=document.createElement('div');mainButtons.className='ytcm-main-buttons';
const flagBtn=document.createElement('div');flagBtn.className=`ytcm-control-btn ${featureSettings.flagMode?'active':'inactive'}`;flagBtn.textContent='標';flagBtn.title='點擊變色代表啟動,指定一個用戶暫時高亮';flagBtn.addEventListener('click',()=>{featureSettings.flagMode=!featureSettings.flagMode;flagBtn.className=`ytcm-control-btn ${featureSettings.flagMode?'active':'inactive'}`;localStorage.setItem('featureSettings',JSON.stringify(featureSettings));});
const pinBtn=document.createElement('div');pinBtn.className=`ytcm-control-btn ${featureSettings.pinEnabled?'active':'inactive'}`;pinBtn.textContent='頂';pinBtn.title='切換清除置頂功能';pinBtn.addEventListener('click',()=>{featureSettings.pinEnabled=!featureSettings.pinEnabled;pinBtn.className=`ytcm-control-btn ${featureSettings.pinEnabled?'active':'inactive'}`;localStorage.setItem('featureSettings',JSON.stringify(featureSettings));});
const highlightBtn=document.createElement('div');highlightBtn.className=`ytcm-control-btn ${featureSettings.highlightEnabled?'active':'inactive'}`;highlightBtn.textContent='亮';highlightBtn.title=getHighlightModeTooltip(highlightSettings.defaultMode);highlightBtn.addEventListener('click',(e)=>{if(e.ctrlKey){highlightSettings.defaultMode=(highlightSettings.defaultMode+1)%3;highlightBtn.title=getHighlightModeTooltip(highlightSettings.defaultMode);localStorage.setItem('highlightSettings',JSON.stringify(highlightSettings));clearProcessedMessages();}else{featureSettings.highlightEnabled=!featureSettings.highlightEnabled;highlightBtn.className=`ytcm-control-btn ${featureSettings.highlightEnabled?'active':'inactive'}`;localStorage.setItem('featureSettings',JSON.stringify(featureSettings));}});
const blockBtn=document.createElement('div');blockBtn.className=`ytcm-control-btn ${featureSettings.blockEnabled?'active':'inactive'}`;blockBtn.textContent='封';blockBtn.title='切換清理封鎖用戶功能';blockBtn.addEventListener('click',()=>{featureSettings.blockEnabled=!featureSettings.blockEnabled;blockBtn.className=`ytcm-control-btn ${featureSettings.blockEnabled?'active':'inactive'}`;localStorage.setItem('featureSettings',JSON.stringify(featureSettings));clearProcessedMessages();});
const mentionBtn=document.createElement('div');mentionBtn.className=`ytcm-control-btn ${featureSettings.mentionHighlightEnabled?'active':'inactive'}`;mentionBtn.textContent='@';mentionBtn.title=getHighlightModeTooltip(highlightSettings.tempMode);mentionBtn.addEventListener('click',(e)=>{if(e.ctrlKey){highlightSettings.tempMode=(highlightSettings.tempMode+1)%3;mentionBtn.title=getHighlightModeTooltip(highlightSettings.tempMode);localStorage.setItem('highlightSettings',JSON.stringify(highlightSettings));clearProcessedMessages();}else{featureSettings.mentionHighlightEnabled=!featureSettings.mentionHighlightEnabled;mentionBtn.className=`ytcm-control-btn ${featureSettings.mentionHighlightEnabled?'active':'inactive'}`;localStorage.setItem('featureSettings',JSON.stringify(featureSettings));if(!featureSettings.mentionHighlightEnabled){tempUsers={};tempUserCache.clear();localStorage.setItem('tempUsers',JSON.stringify(tempUsers));}}});
const spamBtn=document.createElement('div');spamBtn.className=`ytcm-control-btn ${featureSettings.spamFilterEnabled?'active':'inactive'}`;spamBtn.textContent='洗';spamBtn.title=getSpamModeTooltip(featureSettings.spamMode);spamBtn.addEventListener('click',(e)=>{if(e.ctrlKey){featureSettings.spamMode=(featureSettings.spamMode+1)%2;spamBtn.title=getSpamModeTooltip(featureSettings.spamMode);localStorage.setItem('featureSettings',JSON.stringify(featureSettings));}else{featureSettings.spamFilterEnabled=!featureSettings.spamFilterEnabled;spamBtn.className=`ytcm-control-btn ${featureSettings.spamFilterEnabled?'active':'inactive'}`;localStorage.setItem('featureSettings',JSON.stringify(featureSettings));if(!featureSettings.spamFilterEnabled)messageCache.clear();}});
const counterBtn=document.createElement('div');counterBtn.className=`ytcm-control-btn ${featureSettings.counterEnabled?'active':'inactive'}`;counterBtn.textContent='數';counterBtn.title='切換留言計數功能';counterBtn.addEventListener('click',()=>{featureSettings.counterEnabled=!featureSettings.counterEnabled;counterBtn.className=`ytcm-control-btn ${featureSettings.counterEnabled?'active':'inactive'}`;localStorage.setItem('featureSettings',JSON.stringify(featureSettings));if(!featureSettings.counterEnabled){document.querySelectorAll('.ytcm-message-count').forEach(el=>el.remove());}});
mainButtons.appendChild(flagBtn);mainButtons.appendChild(pinBtn);mainButtons.appendChild(highlightBtn);mainButtons.appendChild(blockBtn);mainButtons.appendChild(mentionBtn);mainButtons.appendChild(spamBtn);mainButtons.appendChild(counterBtn);
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 clearProcessedMessages(){processedMessages.clear();}
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.MARK: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(){const allMessages=new Set(document.querySelectorAll('yt-live-chat-text-message-renderer'));for(const msg of processedMessages){if(!allMessages.has(msg)){processedMessages.delete(msg);styleCache.delete(msg);}}if(processedMessages.size>MAX_MESSAGE_CACHE_SIZE){const messages=Array.from(processedMessages).slice(-MAX_MESSAGE_CACHE_SIZE);processedMessages.clear();messages.forEach(msg=>processedMessages.add(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});const messages=document.querySelectorAll('yt-live-chat-text-message-renderer');messages.forEach(msg=>{const nameElement=msg.querySelector('#author-name');if(nameElement&&nameElement.textContent.trim()===mentionedUser){processedMessages.delete(msg);styleCache.delete(msg);}});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;const messages=document.querySelectorAll('yt-live-chat-text-message-renderer');messages.forEach(msg=>{const nameElement=msg.querySelector('#author-name');if(nameElement&&nameElement.textContent.trim()===user){processedMessages.delete(msg);styleCache.delete(msg);}});}}if(changed)localStorage.setItem('tempUsers',JSON.stringify(tempUsers));}
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);const messages=document.querySelectorAll('yt-live-chat-text-message-renderer');messages.forEach(msg=>{const nameElement=msg.querySelector('#author-name');if(nameElement&&nameElement.textContent.trim()===targetElement.name){processedMessages.delete(msg);styleCache.delete(msg);}});}localStorage.setItem('userColorSettings',JSON.stringify(userColorSettings));closeMenu();});colorGrid.appendChild(colorItem);});const buttonRow=document.createElement('div');buttonRow.className='ytcm-button-row';const blockButton=document.createElement('button');blockButton.className='ytcm-button';blockButton.textContent='封鎖';blockButton.addEventListener('click',()=>{if(targetElement.type==='user'){blockedUsers.push(targetElement.name);blockedUsersSet.add(targetElement.name);localStorage.setItem('blockedUsers',JSON.stringify(blockedUsers));const messages=document.querySelectorAll('yt-live-chat-text-message-renderer');messages.forEach(msg=>{const nameElement=msg.querySelector('#author-name');if(nameElement&&nameElement.textContent.trim()===targetElement.name){msg.setAttribute('data-blocked','true');const messageElement=msg.querySelector('#message');if(messageElement)messageElement.textContent='<封鎖>';styleCache.delete(msg);}});}closeMenu();});const editButton=document.createElement('button');editButton.className='ytcm-button';editButton.textContent='編輯';editButton.addEventListener('click',(e)=>{e.stopPropagation();createEditMenu(targetElement,event);});const deleteButton=document.createElement('button');deleteButton.className='ytcm-button';deleteButton.textContent='刪除';deleteButton.addEventListener('click',()=>{if(targetElement.type==='user'&&userColorSettings[targetElement.name]){delete userColorSettings[targetElement.name];userColorCache.delete(targetElement.name);localStorage.setItem('userColorSettings',JSON.stringify(userColorSettings));const messages=document.querySelectorAll('yt-live-chat-text-message-renderer');messages.forEach(msg=>{const nameElement=msg.querySelector('#author-name');if(nameElement&&nameElement.textContent.trim()===targetElement.name){processedMessages.delete(msg);styleCache.delete(msg);}});}closeMenu();});const clearButton=document.createElement('button');clearButton.className='ytcm-button';clearButton.textContent='清除';clearButton.addEventListener('click',()=>{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');window.location.reload();});confirmMenu.appendChild(confirmText);confirmMenu.appendChild(confirmButton);document.body.appendChild(confirmMenu);setTimeout(()=>{if(document.body.contains(confirmMenu))document.body.removeChild(confirmMenu);},5000);});buttonRow.appendChild(blockButton);buttonRow.appendChild(editButton);buttonRow.appendChild(deleteButton);buttonRow.appendChild(clearButton);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();const messages=document.querySelectorAll('yt-live-chat-text-message-renderer');messages.forEach(msg=>{const nameElement=msg.querySelector('#author-name');if(nameElement&&nameElement.textContent.trim()===user){processedMessages.delete(msg);styleCache.delete(msg);}});});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();const messages=document.querySelectorAll('yt-live-chat-text-message-renderer');messages.forEach(msg=>{const nameElement=msg.querySelector('#author-name');if(nameElement&&nameElement.textContent.trim()===user){processedMessages.delete(msg);styleCache.delete(msg);}});});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 messageElement=msg.querySelector('#message');if(!messageElement||messageElement.textContent==='<封鎖>'||messageElement.textContent==='<洗版>')return;const messageText=messageElement.textContent.trim();if(messageCache.has(messageText)){if(featureSettings.spamMode===SPAM_MODES.MARK){messageElement.textContent='<洗版>';msg.setAttribute('data-spam','true');styleCache.delete(msg);}else{msg.style.display='none';}return;}messageCache.add(messageText);}
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 processFlaggedUser(){if(!flaggedUser||Date.now()>flagExpireTime){flaggedUser=null;document.querySelectorAll('.ytcm-flagged-name,.ytcm-flagged-msg').forEach(el=>{el.classList.remove('ytcm-flagged-name','ytcm-flagged-msg');});return;}const messages=document.querySelectorAll('yt-live-chat-text-message-renderer');messages.forEach(msg=>{const nameElement=msg.querySelector('#author-name');if(nameElement&&nameElement.textContent.trim()===flaggedUser){nameElement.classList.add('ytcm-flagged-name');const messageElement=msg.querySelector('#message');if(messageElement)messageElement.classList.add('ytcm-flagged-msg');}});}
function processMessage(msg){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 messageText=messageElement.textContent.trim();if(featureSettings.blockEnabled&&blockedUsersSet.has(userName)){msg.setAttribute('data-blocked','true');messageElement.textContent='<封鎖>';styleCache.set(msg,true);return;}if(msg.hasAttribute('data-blocked')||msg.hasAttribute('data-spam')){styleCache.set(msg,true);return;}if(featureSettings.spamFilterEnabled){const now=Date.now();if(now-lastSpamCheckTime>=SPAM_CHECK_INTERVAL){checkForSpam(msg);lastSpamCheckTime=now;}}authorName.className=authorName.className.replace('ytcm-highlight-name','').replace('ytcm-bg-highlight','').replace('ytcm-flagged-name','').trim();messageElement.className=messageElement.className.replace('ytcm-highlight-msg','').replace('ytcm-bg-highlight','').replace('ytcm-flagged-msg','').trim();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;authorName.style.setProperty('--highlight-color',color);messageElement.style.setProperty('--highlight-color',color);if(mode===HIGHLIGHT_MODES.BOTH||mode===HIGHLIGHT_MODES.NAME_ONLY){authorName.classList.add('ytcm-highlight-name');}if(mode===HIGHLIGHT_MODES.BOTH||mode===HIGHLIGHT_MODES.MESSAGE_ONLY){messageElement.classList.add('ytcm-highlight-msg');}}if(flaggedUser===userName){authorName.classList.add('ytcm-flagged-name');messageElement.classList.add('ytcm-flagged-msg');}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.add(node);}});});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.add(msg);}});}requestAnimationFrame(()=>{messages.forEach(msg=>processMessage(msg));processFlaggedUser();});cleanupExpiredTempUsers();}
function handleClick(event){if(currentMenu&&!currentMenu.contains(event.target))closeMenu();if(featureSettings.flagMode){const nameElement=event.target.closest('#author-name');if(nameElement){flaggedUser=nameElement.textContent.trim();flagExpireTime=Date.now()+FLAG_DURATION;featureSettings.flagMode=false;document.querySelector('.ytcm-control-btn[textContent="標"]').className='ytcm-control-btn inactive';localStorage.setItem('featureSettings',JSON.stringify(featureSettings));processFlaggedUser();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 cleanupIntervalId=setInterval(()=>{cleanupProcessedMessages();cleanupExpiredTempUsers();processFlaggedUser();},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?.();});
})();