您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Improvements to the usability of chats 2.0.
当前为
// ==UserScript== // @name TORN: Better Chat // @namespace dekleinekobini.betterchat // @license GPL-3 // @version 1.1.0 // @description Improvements to the usability of chats 2.0. // @author DeKleineKobini [2114440] // @match https://www.torn.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @run-at document-start // @grant GM_addStyle // ==/UserScript== "use strict"; const SETTINGS_KEY = "better-chat-settings"; // Settings have been moved to the in-game settings window. const DEFAULT_SETTINGS = { messages: { hideAvatars: true, compact: true, leftAlignedText: true, // left align all text, prefixed by the name (supports the mini-profile as well), even for private chats highlight: [ // Colors can be specified as: // - hex color (include #, only full format = 6 numbers) // - custom colors (check below); "torntools-green" // Search is just text, except "%player%" where it used the current players name. {id: 0, color: "torntools-green", search: "%player%"}, ], }, box: { groupRight: true, // opening chat logic to put private chat left of group chats hideAvatars: true, }, }; let localSettings = loadSettings(); const TEXT_COLORS = { "torntools-green": "#7ca900", }; (() => { setupStyles(); setupChatModifier().catch((reason) => console.error("[Better Chat] Failed to initialize the chat modifier.", reason)); setupScriptSettings().catch((reason) => console.error("[Better Chat] Failed to initialize the settings tab.", reason)); })(); function includeStyle(styleRules) { if (typeof GM_addStyle !== "undefined") { GM_addStyle(styleRules); } else { const styleElement = document.createElement("style"); styleElement.setAttribute("type", "text/css"); styleElement.innerHTML = styleRules; document.head.appendChild(styleElement); } } function setupStyles() { if (localSettings.messages.hideAvatars) { includeStyle(` [class*='chat-box-body__avatar___'] { display: none; } `); } if (localSettings.messages.compact) { includeStyle(` [class*='chat-box-body__wrapper___'] { margin-bottom: 0px !important; } [class*='chat-box-body___'] > div:last-child { margin-bottom: 8px !important; } `); } if (localSettings.box.groupRight) { includeStyle(` [class*='group-chat-box___'] { gap: 3px; } [class*='group-chat-box__chat-box-wrapper___'] { margin-right: 0 !important; } `); } if (localSettings.messages.leftAlignedText) { includeStyle(` [class*='chat-box-body__sender___'] { display: unset !important; font-weight: 700; } [class*='chat-box-body__message-box___'] [class*='chat-box-body__sender___'] { margin-right: 4px; } [class*='chat-box-body__message-box___'] { background: none !important; border-radius: none !important; color: initial !important; padding: 0 !important; } [class*='chat-box-body__message-box--self___'] { background: none !important; border-radius: none !important; color: initial !important; padding: 0 !important; } [class*='chat-box-body__wrapper--self___'] { justify-content: normal !important; } [class*='chat-box-body__wrapper--self___'] > [class*='chat-box-body__message___'], [class*='chat-box-body__message___'] { color: var(--chat-text-color) !important; } `); } if (localSettings.box.hideAvatars) { includeStyle(` [class*='avatar__avatar-status-wrapper___'] > img { display: none; } `); } includeStyle(` #better-chat-settings-icon button { color: #f7f7f7; } .better-chat-settings-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 1000; display: flex; align-items: center; justify-content: center; } .better-chat-settings-popup { background-color: #f7f7f7; width: 300px; height: 300px; padding: 4px; } .better-chat-settings-title { display: block; font-size: 1.25em; font-weight: bold; margin-bottom: 2px; } .better-chat-settings-description { display: block; font-size: 0.9em; margin-bottom: 2px; } .better-chat-settings-subtitle { display: block; font-weight: bold; margin-bottom: 2px; } .better-chat-settings-subtitle:not(:first-child) { margin-top: 4px; } .better-chat-settings-popup > section { display: flex; align-items: center; gap: 2px; margin-bottom: 1px; } .better-chat-settings-popup button { cursor: pointer; } .better-chat-settings-highlight-entry { display: flex; gap: 4px; } `); } async function setupChatModifier() { const group = await new Promise((resolve) => { new MutationObserver((_, observer) => { const group = findByClass(document, "group-chat-box___"); if (group) { observer.disconnect(); resolve(group); } }).observe(document, {childList: true, subtree: true}); }); group.childNodes.forEach(processChat) new MutationObserver((mutations) => { mutations.flatMap((mutation) => [...mutation.addedNodes]).forEach(processChat); }).observe(group, {childList: true}); } function processChat(chatNode) { if (localSettings.box.groupRight) { const avatarElement = findByClass(chatNode, "chat-box-header__avatar___", "> *"); const isGroup = avatarElement.tagName.toLowerCase() === "svg"; if (isGroup) { chatNode.style.order = "1"; } } const bodyElement = findByClass(chatNode, "chat-box-body___"); bodyElement.childNodes.forEach(processMessage); new MutationObserver((mutations) => { mutations.flatMap((mutation) => [...mutation.addedNodes]).forEach(processMessage); }).observe(chatNode, {childList: true}); new MutationObserver(() => { bodyElement.childNodes.forEach(processMessage); }).observe(bodyElement, {childList: true}); } function getCurrentPlayername() { const websocketElement = document.getElementById("websocketConnectionData"); if (websocketElement) { const data = JSON.parse(websocketElement.textContent); return data.playername; } const sidebarElement = findByClass(document, "menu-value___"); if (sidebarElement) { // Get name from the sidebar return sidebarElement.textContent; } const attackerElement = document.querySelector(".user-name.left"); if (attackerElement) { // Attack name return attackerElement.textContent; } return "Yourself"; } function processMessage(messageNode) { if (messageNode.querySelector(".color-chatError")) { // This is a "Connecting to the server" message, don't process it. return; } const senderElement = findByClass(messageNode, "chat-box-body__sender___"); const messageElement = findByClass(messageNode, "chat-box-body__message___"); const currentPlayer = getCurrentPlayername(); let senderName = senderElement.textContent.substring(0, senderElement.textContent.length - 1); if (senderName === "newMessage") { // Take the name from the sidebar. senderElement.textContent = `${currentPlayer}:`; senderName = currentPlayer; } if (localSettings.messages.highlight.length > 0) { const highlights = localSettings.messages.highlight .map(({search, color}) => ({ search: search.replaceAll("%player%", currentPlayer), color: convertColor(color), })); const nameHighlight = highlights.find(({search}) => senderName.toLowerCase() === search.toLowerCase()); if (nameHighlight) { senderElement.setAttribute("style", `background-color: ${nameHighlight.color} !important;`); } const messageHighlight = highlights.find(({search}) => messageElement.textContent.toLowerCase().includes(search.toLowerCase())); if (messageHighlight) { const wrapperElement = findByClass(messageNode, "chat-box-body__wrapper___"); wrapperElement.setAttribute("style", `background-color: ${messageHighlight.color} !important;`); } } } function baseColor(color) { if (color in TEXT_COLORS) color = TEXT_COLORS[color]; return color; } function convertColor(color) { color = baseColor(color); return color.length === 7 ? `${color}6e` : color; } function findByClass(node, className, subSelector = "") { return node.querySelector(`[class*='${className}'] ${subSelector}`.trim()) } async function setupScriptSettings() { const chatList = await new Promise((resolve) => { new MutationObserver((_, observer) => { const group = findByClass(document, "chat-app__chat-list-chat-box-wrapper___"); if (group) { observer.disconnect(); resolve(group); } }).observe(document, {childList: true, subtree: true}); }); handleAppPanel(chatList); new MutationObserver(() => { handleAppPanel(chatList); }).observe(chatList, {childList: true}); } function handleAppPanel(chatlist) { const panel = findByClass(chatlist, "chat-app__panel___"); if (!panel) return; modifyAppPanel(panel); new MutationObserver(() => { modifyAppPanel(panel); }).observe(panel, {childList: true}); } function modifyAppPanel(panel) { if (panel.classList.contains("better-chat-settings")) return; panel.classList.add("better-chat-settings") const icon = createScriptSettingsIcon(); const panelHeader = findByClass(panel, "chat-list-header__actions___"); panelHeader.insertAdjacentElement("afterbegin", icon) } function createScriptSettingsIcon() { const icon = document.createElement("div"); icon.id = "better-chat-settings-icon"; icon.classList.add("chat-list-header__action-wrapper___b5asl"); icon.innerHTML = ` <button type="button" class="chat-list-header__button___vfszc"> BS </button> `; icon.addEventListener("click", (event) => { event.stopPropagation(); showScriptSettings(); }, {capture: true}); return icon; } function showScriptSettings() { if (document.querySelector(".better-chat-settings-overlay")) return; const popup = createPopup(); const overlay = createOverlay(); overlay.appendChild(popup); document.body.appendChild(overlay); } function loadSettings() { const storedSettings = localStorage.getItem(SETTINGS_KEY); const settings = storedSettings ? JSON.parse(storedSettings) : {}; const defaultSettings = DEFAULT_SETTINGS; mergeRecursive(defaultSettings, settings); return defaultSettings; } function mergeRecursive(target, otherObject) { for (const key in otherObject) { try { if (typeof otherObject[key] == "object" && !Array.isArray(otherObject[key])) { target[key] = mergeRecursive(target[key], otherObject[key]); } else { target[key] = otherObject[key]; } } catch (e) { target[key] = otherObject[key]; } } return target; } function createPopup() { const popup = document.createElement("div"); popup.classList.add("better-chat-settings-popup"); appendTitle("Better Chat - Settings"); appendDescription("You can change your Better Chat settings here. Reload after changes to apply them."); appendSubtitle("Messages"); appendCheckbox( "messages-hideAvatars", "Hide avatars in the messages.", () => localSettings.messages.hideAvatars, (newValue) => { localSettings.messages.hideAvatars = newValue; }, ); appendCheckbox( "messages-compact", "Make the chat significantly compacter.", () => localSettings.messages.compact, (newValue) => { localSettings.messages.compact = newValue; }, ); appendCheckbox( "messages-leftAlignedText", "Left align all messages.", () => localSettings.messages.leftAlignedText, (newValue) => { localSettings.messages.leftAlignedText = newValue; }, ); appendSubtitle("Boxes"); appendCheckbox( "box-groupRight", "Move group chats to always be to the right of private chats.", () => localSettings.box.groupRight, (newValue) => { localSettings.box.groupRight = newValue; }, ); appendCheckbox( "box-hideAvatars", "Move avatars in the boxes.", () => localSettings.box.hideAvatars, (newValue) => { localSettings.box.hideAvatars = newValue; }, ); appendSubtitle("Highlights"); appendHighlightList( () => localSettings.messages.highlight, ({search, color}) => { localSettings.messages.highlight = localSettings.messages.highlight.filter((highlight) => highlight.search !== search && highlight.color !== color) }, (item) => localSettings.messages.highlight.push(item), ); return popup; function appendTitle(title) { const titleElement = document.createElement("span"); titleElement.classList.add("better-chat-settings-title"); titleElement.innerText = title; popup.appendChild(titleElement); } function appendDescription(title) { const titleElement = document.createElement("span"); titleElement.classList.add("better-chat-settings-description"); titleElement.innerText = title; popup.appendChild(titleElement); } function appendSubtitle(title) { const titleElement = document.createElement("span"); titleElement.classList.add("better-chat-settings-subtitle"); titleElement.innerText = title; popup.appendChild(titleElement); } function appendCheckbox(id, labelText, valueGetter, valueSetter) { const inputId = `setting-${id}`; const input = document.createElement("input"); input.checked = valueGetter(); input.id = inputId; input.type = "checkbox"; input.addEventListener("change", (event) => { valueSetter(event.currentTarget.checked); localStorage.setItem(SETTINGS_KEY, JSON.stringify(localSettings)); }, {capture: true}); const label = document.createElement("label"); label.setAttribute("for", inputId); label.innerText = labelText; const section = document.createElement("section"); section.appendChild(input); section.appendChild(label); popup.appendChild(section); } function appendHighlightList(valueGetter, valueRemover, valueAdder) { const list = document.createElement("ul"); valueGetter().forEach((item) => appendRow(item)); const addButton = document.createElement("button"); addButton.textContent = "add"; addButton.addEventListener("click", () => { const item = {search: "%player%", color: "#7ca900"}; valueAdder(item); appendRow(item, true); localStorage.setItem(SETTINGS_KEY, JSON.stringify(localSettings)); }); list.appendChild(addButton); popup.appendChild(list); function appendRow(item, beforeButton = false) { const itemElement = document.createElement("li"); itemElement.classList.add("better-chat-settings-highlight-entry"); const searchInput = document.createElement("input"); searchInput.type = "text"; searchInput.placeholder = "Search..."; searchInput.value = item.search; searchInput.addEventListener("change", (event) => { item.search = event.currentTarget.value; localStorage.setItem(SETTINGS_KEY, JSON.stringify(localSettings)); }); itemElement.appendChild(searchInput); const colorInput = document.createElement("input"); colorInput.type = "color"; colorInput.value = baseColor(item.color); colorInput.addEventListener("change", (event) => { item.color = event.currentTarget.value; localStorage.setItem(SETTINGS_KEY, JSON.stringify(localSettings)); }); itemElement.appendChild(colorInput); const removeButton = document.createElement("button"); removeButton.textContent = "remove"; removeButton.addEventListener("click", () => { itemElement.remove(); valueRemover(item); localStorage.setItem(SETTINGS_KEY, JSON.stringify(localSettings)); }); itemElement.appendChild(removeButton); if (beforeButton) { list.insertBefore(itemElement, addButton); } else { list.appendChild(itemElement); } } } } function createOverlay() { const overlay = document.createElement("div"); overlay.classList.add("better-chat-settings-overlay"); overlay.addEventListener("click", (event) => { if (event.target !== overlay) return; overlay.remove(); }, {once: true}); return overlay; }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址