Xat Plus

Unaffiliated extension for the Xat HTML5 Chatbox. RIP Xat Edition.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Xat Plus
// @namespace    http://sulptax.io/
// @version      0.4.7
// @description  Unaffiliated extension for the Xat HTML5 Chatbox. RIP Xat Edition.
// @author       sulptax
// @match        https://xat.com/content/web/*/box/www/classic.html
// @require      http://code.jquery.com/jquery-3.5.1.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js
// @grant        unsafeWindow
// ==/UserScript==
const secret = 'sulptax';

function xp_encrypt(str) {
    return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(str));
}

function xp_decrypt(str) {
    return CryptoJS.enc.Base64.parse(str).toString(CryptoJS.enc.Utf8);
}

function xp_aes_encrypt(str) {
    return CryptoJS.AES.encrypt(str, secret).toString();
}

function xp_aes_decrypt(str) {
    var bytes = CryptoJS.AES.decrypt(str, secret);
    return bytes.toString(CryptoJS.enc.Utf8);
}

// Allow functions below to be used in the console, not like these are used for storing important data anyways.
// You must be at the chat directly for these to work:
// https://xat.com/content/web/R00022/box/www/classic.html
unsafeWindow.xp_encrypt = xp_encrypt;
unsafeWindow.xp_decrypt = xp_decrypt;

var users = {
    "585435788": {
        "type": "color", // color, pawn, pawn2
        "reghide": true, // like reghide power
        "v": "000001", // the color value
    },
    "400444434": {
        "type": "color",
        "v": "y",
        "effective": true, // overwrites the message pawn as well if true, This is the case by default.
        "persistent": true // keep the pawn color regardless of the pawn used in the list
    },
    "287773358": { // Shadez bot is here for demonstration purposes, because it's everywhere.
        "type": "pawn2", // pawn2 changes the pawn type
        "v": "p1bot" // examples: p1bot, p1emerald, p1pwn
    },
    "981989": {
        "type": "pawn2",
        "v": "p1emerald#y" // can also use color codes in pawn2 value
    },
    "30000013": {
        "type":"pawn2",
        "v": "p1emerald#r#r"
    }
}

var effectiveChanges = {}
const observer = new MutationObserver((mutationList, observer) => {
    mutationList.forEach((mutation) => {
        if (mutation.type === "childList") {
            if (mutation.target.id == "idvisitors" || mutation.target.id == "idfriends" || mutation.target.id == "messages") {
                mutation.addedNodes.forEach((node) => {
                    Object.keys(users).forEach(function(user) {
                        var color = "";
                        var holder = $(node).find(".holder:first");
                        var res = holder.attr("data-sm");
                        if ($(node).attr("data-user") == user) {
                            var str = res
                            if (res.split("#")[1] != "ff0000" && res.split("#")[1] != "ff0000)_20") {
                                if (users[$(node).attr("data-user")]['type'] == "color") {
                                    color = users[$(node).attr("data-user")]['v']
                                    res = str.split("#");
                                    res[1] = color;
                                    res = res.join("#");
                                    res = res + ")_20"
                                }
                                if (users[$(node).attr("data-user")]['type'] == "pawn") {
                                    res = users[$(node).attr("data-user")]['v']
                                }
                                var res3 = "";
                                if (users[$(node).attr("data-user")]['type'] == "pawn2") {
                                    var res2 = str.split("#");
                                    res2[0] = res2[0].split("(")[0] + "(" + users[$(node).attr("data-user")]['v'];
                                    res = res2.join("#")
                                    res3 = res.split("#", 2).join("#") + ")_20";
                                } else {
                                    res3 = res;
                                }
                                if (Object.keys(users[$(node).attr("data-user")]).includes("reghide")) {
                                    $(node).find(".relIcon2").hide();
                                }
                                var filters = "";
                                if (Object.keys(users[$(node).attr("data-user")]).includes("glow")) {
                                    if(isColor(users[$(node).attr("data-user")]['glow'])) {
                                        filters = filters + "drop-shadow(0px 0px 0.2rem " + users[$(node).attr("data-user")]['glow'] + ") "
                                    }
                                }
                                if(filters) {
                                   holder.css("filter", filters);
                                }
                                if (!(Object.keys(users[$(node).attr("data-user")]).includes("effective") && !(users[$(node).attr("data-user")]['effective'] == true))) {
                                    effectiveChanges[$(node).find(".message").text()] = res3
                                } else if (color && (Object.keys(users[$(node).attr("data-user")]).includes("persistent") && users[$(node).attr("data-user")]['persistent'])) {
                                    effectiveChanges[$(node).find(".message").text()] = "s_(p1pwn#" + color + ")_20"
                                }
                                holder.attr("data-sm", res)
                            }
                        }
                    });
                });
            }
        }
    });
});
messages.addMessageP = messages.addMessage
messages.addMessage = function(t) {
    messages.addMessageP(t);
}
visitors.addVisitorP = visitors.addVisitor
visitors.addVisitor = function(e, l) {
    visitors.addVisitorP(e, l);
    var e_parsed = JSON.parse(e);
    if (Object.keys(e_parsed).includes("image")) {
        e_parsed['image'].split("#").forEach((im) => {
            if (true) { // im.toString(CryptoJS.enc.Utf8)
                try {
                    var checkForPawn = xp_decrypt(im)
                    if (checkForPawn && (checkForPawn.startsWith('{') && checkForPawn.endsWith('}'))) {
                        // example JSON
                        // {"type":"pawn2", "v":"p1emerald#y#hat3#w1i0ic#y", "reghide": true}
                        // {"type":"pawn2", "v":"p1emerald#y#hat3#w1i0ic#y", "reghide": true, "glow":"red"}
                        console.log("JSON PAWN DETECTED IN avatar/image:", checkForPawn);
                        try {
                            users[e_parsed['id']] = JSON.parse(checkForPawn);
                        } catch (e) {
                            console.log("Error parsing pawn code");
                        }
                    } else if (checkForPawn && checkForPawn.substring(0, 2) == "p1") {
                        console.log("NORMAL PAWN DETECTED IN avatar/image:", checkForPawn);
                        users[e_parsed['id']] = {
                            "type": "pawn2",
                            "v": checkForPawn
                        }
                    }
                } catch(e) {
                    // decrypt error?
                }
            }
        })
    }
}
const observer2 = new MutationObserver((mutationList, observer) => {
    mutationList.forEach((mutation) => {
        if (mutation.type === "childList") {
            mutation.addedNodes.forEach((node) => {
                if (Object.keys(effectiveChanges).includes($(node).find(".messagesName").find(".message").text())) {
                    $(node).find(".messagesName").find(".holder:first").attr("data-sm", effectiveChanges[$(node).find(".messagesName").find(".message").text()])
                }
            });
        }
    });
});
observer.observe(document.querySelector("#visitorsContainer"), {
    childList: true,
    subtree: true
});
observer2.observe(document.querySelector("#messagesContainer"), {
    childList: true,
    subtree: true
});
function isColor(color) {
    var div = $("<div>");
    div.css("border", "1px solid " + color);
    return (div.css("border-color") != "")
}