SLITHER.IO - DAMNBRUH.COM STANDALONE CHAT MOD (NOW TALK WITH OTHER GAMERS!)

Adds the 143X community chat with profiles, a working GIF search, admin tools, profanity filter, and Discord integration to Slither.io.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         SLITHER.IO - DAMNBRUH.COM STANDALONE CHAT MOD (NOW TALK WITH OTHER GAMERS!)
// @namespace    http://tampermonkey.net/
// @version      16.0 - GIPHY GIF Fix
// @description  Adds the 143X community chat with profiles, a working GIF search, admin tools, profanity filter, and Discord integration to Slither.io.
// @author       dxxthly & waynesg
// @match        http://slither.io/
// @match        https://slither.io/
// @match        http://slither.com/io
// @match        https://slither.com/io
// @match        https://www.damnbruh.com
// @match        https://www.damnbruh.com/*
// @match        http://www.damnbruh.com/*
// @grant        none
// @icon         https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQUNcRl2Rh40pZLhgffYGFDRLbYJ4qfMNwddQ&s
// ==/UserScript==

(function() {
    'use strict';

    // --- GUARDIAN STYLESHEET ---
    (function createGuardianStylesheet() {
        const guardianStyle = document.createElement('style');
        guardianStyle.id = 'mod-guardian-styles';
        guardianStyle.textContent = `html, body { filter: none !important; transform: none !important; }`;
        (document.head || document.documentElement).appendChild(guardianStyle);
        console.log('Guardian Stylesheet is active.');
    })();

    let hasInitialized = false;

    // --- Initialization Checker ---
    const initChecker = setInterval(() => {
        if (document.getElementById('login') && !hasInitialized) {
            hasInitialized = true;
            clearInterval(initChecker);
            main();
        }
    }, 100);

    const systemAccounts = ["system", "discord_bot", "system_badge"];

    // =================================================================================
    // MAIN SCRIPT FUNCTION
    // =================================================================================
    function main() {
        // --- CONFIGURATION ---
        const config = {
            chatMaxMessages: 75,
            // --- Using GIPHY API from the slither.js file ---
            giphyApiKey: 'xWBhUx8jBtCxxjPHvtUzHLZlPGYBUTFq', // Public GIPHY key
            firebaseConfig: {
                apiKey: "AIzaSyCtTloqGNdhmI3Xt0ta11vF0MQJHiKpO7Q",
                authDomain: "chatforslither.firebaseapp.com",
                databaseURL: "https://chatforslither-default-rtdb.firebaseio.com",
                projectId: "chatforslither",
                storageBucket: "chatforslither.appspot.com",
                messagingSenderId: "1045559625491",
                appId: "1:1045559625491:web:79eb8200eb87edac00bce6"
            },
            adminList: [],
            devList: [
                { uid: "CiOpgh1RLBg3l5oXn0SAho66Po93" }, { uid: "PZA5qgKWsPTXc278pyx7NwROf313" },
                { uid: "P75eMwh756Rb6h1W6iqQfHN2Dm92" }, { uid: "tC1VW4WkXEOfK7rCpwGvFcv7MJo1" },
                { uid: "hTinUwrewTYNcXujW1hUaXrScGW2" }
            ],
            vipMembers: [{ uid: "crcOY9hoRrfayStCxMVm7Zdx2W92", name: "stevao" }, { uid: "DhGhICAZwkRa7wuMsyquM9a5uO92", name: "LUANBLAYNER" }]
        };

        // --- STYLES (CSS INJECTION) ---
        const style = document.createElement('style');
        style.textContent = `
            @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap');
            :root { --chat-accent-color: #00E676; --chat-accent-color-dark: #00C853; --chat-bg-color: rgba(22, 24, 30, 0.75); --chat-header-bg: rgba(0, 0, 0, 0.2); --chat-text-color: #E0E0E0; --chat-text-color-muted: #9E9E9E; --chat-border-color: rgba(0, 230, 118, 0.3); --chat-shadow-color: rgba(0, 0, 0, 0.5); --font-main: 'Inter', sans-serif; }
            @keyframes fadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
            #chat-container { position: fixed; left: 20px; top: 100px; width: 380px; height: 450px; z-index: 9999; display: flex; flex-direction: column; background: var(--chat-bg-color); backdrop-filter: blur(12px) saturate(180%); -webkit-backdrop-filter: blur(12px) saturate(180%); border: 1px solid var(--chat-border-color); border-radius: 12px; box-shadow: 0 8px 32px 0 var(--chat-shadow-color); overflow: hidden; user-select: none; resize: both; min-width: 320px; min-height: 250px; font-family: var(--font-main); animation: fadeIn 0.3s ease-out; }
            #chat-header { display: flex; align-items: center; background: var(--chat-header-bg); cursor: move; border-bottom: 1px solid var(--chat-border-color); }
            .chat-tab { flex: 1; padding: 12px 15px; text-align: center; cursor: pointer; font-weight: 500; color: var(--chat-text-color-muted); transition: all 0.25s ease; border-bottom: 3px solid transparent; }
            #chat-tab-main { color: var(--chat-text-color); border-bottom-color: var(--chat-accent-color); background: rgba(0, 230, 118, 0.1); }
            #chat-settings-btn, #chat-hide-btn { background: none; border: none; color: var(--chat-text-color-muted); font-size: 20px; padding: 0 15px; cursor: pointer; transition: all 0.25s ease; }
            #chat-settings-btn:hover, #chat-hide-btn:hover { color: var(--chat-accent-color); transform: scale(1.1); }
            #chat-area { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
            #chat-body, #online-users { flex: 1; padding: 10px 15px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; scrollbar-width: thin; scrollbar-color: var(--chat-accent-color) transparent; }
            #online-users { display: none; } #chat-body p:first-child { color: var(--chat-text-color-muted); text-align: center; margin-top: 10px; }
            #chat-input-container { display: flex; align-items: center; border-top: 1px solid var(--chat-border-color); padding: 5px; background: rgba(0, 0, 0, 0.2); }
            #chat-input { flex-grow: 1; padding: 12px 15px; border: none; background: transparent; color: var(--chat-text-color); outline: none; font-size: 14px; transition: all 0.25s ease; }
            #chat-input::placeholder { color: var(--chat-text-color-muted); } #chat-input:focus { box-shadow: 0 0 15px rgba(0, 230, 118, 0.2) inset; border-radius: 6px; }
            #gif-btn { background: none; border: none; color: var(--chat-text-color-muted); cursor: pointer; font-size: 16px; padding: 0 10px; font-weight: bold; transition: all 0.25s ease; }
            #gif-btn:hover { color: var(--chat-accent-color); transform: scale(1.1); }
            .chat-modal-overlay, #gif-modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 10001; background: rgba(0,0,0,0.6); backdrop-filter: blur(5px); align-items: center; justify-content: center; animation: fadeIn 0.2s ease-out; }
            .chat-modal-content { background: var(--chat-bg-color); backdrop-filter: blur(15px) saturate(180%); -webkit-backdrop-filter: blur(15px) saturate(180%); border-radius: 12px; padding: 25px 30px; width: 420px; display: flex; flex-direction: column; gap: 20px; border: 1px solid var(--chat-border-color); box-shadow: 0 8px 32px 0 var(--chat-shadow-color); }
            .chat-modal-content h2 { margin: 0 0 10px 0; color: var(--chat-accent-color); text-align: center; font-weight: 700; }
            .settings-field { display: flex; flex-direction: column; gap: 8px; } .settings-field label { color: var(--chat-text-color-muted); font-size: 0.9em; font-weight: 500; }
            .settings-field input[type="text"] { width: 100%; box-sizing: border-box; padding: 12px; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; color: var(--chat-text-color); font-size: 1em; transition: all 0.25s ease; }
            .settings-field input[type="text"]:focus { border-color: var(--chat-accent-color); box-shadow: 0 0 10px rgba(0, 230, 118, 0.3); }
            .color-input-wrapper { display: flex; align-items: center; gap: 10px; padding: 5px; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; }
            .color-input-wrapper input[type="color"] { width: 35px; height: 35px; border: none; background: none; cursor: pointer; border-radius: 4px; }
            .modal-buttons { display: flex; justify-content: flex-end; gap: 10px; margin-top: 15px; }
            .modal-btn { padding: 10px 22px; border: none; border-radius: 6px; font-size: 0.95em; font-weight: 500; cursor: pointer; transition: all 0.25s ease; }
            #settings-save-btn, .admin-edit-save-btn { background: var(--chat-accent-color); color: #000; }
            #settings-save-btn:hover, .admin-edit-save-btn:hover { background: var(--chat-accent-color-dark); transform: translateY(-2px); box-shadow: 0 4px 15px rgba(0, 230, 118, 0.3); }
            #settings-cancel-btn, .admin-edit-cancel-btn { background: rgba(255,255,255,0.15); color: var(--chat-text-color); }
            #settings-cancel-btn:hover, .admin-edit-cancel-btn:hover { background: rgba(255,255,255,0.25); }
            .profile-popup { position: fixed; z-index: 10002; display: flex; flex-direction: column; align-items: center; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 320px; background: var(--chat-bg-color); backdrop-filter: blur(15px) saturate(180%); color: var(--chat-text-color); border-radius: 16px; border: 1px solid var(--chat-border-color); box-shadow: 0 8px 40px rgba(0,0,0,0.5); padding: 30px; animation: fadeIn 0.2s ease-out; text-align: center; }
            .profile-popup .avatar { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; border: 4px solid var(--chat-accent-color); margin-bottom: 20px; box-shadow: 0 0 20px rgba(0, 230, 118, 0.4); }
            .profile-popup .close-btn { position: absolute; top: 15px; right: 15px; background: none; border: none; color: var(--chat-text-color-muted); font-size: 1.8em; cursor: pointer; transition: all 0.25s ease; }
            .profile-popup .close-btn:hover { color: #fff; transform: rotate(90deg); }
            .profile-popup h3 { margin: 0; font-size: 1.4em; font-weight: 700; word-break: break-all; }
            .profile-popup p { color: #bbb; font-style: italic; margin: 8px 0 18px 0; word-break: break-word; }
            .profile-popup .admin-actions button { background: #673AB7; color: white; margin-top: 10px; }
            #gif-modal-content { background: #1c1c1e; width: 400px; height: 500px; display: flex; flex-direction: column; border-radius: 10px; border: 1px solid #444; overflow: hidden; }
            #gif-search-header { display: flex; padding: 10px; border-bottom: 1px solid #444; }
            #gif-search-input { flex-grow: 1; padding: 10px; background: #2c2c2e; border: 1px solid #555; border-radius: 5px; color: #fff; outline: none; }
            #gif-results { flex-grow: 1; overflow-y: auto; padding: 10px; display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
            #gif-results img { width: 100%; border-radius: 5px; cursor: pointer; transition: transform 0.2s ease; background: #2c2c2e; }
            #gif-results img:hover { transform: scale(1.05); }
            ::-webkit-scrollbar { width: 8px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--chat-border-color); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: var(--chat-accent-color); }
        `;
        document.head.appendChild(style);

        // --- HELPER FUNCTIONS ---
        const escapeHTML = (str) => { const p = document.createElement('p'); p.appendChild(document.createTextNode(str)); return p.innerHTML; };
        const isDev = (uid) => config.devList.some(dev => dev.uid === uid);
        const isAdmin = (uid) => config.adminList.some(admin => admin.uid === uid);
        const isVip = (uid, name) => config.vipMembers.some(vip => vip.uid === uid && vip.name.toLowerCase() === (name || '').toLowerCase());
        const rainbowTextStyle = (name) => name.split('').map((char, i) => `<span style="color:${["#ef3550","#f48fb1","#7e57c2","#2196f3","#26c6da","#43a047","#eeff41","#f9a825","#ff5722"][i % 9]}; font-weight: bold;">${char}</span>`).join('');
        const vipGlowStyle = (name, color) => `<span style="color:#fff;font-weight:bold;text-shadow:0 0 5px #fff, 0 0 10px ${color}, 0 0 15px ${color};">${name}</span>`;
        const containsProfanity = (text) => {
            if (!text) return false;
            const pattern = /\bn\s*i\s*g\s*g\s*([e3a@])\s*r\b|\bn\s*i\s*g\s*g\s*a\b/gi;
            const normalizedText = text.replace(/[1!]/g, 'i').replace(/[3€]/g, 'e').replace(/@/g, 'a').replace(/[4]/g, 'a').replace(/[9]/g, 'g');
            return pattern.test(normalizedText);
        };

        // --- UI & MODAL CREATION ---
        const chatContainer = document.createElement('div'); chatContainer.id = 'chat-container';
        chatContainer.innerHTML = `
            <div id="chat-header"> <div id="chat-tab-main" class="chat-tab">143X Chat</div> <div id="chat-tab-users" class="chat-tab">Online Users</div> <button id="chat-settings-btn" title="Settings">⚙️</button> <button id="chat-hide-btn" title="Hide Chat">×</button> </div>
            <div id="chat-area"> <div id="chat-body"><p>Initializing...</p></div> <div id="online-users"></div> <div id="chat-input-container"><input id="chat-input" type="text" placeholder="Connecting..." disabled><button id="gif-btn" title="Send a GIF">GIF</button></div> </div>`;
        document.body.appendChild(chatContainer);
        makeDraggable(chatContainer, document.getElementById('chat-header'));
        const settingsModal = document.createElement('div'); settingsModal.id = 'settings-modal-overlay'; settingsModal.className = 'chat-modal-overlay';
        settingsModal.innerHTML = `
            <div id="settings-modal" class="chat-modal-content">
                <h2>Chat & Profile Settings</h2>
                <div class="settings-field"><label>Nickname</label><input type="text" id="settings-nickname" maxlength="20"></div>
                <div class="settings-field"><label>Chat Name Color</label><div class="color-input-wrapper"><input type="color" id="settings-name-color"></div></div>
                <div class="settings-field"><label>Profile Avatar URL</label><input type="text" id="settings-avatar"></div>
                <div class="settings-field"><label>Profile Motto</label><input type="text" id="settings-motto" maxlength="60"></div>
                <div class="modal-buttons"><button id="settings-cancel-btn" class="modal-btn">Cancel</button><button id="settings-save-btn" class="modal-btn">Save</button></div>
            </div>`;
        document.body.appendChild(settingsModal);
        const gifModal = document.createElement('div'); gifModal.id = 'gif-modal-overlay';
        gifModal.innerHTML = `<div id="gif-modal-content"><div id="gif-search-header"><input type="text" id="gif-search-input" placeholder="Search GIPHY GIFs..."></div><div id="gif-results"></div></div>`;
        document.body.appendChild(gifModal);

        // --- FIREBASE & CHAT LOGIC ---
        function loadFirebaseAndInit() {
            const script1 = document.createElement('script'); script1.src = 'https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js';
            script1.onload = () => { const script2 = document.createElement('script'); script2.src = 'https://www.gstatic.com/firebasejs/8.10.0/firebase-database.js';
                script2.onload = () => { const script3 = document.createElement('script'); script3.src = 'https://www.gstatic.com/firebasejs/8.10.0/firebase-auth.js';
                    script3.onload = () => initializeChat(window.firebase); document.head.appendChild(script3); }; document.head.appendChild(script2); }; document.head.appendChild(script1);
        }

        function initializeChat(firebase) {
            if (!firebase.apps.length) firebase.initializeApp(config.firebaseConfig);
            const auth = firebase.auth(); const db = firebase.database();
            auth.signInAnonymously().catch(err => console.error("Firebase Sign-In Error:", err));
            auth.onAuthStateChanged(user => {
                if (user) {
                    const chatInput = document.getElementById('chat-input'); chatInput.disabled = false; chatInput.placeholder = 'Type and press Enter...'; document.getElementById('chat-body').innerHTML = '';
                    let nickname = localStorage.getItem("slitherChatNickname");
                    if (!nickname || containsProfanity(nickname)) {
                        nickname = prompt("Welcome! Please enter a nickname:", "Player") || "Anon";
                        while(containsProfanity(nickname)) {
                           nickname = prompt("That nickname is not allowed. Please choose another:", "Player") || "Anon";
                        }
                        localStorage.setItem("slitherChatNickname", nickname);
                    }
                    const userRef = db.ref("onlineUsers/" + user.uid);
                    userRef.onDisconnect().remove();
                    const localData = { name: nickname, chatNameColor: localStorage.getItem("slitherChatNameColor") || "#FFD700", profileAvatar: localStorage.getItem("slitherChatAvatar") || "", profileMotto: localStorage.getItem("slitherChatMotto") || "" };
                    userRef.update({ ...localData, uid: user.uid, lastActive: firebase.database.ServerValue.TIMESTAMP });
                    setInterval(() => userRef.update({ lastActive: Date.now() }), 45000);
                    listenForOnlineUsers(db);
                    setupEventListeners(db, user);
                }
            });
            let chatMessagesArray = [];
            let latestTimeLoaded = 0;
            const chatBody = document.getElementById('chat-body');

            db.ref("slitherChat").orderByChild("time").limitToLast(config.chatMaxMessages).once("value", (snapshot) => {
                if (!snapshot.exists()) return;
                snapshot.forEach(child => { chatMessagesArray.push({ key: child.key, ...child.val() }); });
                chatMessagesArray.sort((a, b) => a.time - b.time);
                if (chatMessagesArray.length > 0) { latestTimeLoaded = chatMessagesArray[chatMessagesArray.length - 1].time; }
                if (chatBody) { chatBody.innerHTML = ''; for (const msg of chatMessagesArray) { renderChatMessage(msg, chatBody, auth.currentUser?.uid); } chatBody.scrollTop = chatBody.scrollHeight; }
                db.ref("slitherChat").orderByChild("time").startAt(latestTimeLoaded + 1).on("child_added", (newSnapshot) => {
                    const newMsg = { key: newSnapshot.key, ...newSnapshot.val() };
                    if (chatMessagesArray.some(m => m.key === newMsg.key)) return;
                    chatMessagesArray.push(newMsg);
                    if (chatMessagesArray.length > config.chatMaxMessages) chatMessagesArray.shift();
                    if (chatBody) { while (chatBody.children.length >= config.chatMaxMessages) { chatBody.removeChild(chatBody.firstChild); } renderChatMessage(newMsg, chatBody, auth.currentUser?.uid, true); }
                });
            });
        }

        function renderChatMessage(msg, chatBodyElement, currentUid, shouldScroll = false) {
            if (!msg || !msg.uid) return;
            const isValidHexColor = (color) => /^#([0-9a-fA-F]{3}){1,2}$/.test(color);
            let userColor = (msg.chatNameColor && isValidHexColor(msg.chatNameColor)) ? msg.chatNameColor : '#FFD700';
            const isSystemMessage = systemAccounts.includes(msg.uid);
            const isDiscordBot = msg.uid === 'discord_bot';
            const displayName = escapeHTML(msg.name || 'Anon');
            let nameHtml; let roleTagHTML = '';

            if (isDiscordBot) {
                nameHtml = `<span style="color:${userColor};font-weight:bold;">${displayName}</span>`;
                roleTagHTML = ` <span style="background: #7289DA; color: #fff; padding: 2px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 700; vertical-align:middle;">DISCORD</span>`;
            } else if (isAdmin(msg.uid)) {
                nameHtml = `<span class="chat-username" data-uid="${msg.uid}" style="cursor:pointer;">${rainbowTextStyle(displayName)}</span>`;
                roleTagHTML = ` <span style="background: #F44336; color: #fff; padding: 2px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 700; vertical-align:middle;">ADMIN</span>`;
            } else if (isDev(msg.uid)) {
                nameHtml = `<span class="chat-username" data-uid="${msg.uid}" style="cursor:pointer;">${rainbowTextStyle(displayName)}</span>`;
                roleTagHTML = ` <span style="background: #E91E63; color: #fff; padding: 2px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 700; vertical-align:middle;">DEV</span>`;
            } else if (isVip(msg.uid, msg.name)) {
                nameHtml = `<span class="chat-username" data-uid="${msg.uid}" style="cursor:pointer;">${vipGlowStyle(displayName, userColor)}</span>`;
                roleTagHTML = ` <span style="background: #9C27B0; color: #fff; padding: 2px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 700; vertical-align:middle;">VIP</span>`;
            } else if (isSystemMessage) {
                nameHtml = `<span style="color:${userColor};font-weight:bold;">System</span>`;
                roleTagHTML = ` <span style="background: #e74c3c; color: #fff; padding: 2px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 700; vertical-align:middle;">SYSTEM</span>`;
            } else {
                nameHtml = `<span class="chat-username" data-uid="${msg.uid}" style="color:${userColor};font-weight:bold;cursor:pointer;">${displayName}</span>`;
            }

            let finalMessage = '';
            const imageRegex = /(https?:\/\/[^\s]+\.(?:png|jpg|jpeg|gif|webp))/i;
            const imageMatch = msg.text.match(imageRegex);
            if (imageMatch) {
                const textPart = escapeHTML(msg.text.replace(imageRegex, '').trim());
                finalMessage = `${textPart}<br><a href="${imageMatch[0]}" target="_blank"><img src="${imageMatch[0]}" style="max-width:90%; border-radius:6px; margin-top:5px; cursor:pointer;"></a>`;
            } else if (isSystemMessage || isDev(msg.uid) || isAdmin(msg.uid)) {
                finalMessage = msg.text;
            } else {
                finalMessage = escapeHTML(msg.text);
            }

            const el = document.createElement('div');
            const borderColor = (msg.uid === currentUid) ? 'var(--chat-accent-color)' : 'rgba(255,255,255,0.1)';
            const bgColor = (msg.uid === currentUid) ? 'rgba(0, 230, 118, 0.1)' : 'rgba(255,255,255,0.04)';
            el.style.cssText = `margin-bottom: 2px; word-break: break-word; background: ${bgColor}; padding: 8px 12px; border-radius: 6px; color: var(--chat-text-color); font-family: inherit; font-size: 14px; line-height: 1.5; border-left: 3px solid ${borderColor};`;
            const timestamp = new Date(msg.time).toLocaleTimeString([], { hour12: true, hour: '2-digit', minute: '2-digit' });
            el.innerHTML = `<span style="color:var(--chat-text-color-muted); font-size:0.9em; margin-right:8px;">${timestamp}</span> <b>${nameHtml}${roleTagHTML}:</b> ${finalMessage}`;
            chatBodyElement.appendChild(el);
            if (shouldScroll || chatBodyElement.scrollTop >= chatBodyElement.scrollHeight - chatBodyElement.clientHeight - 150) {
                chatBodyElement.scrollTop = chatBodyElement.scrollHeight;
            }
        }

        function listenForOnlineUsers(db) {
            const onlineUsersRef = db.ref("onlineUsers"); const onlineUsersDiv = document.getElementById('online-users'); const onlineUsersTab = document.getElementById('chat-tab-users');
            const tenMinutesAgo = Date.now() - (10 * 60 * 1000);
            onlineUsersRef.orderByChild('lastActive').startAt(tenMinutesAgo).on('value', (snapshot) => {
                if (!onlineUsersDiv) return; onlineUsersDiv.innerHTML = ''; let userCount = 0;
                if (snapshot.exists()) {
                    snapshot.forEach(childSnapshot => {
                        const user = childSnapshot.val(); if (!user || !user.name) return; userCount++;
                        const userEl = document.createElement('div');
                        userEl.style.cssText = `padding: 8px 5px; cursor: pointer; border-radius: 4px; display:flex; align-items:center; transition: background 0.2s ease;`;
                        userEl.className = 'chat-username'; userEl.dataset.uid = user.uid;
                        userEl.onmouseover = () => { userEl.style.background = 'rgba(255,255,255,0.1)'; }; userEl.onmouseout = () => { userEl.style.background = 'transparent'; };
                        const userColor = (user.chatNameColor && /^#([0-9a-fA-F]{3}){1,2}$/.test(user.chatNameColor)) ? user.chatNameColor : '#FFFFFF';
                        let nameHtml;
                        if (isAdmin(user.uid) || isDev(user.uid)) { nameHtml = rainbowTextStyle(escapeHTML(user.name));
                        } else if (isVip(user.uid, user.name)) { nameHtml = vipGlowStyle(escapeHTML(user.name), userColor);
                        } else { nameHtml = `<span style="color:${userColor};">${escapeHTML(user.name)}</span>`; }
                        userEl.innerHTML = nameHtml; onlineUsersDiv.appendChild(userEl);
                    });
                }
                onlineUsersTab.textContent = `Online Users (${userCount})`;
            });
        }

        async function showUserProfile(db, uid, currentUser) {
            if (document.querySelector('.profile-popup')) return;
            try {
                const userSnapshot = await db.ref(`onlineUsers/${uid}`).once('value');
                if (!userSnapshot.exists()) { console.log("User not found or offline."); return; }
                const userData = userSnapshot.val();
                const popup = document.createElement('div'); popup.className = 'profile-popup';
                const defaultAvatar = 'https://i.imgur.com/M6NYjjO.jpeg';
                const avatarUrl = (userData.profileAvatar || '').trim() || defaultAvatar;
                const isCurrentUserAdminOrDev = isAdmin(currentUser.uid) || isDev(currentUser.uid);
                const adminActionsHTML = isCurrentUserAdminOrDev && currentUser.uid !== uid ? `<div class="admin-actions"><button id="admin-edit-btn" class="modal-btn">Edit Profile</button></div>` : '';
                popup.innerHTML = `<button class="close-btn">×</button><img src="${escapeHTML(avatarUrl)}" class="avatar" onerror="this.src='${defaultAvatar}'"><h3 style="margin: 0; color: ${escapeHTML(userData.chatNameColor || '#fff')};">${escapeHTML(userData.name || 'Anonymous')}</h3><p>"${escapeHTML(userData.profileMotto || 'No motto.')}"</p>${adminActionsHTML}`;
                document.body.appendChild(popup);
                popup.querySelector('.close-btn').addEventListener('click', () => popup.remove());
                if (isCurrentUserAdminOrDev && currentUser.uid !== uid) {
                    popup.querySelector('#admin-edit-btn').addEventListener('click', () => { popup.remove(); showAdminEditModal(db, uid, userData); });
                }
            } catch (error) { console.error("Error fetching user profile:", error); }
        }

        function showAdminEditModal(db, targetUid, userData) {
            if (document.getElementById('admin-edit-modal-overlay')) return;
            const editModal = document.createElement('div');
            editModal.id = 'admin-edit-modal-overlay'; editModal.className = 'chat-modal-overlay'; editModal.style.display = 'flex';
            editModal.innerHTML = `
                <div class="chat-modal-content">
                    <h2>Editing: ${escapeHTML(userData.name)}</h2>
                    <div class="settings-field"><label>Nickname</label><input type="text" id="admin-edit-nickname" value="${escapeHTML(userData.name || '')}"></div>
                    <div class="settings-field"><label>Avatar URL</label><input type="text" id="admin-edit-avatar" value="${escapeHTML(userData.profileAvatar || '')}"></div>
                    <div class="settings-field"><label>Motto</label><input type="text" id="admin-edit-motto" value="${escapeHTML(userData.profileMotto || '')}"></div>
                    <div class="modal-buttons"><button class="modal-btn admin-edit-cancel-btn">Cancel</button><button class="modal-btn admin-edit-save-btn">Save</button></div>
                </div>`;
            document.body.appendChild(editModal);
            editModal.querySelector('.admin-edit-cancel-btn').addEventListener('click', () => editModal.remove());
            editModal.querySelector('.admin-edit-save-btn').addEventListener('click', () => {
                const newName = document.getElementById('admin-edit-nickname').value; const newAvatar = document.getElementById('admin-edit-avatar').value; const newMotto = document.getElementById('admin-edit-motto').value;
                if (containsProfanity(newName) || containsProfanity(newMotto)) { alert("Update contains blocked words."); return; }
                db.ref(`onlineUsers/${targetUid}`).update({ name: newName, profileAvatar: newAvatar, profileMotto: newMotto });
                editModal.remove();
            });
        }

        // --- GIPHY GIF FUNCTIONS (from slither.js) ---
        async function openGifPicker() {
            const gifPicker = document.getElementById('gif-modal-overlay');
            const resultsContainer = document.getElementById('gif-results');
            if (!gifPicker || !resultsContainer) return;

            gifPicker.style.display = 'flex';
            resultsContainer.innerHTML = '<div style="color:#888; text-align:center; grid-column: 1 / -1;">Loading trending GIFs...</div>';

            try {
                const response = await fetch(`https://api.giphy.com/v1/gifs/trending?api_key=${config.giphyApiKey}&limit=20&rating=pg-13`);
                const json = await response.json();
                displayGifs(json.data);
            } catch (error) {
                resultsContainer.innerHTML = '<div style="color:#f77; text-align:center; grid-column: 1 / -1;">Could not load GIFs.</div>';
                console.error("Giphy Trending Error:", error);
            }
        }

        async function searchGifs() {
            const searchInput = document.getElementById('gif-search-input');
            const resultsContainer = document.getElementById('gif-results');
            const query = searchInput.value.trim();

            if (!query) {
                openGifPicker(); // Show trending if search is empty
                return;
            }
            resultsContainer.innerHTML = `<div style="color:#888; text-align:center; grid-column: 1 / -1;">Searching for "${escapeHTML(query)}"...</div>`;

            try {
                const response = await fetch(`https://api.giphy.com/v1/gifs/search?api_key=${config.giphyApiKey}&q=${encodeURIComponent(query)}&limit=30&rating=pg-13`);
                const json = await response.json();
                displayGifs(json.data);
            } catch (error) {
                resultsContainer.innerHTML = '<div style="color:#f77; text-align:center; grid-column: 1 / -1;">Search failed.</div>';
                console.error("Giphy Search Error:", error);
            }
        }

        function displayGifs(gifData) {
            const resultsContainer = document.getElementById('gif-results');
            resultsContainer.innerHTML = '';

            if (!gifData || gifData.length === 0) {
                resultsContainer.innerHTML = '<div style="color:#888; text-align:center; grid-column: 1 / -1;">No results found.</div>';
                return;
            }

            gifData.forEach(gif => {
                const img = document.createElement('img');
                img.src = gif.images.fixed_height_small.url;
                img.dataset.originalurl = gif.images.original.url;
                img.alt = gif.title;
                resultsContainer.appendChild(img);
            });
        }


        function setupEventListeners(db, user) {
            const { uid } = user;
            const chatTabMain = document.getElementById('chat-tab-main'); const chatTabUsers = document.getElementById('chat-tab-users'); const chatBody = document.getElementById('chat-body'); const onlineUsers = document.getElementById('online-users');
            chatTabMain.addEventListener('click', () => { chatBody.style.display = 'flex'; onlineUsers.style.display = 'none'; chatTabMain.style.background='rgba(0, 230, 118, 0.1)'; chatTabMain.style.color='var(--chat-text-color)'; chatTabMain.style.borderBottomColor='var(--chat-accent-color)'; chatTabUsers.style.background='transparent'; chatTabUsers.style.color='var(--chat-text-color-muted)'; chatTabUsers.style.borderBottomColor='transparent'; });
            chatTabUsers.addEventListener('click', () => { chatBody.style.display = 'none'; onlineUsers.style.display = 'flex'; chatTabUsers.style.background='rgba(0, 230, 118, 0.1)'; chatTabUsers.style.color='var(--chat-text-color)'; chatTabUsers.style.borderBottomColor='var(--chat-accent-color)'; chatTabMain.style.background='transparent'; chatTabMain.style.color='var(--chat-text-color-muted)'; chatTabMain.style.borderBottomColor='transparent'; });
            document.getElementById('chat-input').addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    e.stopPropagation();
                    const text = e.target.value.trim();
                    if(containsProfanity(text)) { alert("Your message contains blocked words."); return; }
                    if (text) { const name = localStorage.getItem("slitherChatNickname"); const color = localStorage.getItem("slitherChatNameColor") || "#FFD700"; db.ref("slitherChat").push({ uid, name, text, time: firebase.database.ServerValue.TIMESTAMP, chatNameColor: color }); db.ref("discordBridge").push({ uid, name, text, time: firebase.database.ServerValue.TIMESTAMP }); e.target.value = ''; }
                }
            });
            const settingsOverlay = document.getElementById('settings-modal-overlay');
            document.getElementById('chat-settings-btn').addEventListener('click', () => { document.getElementById('settings-nickname').value = localStorage.getItem("slitherChatNickname") || ''; document.getElementById('settings-name-color').value = localStorage.getItem("slitherChatNameColor") || '#FFD700'; document.getElementById('settings-avatar').value = localStorage.getItem("slitherChatAvatar") || ''; document.getElementById('settings-motto').value = localStorage.getItem("slitherChatMotto") || ''; settingsOverlay.style.display = 'flex'; });
            document.getElementById('settings-cancel-btn').addEventListener('click', () => settingsOverlay.style.display = 'none');
            document.getElementById('settings-save-btn').addEventListener('click', () => {
                const nickname = document.getElementById('settings-nickname').value.trim().slice(0, 20) || 'Anon';
                const motto = document.getElementById('settings-motto').value.trim();
                if(containsProfanity(nickname) || containsProfanity(motto)) { alert("Your nickname or motto contains blocked words."); return; }
                const newData = { name: nickname, chatNameColor: document.getElementById('settings-name-color').value, profileAvatar: document.getElementById('settings-avatar').value.trim(), profileMotto: motto };
                localStorage.setItem("slitherChatNickname", newData.name); localStorage.setItem("slitherChatNameColor", newData.chatNameColor); localStorage.setItem("slitherChatAvatar", newData.profileAvatar); localStorage.setItem("slitherChatMotto", newData.profileMotto); db.ref("onlineUsers/" + uid).update(newData); settingsOverlay.style.display = 'none';
            });
            document.getElementById('chat-hide-btn').addEventListener('click', () => document.getElementById('chat-container').style.display = 'none');
            document.body.addEventListener('click', e => { if (e.target.closest('.chat-username')) showUserProfile(db, e.target.closest('.chat-username').dataset.uid, user); });

            // --- CORRECTED GIF Event Listeners ---
            const gifOverlay = document.getElementById('gif-modal-overlay');
            document.getElementById('gif-btn').addEventListener('click', () => { openGifPicker(); });
            gifOverlay.addEventListener('click', (e) => { if (e.target.id === 'gif-modal-overlay') gifOverlay.style.display = 'none'; });
            document.getElementById('gif-search-input').addEventListener('keydown', e => {
                if (e.key === 'Enter') { e.preventDefault(); e.stopPropagation(); searchGifs(); }
            });
            document.getElementById('gif-results').addEventListener('click', e => {
                if (e.target.tagName === 'IMG') {
                    const gifUrl = e.target.dataset.originalurl;
                    const name = localStorage.getItem("slitherChatNickname");
                    const color = localStorage.getItem("slitherChatNameColor") || "#FFD700";
                    db.ref("slitherChat").push({ uid, name, text: gifUrl, time: firebase.database.ServerValue.TIMESTAMP, chatNameColor: color });
                    db.ref("discordBridge").push({ uid, name, text: gifUrl, time: firebase.database.ServerValue.TIMESTAMP });
                    gifOverlay.style.display = 'none';
                }
            });
        }

        // --- UTILITY ---
        function makeDraggable(el, handle) { let p1=0, p2=0, p3=0, p4=0; handle.onmousedown = (e) => { e.preventDefault(); p3 = e.clientX; p4 = e.clientY; document.onmouseup = () => {document.onmouseup=null; document.onmousemove=null;}; document.onmousemove = (e) => { e.preventDefault(); p1=p3-e.clientX; p2=p4-e.clientY; p3=e.clientX; p4=e.clientY; el.style.top=(el.offsetTop-p2)+"px"; el.style.left=(el.offsetLeft-p1)+"px"; }; }; }

        // --- RUN ---
        loadFirebaseAndInit();
    }
})();