TORN: Better Chat

Improvements to the usability of chats 2.0.

目前为 2023-10-26 提交的版本。查看 最新版本

// ==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或关注我们的公众号极客氢云获取最新地址