您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Improvements to the usability of chats 2.0.
当前为
// ==UserScript== // @name TORN: Better Chat // @namespace dekleinekobini.betterchat // @license GPL-3 // @version 1.0.6 // @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 = { 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. {color: "torntools-green", search: "%player%"}, ] }, box: { groupRight: true, // opening chat logic to put private chat left of group chats hideAvatars: true, }, }; const TEXT_COLORS = { "torntools-green": "#7ca900", }; const URL_PATTERN = /(https?:\/\/)?(\.?[\w-]+)+\.([a-z]{2,})(\/[\w\.]*)*(\?[\w=]+)?(#[\w/=]+)?/gi; (() => { setupStyles(); setupChatModifier().catch((reason) => console.error("[Better Chat] Failed to initialize the chat modifier.", 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 (settings.messages.hideAvatars) { includeStyle(` [class*='chat-box-body__avatar___'] { display: none; } `); } if (settings.messages.compact) { includeStyle(` [class*='chat-box-body__wrapper___'] { margin-bottom: 0px !important; } [class*='chat-box-body___'] > div:last-child { margin-bottom: 8px !important; } `); } if (settings.box.groupRight) { includeStyle(` [class*='group-chat-box___'] { gap: 3px; } [class*='group-chat-box__chat-box-wrapper___'] { margin-right: 0 !important; } `); } if (settings.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 (settings.box.hideAvatars) { includeStyle(` [class*='avatar__avatar-status-wrapper___'] > img { display: none; } `); } } 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 (settings.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 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 = findByClass(document, "menu-value___")?.textContent // Get name from the sidebar || document.querySelector(".user-name.left")?.textContent // Attack name on desktop || "Yourself"; 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 (settings.messages.highlight.length > 0) { const highlights = settings.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 convertColor(color) { if (color in TEXT_COLORS) color = TEXT_COLORS[color]; return color.length === 7 ? `${color}6e` : color; } function findByClass(node, className, subSelector = "") { return node.querySelector(`[class*='${className}'] ${subSelector}`.trim()) }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址