您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add customizable buttons to Torn.com chat with enhanced UI and features, including caching for API data and feedback notifications for actions.
当前为
// ==UserScript== // @name Torn.com Enhanced Chat Buttons V2 // @namespace http://tampermonkey.net/ // @version 2.20 // @description Add customizable buttons to Torn.com chat with enhanced UI and features, including caching for API data and feedback notifications for actions. // @author Created by Callz [2188704], updated by Weav3r [1853324] // @match https://www.torn.com/* // @grant GM_setClipboard // ==/UserScript== (function() { 'use strict'; const CACHE_TTL = 24 * 60 * 60 * 1000; const buttonCSS = ` .custom-chat-button { background-color: #007BFF; color: white; padding: 2px 7px; text-align: center; text-decoration: none; display: inline-block; font-size: 14px; margin: 4px 6px; cursor: pointer; border-radius: 5px; border: none; transition: transform 0.1s ease, box-shadow 0.1s ease; min-width: 80px; overflow: hidden; white-space: nowrap; } .custom-chat-button:active { transform: scale(0.95); box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); } .custom-chat-button.recent { border: 2px solid #FFD700; box-shadow: 0 0 5px rgba(255, 215, 0, 0.8); } .custom-ui-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #f5f5f5; padding: 10px; color: black; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); z-index: 9999999999; width: 90%; max-width: 500px; box-sizing: border-box; max-height: 90vh; overflow: auto; } .custom-ui-panel h3 { font-size: 20px; margin-bottom: 10px; text-align: center; } .custom-ui-panel label { font-size: 14px; margin-bottom: 5px; display: block; } .custom-ui-panel input[type="text"], .custom-ui-panel select, .custom-ui-panel textarea { width: calc(100% - 12px); padding: 5px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 5px; font-size: 14px; } .custom-ui-panel input[type="color"] { padding: 0; margin-top: 5px; } .custom-ui-panel button { background-color: #007BFF; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; margin: 5px; font-size: 14px; transition: background-color 0.3s ease; } .custom-ui-panel button#close-ui { background-color: #ccc; } .custom-ui-panel button#close-ui:hover { background-color: #999; } .custom-ui-panel textarea { height: 60px; resize: vertical; position: relative; } .custom-ui-panel hr { margin: 10px 0; border: 0; border-top: 1px solid #ccc; } .char-counter { position: absolute; bottom: 10px; right: 10px; font-size: 12px; color: #999; } #chat-config-button { color: green; } #button-configs { max-height: 400px; overflow-y: auto; margin-bottom: 10px; } @media (max-width: 600px) { .custom-ui-panel { width: 95%; max-width: none; padding: 8px; } .custom-ui-panel h3 { font-size: 18px; } .custom-ui-panel label, .custom-ui-panel button { font-size: 12px; } .custom-ui-panel input[type="text"], .custom-ui-panel select, .custom-ui-panel textarea { font-size: 12px; } .custom-ui-panel button { padding: 6px 10px; } .char-counter { font-size: 10px; } } .tabs { display: flex; margin-bottom: 10px; } .tab { flex: 1; padding: 10px; cursor: pointer; text-align: center; background-color: #e9e9e9; border: 1px solid #ccc; border-bottom: none; border-radius: 10px 10px 0 0; } .tab.settings-tab { flex: 0.2; padding: 10px; cursor: pointer; text-align: center; background-color: #e9e9e9; border: 1px solid #ccc; border-bottom: none; border-radius: 10px 10px 0 0; } .tab.active { background-color: #fff; border-bottom: 1px solid #fff; } .tab-content { display: none; } .tab-content.active { display: block; } .custom-ui-panel.config-list-tab-active { max-height: 80vh; } .search-container { display: flex; margin-bottom: 10px; } .search-container input[type="text"] { flex: 3; padding: 5px; margin-right: 5px; } .search-container select { flex: 1; padding: 5px; } .highlight { background-color: yellow; } .tt-chat-filter { display: flex; padding: 4px; align-items: center; background-color: var(--chat-box-bg); color: var(--chat-box-label-info); border-bottom: 1px solid var(--chat-box-input-border); margin-bottom: 8px; } .tt-chat-filter input { margin-left: 4px; margin-right: 4px; border-radius: 5px; width: -webkit-fill-available; width: -moz-available; border: 1px solid var(--chat-box-input-border); background-color: var(--chat-box-bg); color: var(--chat-box-label-info); } .notification { position: fixed; bottom: 20px; right: 20px; background-color: rgba(0, 0, 0, 0.8); color: white; padding: 10px; border-radius: 5px; z-index: 9999999999; opacity: 0; transition: opacity 0.5s ease; } .notification.show { opacity: 1; } `; const conditions = { TradeChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Trade', HospitalChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Hospital', FactionChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Faction', CompanyChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Company', GlobalChat: chatBox => chatBox.querySelector('.chat-box-header__name___jIjjM').textContent === 'Global', UserChat: chatBox => chatBox.querySelector('.chat-box-header__options___nTsMU'), }; const companyTypes = { 1: "Adult Novelties", 2: "Amusement Park", 3: "Candle Shop", 4: "Car Dealership", 5: "Clothing Store", 6: "Cruise Line", 7: "Cyber Cafe", 8: "Detective Agency", 9: "Farm", 10: "Firework Stand", 11: "Fitness Center", 12: "Flower Shop", 13: "Furniture Store", 14: "Game Shop", 15: "Gas Station", 16: "Gents Strip Club", 17: "Grocery Store", 18: "Gun Shop", 19: "Hair Salon", 20: "Ladies Strip Club", 21: "Law Firm", 22: "Lingerie Store", 23: "Logistics Management", 24: "Meat Warehouse", 25: "Mechanic Shop", 26: "Mining Corporation", 27: "Music Store", 28: "Nightclub", 29: "Oil Rig", 30: "Private Security Firm", 31: "Property Broker", 32: "Pub", 33: "Restaurant", 34: "Software Corporation", 35: "Sweet Shop", 36: "Television Network", 37: "Theater", 38: "Toy Shop", 39: "Zoo" }; function addCSS(cssString) { const style = document.createElement('style'); style.textContent = cssString; document.head.append(style); } function showNotification(message) { const notification = document.createElement('div'); notification.className = 'notification'; notification.innerText = message; document.body.appendChild(notification); requestAnimationFrame(() => { notification.classList.add('show'); }); setTimeout(() => { notification.classList.remove('show'); setTimeout(() => { notification.remove(); }, 500); }, 2000); } function saveRecentButtonInfo(buttonText, chatBoxName) { localStorage.setItem('recentButtonInfo', JSON.stringify({ buttonText, chatBoxName })); } function clearRecentButtonInfo() { localStorage.removeItem('recentButtonInfo'); } function getButtonConfigurations() { return JSON.parse(localStorage.getItem('chatButtonConfig')) || { buttons: [] }; } function saveButtonConfigurations(config) { localStorage.setItem('chatButtonConfig', JSON.stringify(config)); } function getAPIKey() { return localStorage.getItem('apiKey') || ''; } function saveAPIKey(key) { localStorage.setItem('apiKey', key); showNotification('API key saved.'); } function saveCache(key, data) { const cacheData = { timestamp: Date.now(), data }; localStorage.setItem(key, JSON.stringify(cacheData)); } function loadCache(key) { const cacheData = JSON.parse(localStorage.getItem(key)); if (cacheData && (Date.now() - cacheData.timestamp < CACHE_TTL)) { return cacheData.data; } return null; } function clearCache() { localStorage.removeItem('companyCache'); localStorage.removeItem('factionCache'); showNotification('API cache cleared.'); } function createUIPanel() { const panel = document.createElement('div'); panel.className = 'custom-ui-panel'; panel.innerHTML = ` <div class="tabs"> <div class="tab active" data-tab="config-list-tab">Configured Buttons</div> <div class="tab" data-tab="config-edit-tab">Create/Edit Button</div> <div class="tab settings-tab" data-tab="config-settings-tab">⚙️</div> </div> <div id="config-list-tab" class="tab-content active"> <div class="search-container"> <input type="text" id="search-input" placeholder="Search..."> <select id="search-select"> <option value="buttonText">Text</option> <option value="condition">Condition</option> <option value="text">Message</option> </select> </div> <div id="button-configs"></div> </div> <div id="config-edit-tab" class="tab-content"> <div> <label for="button-text">Button Text</label> <input type="text" id="button-text" placeholder="Button Text"> <label for="button-color">Background Color</label> <input type="color" id="button-color"> <label for="button-condition">Condition</label> <select id="button-condition"> <option value="TradeChat">Trade Chat</option> <option value="HospitalChat">Hospital Chat</option> <option value="FactionChat">Faction Chat</option> <option value="CompanyChat">Company Chat</option> <option value="GlobalChat">Global Chat</option> <option value="UserChat">User Chat</option> </select> <label for="button-text-content">Message</label> <textarea id="button-text-content" placeholder="Enter your message here. Use {name} to automatically generate the name of the chatter, {company} for your own company info, or {faction} for your own faction info."></textarea> <div class="char-counter" id="char-counter">0</div> <button id="add-button">Add Button</button> <button id="edit-button" style="display: none;">Save Button</button> </div> </div> <div id="config-settings-tab" class="tab-content"> <label for="api-key">API Key</label> <input type="text" id="api-key" placeholder="Enter your API key" value="${getAPIKey()}"> <button id="import-button">Import Config</button> <button id="export-button">Export Config</button> <button id="clear-cache-button">Clear API Cache</button> </div> <button id="close-ui">Close</button> `; document.body.appendChild(panel); document.querySelectorAll('.tab').forEach(tab => { tab.addEventListener('click', () => { switchTab(tab.dataset.tab); }); }); document.getElementById('add-button').addEventListener('click', addNewButtonConfig); document.getElementById('edit-button').addEventListener('click', editButtonConfig); document.getElementById('close-ui').addEventListener('click', closeUI); document.getElementById('import-button').addEventListener('click', importConfig); document.getElementById('export-button').addEventListener('click', exportConfig); document.getElementById('clear-cache-button').addEventListener('click', clearCache); document.getElementById('button-text-content').addEventListener('input', updateCharCounter); document.getElementById('search-input').addEventListener('input', filterButtonConfigs); document.getElementById('api-key').addEventListener('input', (e) => saveAPIKey(e.target.value)); populateButtonConfigs(); } function switchTab(tabId) { document.querySelectorAll('.tab, .tab-content').forEach(el => { el.classList.remove('active'); }); document.querySelector(`[data-tab="${tabId}"]`).classList.add('active'); document.getElementById(tabId).classList.add('active'); const panel = document.querySelector('.custom-ui-panel'); if (tabId === 'config-list-tab') { panel.classList.add('config-list-tab-active'); } else { panel.classList.remove('config-list-tab-active'); } } function populateButtonConfigs() { const configsContainer = document.getElementById('button-configs'); configsContainer.innerHTML = ''; const configs = getButtonConfigurations(); configs.buttons.forEach((buttonConfig, index) => { const configDiv = document.createElement('div'); configDiv.className = 'draggable'; configDiv.dataset.index = index; configDiv.innerHTML = ` <div><strong>Text:</strong> ${buttonConfig.buttonText}</div> <div><strong>Color:</strong> ${buttonConfig.backgroundColor}</div> <div><strong>Condition:</strong> ${buttonConfig.condition}</div> <div><strong>Message:</strong> ${buttonConfig.text}</div> `; const editButton = document.createElement('button'); editButton.textContent = 'Edit'; editButton.addEventListener('click', () => { selectForEdit(index); switchTab('config-edit-tab'); }); configDiv.appendChild(editButton); const deleteButton = document.createElement('button'); deleteButton.textContent = 'Delete'; deleteButton.addEventListener('click', () => deleteButtonConfig(index)); configDiv.appendChild(deleteButton); const moveUpButton = document.createElement('button'); moveUpButton.textContent = 'Up'; moveUpButton.addEventListener('click', () => moveButtonConfig(index, -1)); configDiv.appendChild(moveUpButton); const moveDownButton = document.createElement('button'); moveDownButton.textContent = 'Down'; moveDownButton.addEventListener('click', () => moveButtonConfig(index, 1)); configDiv.appendChild(moveDownButton); configsContainer.appendChild(configDiv); }); } function filterButtonConfigs() { const searchInput = document.getElementById('search-input').value.toLowerCase(); const searchBy = document.getElementById('search-select').value; const configs = getButtonConfigurations(); const filteredConfigs = configs.buttons.filter(buttonConfig => { const fieldValue = buttonConfig[searchBy].toLowerCase(); return fieldValue.includes(searchInput); }); const configsContainer = document.getElementById('button-configs'); configsContainer.innerHTML = ''; filteredConfigs.forEach((buttonConfig, index) => { const configDiv = document.createElement('div'); configDiv.className = 'draggable'; configDiv.dataset.index = index; configDiv.innerHTML = ` <div><strong>Text:</strong> ${buttonConfig.buttonText}</div> <div><strong>Color:</strong> ${buttonConfig.backgroundColor}</div> <div><strong>Condition:</strong> ${buttonConfig.condition}</div> <div><strong>Message:</strong> ${buttonConfig.text}</div> `; const editButton = document.createElement('button'); editButton.textContent = 'Edit'; editButton.addEventListener('click', () => { selectForEdit(index); switchTab('config-edit-tab'); }); configDiv.appendChild(editButton); const deleteButton = document.createElement('button'); deleteButton.textContent = 'Delete'; deleteButton.addEventListener('click', () => deleteButtonConfig(index)); configDiv.appendChild(deleteButton); const moveUpButton = document.createElement('button'); moveUpButton.textContent = 'Up'; moveUpButton.addEventListener('click', () => moveButtonConfig(index, -1)); configDiv.appendChild(moveUpButton); const moveDownButton = document.createElement('button'); moveDownButton.textContent = 'Down'; moveDownButton.addEventListener('click', () => moveButtonConfig(index, 1)); configDiv.appendChild(moveDownButton); configsContainer.appendChild(configDiv); }); } function selectForEdit(index) { const config = getButtonConfigurations().buttons[index]; document.getElementById('button-text').value = config.buttonText; document.getElementById('button-color').value = config.backgroundColor; document.getElementById('button-condition').value = config.condition; document.getElementById('button-text-content').value = config.text; document.getElementById('add-button').style.display = 'block'; document.getElementById('edit-button').style.display = 'block'; document.getElementById('edit-button').setAttribute('data-edit-index', index); } function deleteButtonConfig(index) { const config = getButtonConfigurations(); config.buttons.splice(index, 1); saveButtonConfigurations(config); populateButtonConfigs(); showNotification('Button deleted.'); } function moveButtonConfig(index, direction) { const config = getButtonConfigurations(); const newIndex = index + direction; if (newIndex >= 0 && newIndex < config.buttons.length) { const buttonConfig = config.buttons.splice(index, 1)[0]; config.buttons.splice(newIndex, 0, buttonConfig); saveButtonConfigurations(config); populateButtonConfigs(); showNotification('Button moved.'); } } function addNewButtonConfig() { const buttonText = document.getElementById('button-text').value; const backgroundColor = document.getElementById('button-color').value; const condition = document.getElementById('button-condition').value; const text = document.getElementById('button-text-content').value; const config = getButtonConfigurations(); config.buttons.push({ buttonText, backgroundColor, condition, text }); saveButtonConfigurations(config); populateButtonConfigs(); highlightButton(config.buttons.length - 1); switchTab('config-list-tab'); clearInputFields(); showNotification('Button added.'); } function editButtonConfig() { const index = parseInt(document.getElementById('edit-button').getAttribute('data-edit-index'), 10); const buttonText = document.getElementById('button-text').value; const backgroundColor = document.getElementById('button-color').value; const condition = document.getElementById('button-condition').value; const text = document.getElementById('button-text-content').value; const config = getButtonConfigurations(); config.buttons[index] = { buttonText, backgroundColor, condition, text }; saveButtonConfigurations(config); populateButtonConfigs(); highlightButton(index); switchTab('config-list-tab'); document.getElementById('add-button').style.display = 'block'; document.getElementById('edit-button').style.display = 'none'; clearInputFields(); showNotification('Button edited.'); } function clearInputFields() { document.getElementById('button-text').value = ''; document.getElementById('button-text-content').value = ''; } function closeUI() { document.querySelector('.custom-ui-panel').remove(); } function createConfigButton() { const settingsPanel = document.querySelector('.settings-panel___IZSDs'); if (settingsPanel && !document.querySelector('#chat-config-button')) { const configButton = document.createElement('button'); configButton.id = 'chat-config-button'; configButton.textContent = 'Edit Chat Buttons'; configButton.addEventListener('click', createUIPanel); settingsPanel.appendChild(configButton); } } function applyButtonConfigurations() { const configs = getButtonConfigurations(); document.querySelectorAll('.chat-box___mHm01').forEach(chatBox => { configs.buttons.forEach(buttonConfig => { const conditionFunc = conditions[buttonConfig.condition]; if (conditionFunc && conditionFunc(chatBox) && !chatBox.querySelector(`[data-button-text="${buttonConfig.buttonText}"]`)) { const button = document.createElement('button'); button.className = 'custom-chat-button'; button.innerText = buttonConfig.buttonText; button.style.backgroundColor = buttonConfig.backgroundColor; button.setAttribute('data-button-text', buttonConfig.buttonText); button.addEventListener('click', (event) => addCustomText(chatBox, buttonConfig.text, event)); button.addEventListener('mousedown', (event) => { if (event.button === 0) { // Left mouse button let timer; const delay = 1000; // 1 second timer = setTimeout(() => { button.classList.remove('recent'); clearRecentButtonInfo(); }, delay); button.addEventListener('mouseup', () => { clearTimeout(timer); }, { once: true }); button.addEventListener('mouseleave', () => { clearTimeout(timer); }, { once: true }); } }); // Add button to the appropriate container const filterContainer = chatBox.querySelector('.chat-box-footer___YK914 tt-modified'); const footerContainer = chatBox.querySelector('.chat-box-footer___YK914'); if (filterContainer) { filterContainer.insertBefore(button, filterContainer.firstChild); } else if (footerContainer) { const buttonContainer = chatBox.querySelector('.button-container') || document.createElement('div'); buttonContainer.className = 'button-container'; buttonContainer.style.display = 'flex'; buttonContainer.style.flexWrap = 'wrap'; footerContainer.insertAdjacentElement('beforebegin', buttonContainer); buttonContainer.appendChild(button); } else { const buttonContainer = document.createElement('div'); buttonContainer.className = 'button-container'; buttonContainer.style.display = 'flex'; buttonContainer.style.flexWrap = 'wrap'; chatBox.insertBefore(buttonContainer, chatBox.firstChild); buttonContainer.appendChild(button); } } }); }); } async function addCustomText(chatBox, messageTemplate, event) { const nameElement = chatBox.querySelector('.typography___Dc5WV'); const name = nameElement ? nameElement.textContent.trim() : 'Trader'; let message = messageTemplate.replace('{name}', name); if (message.includes('{company}')) { const apiKey = getAPIKey(); if (!apiKey) { alert('API key is not set. Please set the API key in the settings.'); return; } let companyInfo = loadCache('companyCache'); if (!companyInfo) { const apiUrl = `https://api.torn.com/company/?selections=profile&key=${apiKey}`; try { const response = await fetch(apiUrl); const data = await response.json(); companyInfo = data.company; if (companyInfo) { saveCache('companyCache', companyInfo); } else { alert('Failed to retrieve company information.'); return; } } catch (error) { alert('Error fetching company information:', error); return; } } const companyType = companyTypes[companyInfo.company_type] || 'Unknown'; const companyDetails = `${companyInfo.rating}* ${companyType}`; message = message.replace('{company}', companyDetails); } if (message.includes('{faction}')) { const apiKey = getAPIKey(); if (!apiKey) { alert('API key is not set. Please set the API key in the settings.'); return; } let factionInfo = loadCache('factionCache'); if (!factionInfo) { const apiUrl = `https://api.torn.com/faction/?selections=basic&key=${apiKey}`; try { const response = await fetch(apiUrl); const data = await response.json(); factionInfo = data; if (factionInfo) { saveCache('factionCache', factionInfo); } else { alert('Failed to retrieve faction information.'); return; } } catch (error) { alert('Error fetching faction information:', error); return; } } const respectFormatted = factionInfo.respect >= 1000000 ? (factionInfo.respect / 1000000).toFixed(1) + 'm' : (factionInfo.respect / 1000).toFixed(1) + 'k'; const factionDetails = `${factionInfo.name}, ${factionInfo.rank.name} Ranked ${respectFormatted} Respect`; message = message.replace('{faction}', factionDetails); } insertMessage(chatBox, message, event.target); } function insertMessage(chatBox, message, targetButton) { navigator.clipboard.writeText(message).then(() => { const textArea = chatBox.querySelector('textarea'); textArea.focus(); textArea.value = ''; // Clear the existing text const startPos = textArea.selectionStart; const endPos = textArea.selectionEnd; textArea.setRangeText(message, startPos, endPos, 'end'); textArea.dispatchEvent(new Event('input', { bubbles: true })); textArea.focus(); textArea.selectionStart = textArea.selectionEnd = startPos + message.length; // Remove the 'recent' class from all buttons chatBox.querySelectorAll('.custom-chat-button').forEach(btn => { btn.classList.remove('recent'); }); // Add the 'recent' class to the clicked button targetButton.classList.add('recent'); // Save the recently clicked button information const chatBoxName = chatBox.querySelector('.chat-box-header__name___jIjjM').textContent; saveRecentButtonInfo(targetButton.getAttribute('data-button-text'), chatBoxName); }); } function applyRecentButtonClass() { const recentButtonInfo = JSON.parse(localStorage.getItem('recentButtonInfo')); if (recentButtonInfo) { document.querySelectorAll('.chat-box___mHm01').forEach(chatBox => { const chatBoxName = chatBox.querySelector('.chat-box-header__name___jIjjM').textContent; if (chatBoxName === recentButtonInfo.chatBoxName) { const button = chatBox.querySelector(`[data-button-text="${recentButtonInfo.buttonText}"]`); if (button) { button.classList.add('recent'); } } }); } } function importConfig() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { const config = JSON.parse(e.target.result); saveButtonConfigurations(config); populateButtonConfigs(); applyButtonConfigurations(); showNotification('Configuration imported.'); }; reader.readAsText(file); }; input.click(); } function exportConfig() { const config = getButtonConfigurations(); const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'chatButtonConfig.json'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); showNotification('Configuration exported.'); } function updateCharCounter() { const textArea = document.getElementById('button-text-content'); const counter = document.getElementById('char-counter'); counter.textContent = textArea.value.length; } function highlightButton(index) { const configsContainer = document.getElementById('button-configs'); const buttonDiv = configsContainer.querySelector(`.draggable[data-index="${index}"]`); if (buttonDiv) { buttonDiv.classList.add('highlight'); buttonDiv.scrollIntoView({ behavior: 'smooth', block: 'center' }); setTimeout(() => { buttonDiv.classList.remove('highlight'); }, 2000); } } addCSS(buttonCSS); const chatContainerObserver = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { createConfigButton(); applyButtonConfigurations(); applyRecentButtonClass(); }); }); const chatContainer = document.querySelector('#chatRoot'); if (chatContainer) { chatContainerObserver.observe(chatContainer, { childList: true, subtree: true }); } applyButtonConfigurations(); applyRecentButtonClass(); })();