SLITHER.IO STANDALONE CHAT MOD (NOW TALK WITH SLITHER GAMERS!)

Adds the full 143X chat with GIF sending, clickable links, and bold text formatting.

As of 19. 07. 2025. See the latest version.

// ==UserScript==
// @name         SLITHER.IO STANDALONE CHAT MOD (NOW TALK WITH SLITHER GAMERS!)
// @namespace    http://tampermonkey.net/
// @version      9.0
// @description  Adds the full 143X chat with GIF sending, clickable links, and bold text formatting.
// @author       dxxthly & waynesg 
// @match        http://slither.io/
// @match        https://slither.io/
// @match        http://slither.com/io
// @match        https://slither.com/io
// @grant        none
// @icon         https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQUNcRl2Rh40pZLhgffYGFDRLbYJ4qfMNwddQ&s
// ==/UserScript==

(function() {
    'use strict';

    let hasInitialized = false;

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

    function main() {
        let isChatVisible = true;

        const config = {
            giphyApiKey: 'xWBhUx8jBtCxxjPHvtUzHLZlPGYBUTFq', // GIPHY's public demo key
            chatMaxMessages: 75,
            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" },
            devList: [{ uid: "CiOpgh1RLBg3l5oXn0SAho66Po93" }, { uid: "PZA5qgKWsPTXc278pyx7NwROf313" }, { uid: "P75eMwh756Rb6h1W6iqQfHN2Dm92" }],
            vipMembers: [{ uid: "crcOY9hoRrfayStCxMVm7Zdx2W92", name: "stevao" }, { uid: "DhGhICAZwkRa7wuMsyquM9a5uO92", name: "LUANBLAYNER" }]
        };

        const style = document.createElement('style');
        style.textContent = `
            #chat-container { position: fixed; left: 20px; top: 100px; width: 380px; height: 400px; z-index: 9999; display: flex; flex-direction: column; background: rgba(28, 28, 32, 0.97); border: 1px solid #4CAF50; border-radius: 8px; box-shadow: 0 5px 20px rgba(0,0,0,0.3); overflow: hidden; user-select: none; resize: both; min-width: 320px; min-height: 250px; }
            #chat-header { display: flex; align-items: center; border-bottom: 1px solid #4CAF50; background: rgba(0,0,0,0.2); cursor: move; }
            .chat-tab { flex: 1; padding: 10px 12px; text-align: center; cursor: pointer; font-weight: 500; transition: background 0.2s, color 0.2s; }
            #chat-tab-main { background: rgba(76, 175, 80, 0.25); color: #fff; } #chat-tab-users { background: transparent; color: #ccc; }
            #chat-settings-btn, #chat-hide-btn { background: none; border: none; color: #ccc; font-size: 20px; padding: 0 12px; cursor: pointer; transition: color 0.2s; border-left: 1px solid rgba(255,255,255,0.1); }
            #chat-settings-btn:hover, #chat-hide-btn:hover { color: white; }
            #chat-area { flex: 1; display: flex; flex-direction: column; overflow: hidden; position: relative; }
            #chat-body, #online-users { flex: 1; padding: 10px 15px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; scrollbar-width: thin; scrollbar-color: #4CAF50 rgba(0,0,0,0.2); }
            #online-users { display: none; gap: 0; }
            #chat-input-container { display: flex; align-items: center; border-top: 1px solid #4CAF50; background-color: #1c1c20; }
            #chat-input { flex-grow: 1; padding: 12px 15px; border: none; background: transparent; color: #e0e0e0; outline: none; font-size: 14px; }
            .chat-action-btn { background: transparent; border: none; border-left: 1px solid #333; color: #aaa; font-size: 14px; cursor: pointer; padding: 0 12px; font-weight: bold; }
            .chat-action-btn:hover { background: #333; color: #fff; }
            .chat-message { line-height: 1.5; word-break: break-word; background: rgba(255,255,255,0.04); padding: 8px 12px; border-radius: 6px; color: #e0e0e0; }
            .chat-message a { color: #3498db; text-decoration: none; font-weight: bold; } .chat-message a:hover { text-decoration: underline; }
            .chat-image-preview { max-width: 90%; max-height: 180px; border-radius: 6px; margin-top: 8px; cursor: pointer; border: 1px solid #444; }
            .chat-username { font-weight: bold; cursor: pointer; text-decoration: underline dotted; }
            .chat-modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 10001; background: rgba(0,0,0,0.75); align-items: center; justify-content: center; }
            .chat-modal-content { background: #2E2E34; border-radius: 10px; padding: 25px 30px; min-width: 400px; display: flex; flex-direction: column; gap: 15px; border: 1px solid #555; text-align: center; }
            .settings-field { text-align: left; }
            #get-full-mod-btn { background-color: #333; color: #4CAF50; border: 1px solid #4CAF50; border-radius: 5px; padding: 8px; margin-top: 10px; cursor: pointer; transition: background-color 0.2s; } #get-full-mod-btn:hover { background-color: #444; }
            #show-chat-button { display: none; position: fixed; bottom: 20px; left: 20px; z-index: 9998; width: 50px; height: 50px; background-color: #2E2E34; color: #fff; border: 1px solid #4CAF50; border-radius: 50%; font-size: 24px; cursor: pointer; text-align: center; line-height: 50px; box-shadow: 0 4px 15px rgba(0,0,0,0.4); transition: transform 0.2s; } #show-chat-button:hover { transform: scale(1.1); }
            #gif-picker-modal { display: none; position: absolute; bottom: 50px; right: 10px; width: 320px; height: 350px; background: #2E2E34; border: 1px solid #4CAF50; border-radius: 8px; box-shadow: 0 5px 20px rgba(0,0,0,0.4); z-index: 10002; flex-direction: column; overflow: hidden; }
            #gif-picker-header { padding: 8px; border-bottom: 1px solid #444; }
            #gif-search-input { width: 100%; box-sizing: border-box; background: #222; border: 1px solid #555; color: #eee; border-radius: 4px; padding: 6px 8px; outline: none; }
            #gif-results-container { flex-grow: 1; overflow-y: auto; padding: 8px; display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
            #gif-results-container img { width: 100%; height: 120px; object-fit: cover; cursor: pointer; border-radius: 4px; border: 2px solid transparent; } #gif-results-container img:hover { border-color: #4CAF50; }
        `;
        document.head.appendChild(style);

        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 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 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="Minimize Chat (Key: C)">×</button></div><div id="chat-area"><div id="chat-body"><p style="color:#888;text-align:center;">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" class="chat-action-btn">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 style="text-align: left;">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"><input type="text" id="settings-name-color-hex" maxlength="7"></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 promoModal = document.createElement('div'); promoModal.id = 'promo-modal-overlay'; promoModal.className = 'chat-modal-overlay';
        promoModal.innerHTML = `<div id="promo-modal" class="chat-modal-content"><h2>Want the Full Mod?</h2><p>Get features like Zoom, AFK Bot, Neon Line, Auto Boost, and more!</p><p>Visit <a href="https://dsc.gg/143x" target="_blank">DSC.GG/143X</a> to download the full mod today!</p><div class="modal-buttons" style="justify-content: center;"><button id="promo-okay-btn" class="modal-btn">Okay</button></div></div>`;
        document.body.appendChild(promoModal);
        const gifPickerModal = document.createElement('div'); gifPickerModal.id = 'gif-picker-modal';
        gifPickerModal.innerHTML = `<div id="gif-picker-header"><input type="text" id="gif-search-input" placeholder="Search GIPHY..."></div><div id="gif-results-container"></div>`;
        document.getElementById('chat-area').appendChild(gifPickerModal);
        const showChatButton = document.createElement('div'); showChatButton.id = 'show-chat-button'; showChatButton.title = "Show Chat (Key: C)"; showChatButton.innerHTML = '💬';
        document.body.appendChild(showChatButton);

        function toggleChatVisibility() { isChatVisible = !isChatVisible; chatContainer.style.display = isChatVisible ? 'flex' : 'none'; showChatButton.style.display = isChatVisible ? 'none' : 'block'; }
        
        function loadFirebaseAndInit(){const s1=document.createElement('script');s1.src='https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js';s1.onload=()=>{const s2=document.createElement('script');s2.src='https://www.gstatic.com/firebasejs/8.10.0/firebase-database.js';s2.onload=()=>{const s3=document.createElement('script');s3.src='https://www.gstatic.com/firebasejs/8.10.0/firebase-auth.js';s3.onload=()=>initializeChat(window.firebase);document.head.appendChild(s3);};document.head.appendChild(s2);};document.head.appendChild(s1);}
        function initializeChat(firebase){if(!firebase.apps.length)firebase.initializeApp(config.firebaseConfig);const auth=firebase.auth();const db=firebase.database();auth.signInAnonymously().then(()=>{auth.onAuthStateChanged(user=>{if(user){const ci=document.getElementById('chat-input');ci.disabled=false;ci.placeholder='Type and press Enter...';document.getElementById('chat-body').innerHTML='';let n=localStorage.getItem("slitherChatNickname");if(!n){n=prompt("Welcome! Please enter a nickname:","Player")||"Anon";localStorage.setItem("slitherChatNickname",n);}const ur=db.ref("onlineUsers/"+user.uid);ur.onDisconnect().remove();const ld={name:n,chatNameColor:localStorage.getItem("slitherChatNameColor")||"#FFD700",profileAvatar:localStorage.getItem("slitherChatAvatar")||"",profileMotto:localStorage.getItem("slitherChatMotto")||""};ur.update({...ld,uid:user.uid,lastActive:firebase.database.ServerValue.TIMESTAMP});setInterval(()=>ur.update({lastActive:Date.now()}),45000);listenForMessages(db,user.uid);listenForOnlineUsers(db,user.uid);setupEventListeners(db,user.uid);}})}).catch(err=>console.error("Firebase Sign-In Error:",err));}
        function listenForMessages(db,currentUid){const cb=document.getElementById('chat-body');db.ref("slitherChat").orderByChild("time").limitToLast(config.chatMaxMessages).on("child_added",snap=>{try{if(cb.children.length>=config.chatMaxMessages)cb.removeChild(cb.firstChild);renderChatMessage(snap.val(),cb,currentUid,true);}catch(e){console.error("Failed to render message:",e,snap.val());}});}

        function renderChatMessage(msg,container,currentUid,shouldScroll){
            if(!msg||typeof msg.text!=='string')return;
            const uc=msg.chatNameColor||'#FFD700';const idb=msg.uid==='discord_bot';
            let dn=escapeHTML(msg.name||'Anon');if(idb)dn=escapeHTML((dn.match(/^Discord\((.*)\)$/)||['','User'])[1]);
            let nh,rt='';
            if(idb){rt=` <span style="background:#7289DA;color:#fff;padding:1px 5px;border-radius:3px;font-size:0.8em;">DISCORD</span>`;nh=`<span class="chat-username" data-uid="${msg.uid}" style="color:${uc}">${dn}</span>`;}
            else if(isDev(msg.uid)){rt=` <span style="background:#E91E63;color:#fff;padding:1px 5px;border-radius:3px;font-size:0.8em;">DEV</span>`;nh=`<span class="chat-username" data-uid="${msg.uid}">${rainbowTextStyle(dn)}</span>`;}
            else if(isVip(msg.uid,msg.name)){rt=` <span style="background:#9C27B0;color:#fff;padding:1px 5px;border-radius:3px;font-size:0.8em;">VIP</span>`;nh=`<span class="chat-username" data-uid="${msg.uid}">${vipGlowStyle(dn,uc)}</span>`;}
            else{nh=`<span class="chat-username" data-uid="${msg.uid}" style="color:${uc}">${dn}</span>`;}

            let messageContent = '';
            const imageRegex = /(https?:\/\/[^\s]+\.(?:png|jpg|jpeg|gif|webp))/i;
            const linkRegex = /https?:\/\/[^\s]+/gi;
            if (imageRegex.test(msg.text)) {
                const imageUrl = msg.text.match(imageRegex)[0];
                messageContent = `<img src="${imageUrl}" class="chat-image-preview" onclick="window.open('${imageUrl}', '_blank')">`;
            } else {
                let sanitizedText = escapeHTML(msg.text);
                sanitizedText = sanitizedText.replace(linkRegex, '<a href="https://dsc.gg/143x" target="_blank">dsc.gg/143x</a>');
                sanitizedText = sanitizedText.replace(/<b>/g, '<b>').replace(/<\/b>/g, '</b>');
                messageContent = sanitizedText;
            }

            const el=document.createElement('div');el.className='chat-message';if(msg.uid===currentUid)el.style.background='rgba(76,175,80,0.1)';
            el.innerHTML=`<b>${nh}${rt}:</b> ${messageContent}`;container.appendChild(el);
            if(shouldScroll)container.scrollTop=container.scrollHeight;
        }

        function listenForOnlineUsers(db,currentUid){const oue=document.getElementById('online-users');db.ref("onlineUsers").on("value",snap=>{const users=snap.val()||{};const au=Object.values(users).filter(u=>Date.now()-(u.lastActive||0)<300000);let ulh=au.map(user=>{let dn=escapeHTML(user.name||'Anon');if(isDev(user.uid))dn=rainbowTextStyle(dn);const st=user.uid===currentUid?' (You)':'';return `<div style="padding:5px 2px;border-bottom:1px solid rgba(255,255,255,0.08);"><span style="color:lime;">●</span> <span class="chat-username" data-uid="${user.uid}" style="color:${user.chatNameColor||'#FFD700'}">${dn}</span><span style="color:#8f8;font-size:0.9em;">${st}</span></div>`;}).join('');ulh+=`<button id="get-full-mod-btn">Get Full Mod</button>`;oue.innerHTML=ulh||`<p style="text-align:center;color:#888;">No users online.</p><button id="get-full-mod-btn">Get Full Mod</button>`;});}
        async function showUserProfile(db,uid){document.querySelector('.profile-popup')?.remove();try{const us=await db.ref("onlineUsers/"+uid).once('value');if(!us.exists())return;const d=us.val();const p=document.createElement('div');p.className='profile-popup';p.innerHTML=`<button class="close-btn" onclick="this.parentElement.remove();">×</button><img class="avatar" src="${d.profileAvatar||`https://api.dicebear.com/7.x/identicon/svg?seed=${uid}`}" onerror="this.src='https://api.dicebear.com/7.x/identicon/svg?seed=${uid}'" style="border-color:${d.chatNameColor||'#FFD700'}"><h3 style="margin:0;">${d.name}</h3><p style="font-style:italic;color:#ccc;margin-top:10px;">${d.profileMotto||'No motto.'}</p>`;document.body.appendChild(p);}catch(err){console.error("Error fetching profile:",err);}}
        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";};};}
        
        async function openGifPicker(){const picker=document.getElementById('gif-picker-modal');const results=document.getElementById('gif-results-container');picker.style.display='flex';results.innerHTML='Loading...';try{const res=await fetch(`https://api.giphy.com/v1/gifs/trending?api_key=${config.giphyApiKey}&limit=20&rating=pg-13`);const json=await res.json();displayGifs(json.data);}catch(e){results.innerHTML='Could not load GIFs.';}}
        async function searchGifs(){const query=document.getElementById('gif-search-input').value.trim();const results=document.getElementById('gif-results-container');if(!query)return;results.innerHTML=`Searching...`;try{const res=await fetch(`https://api.giphy.com/v1/gifs/search?api_key=${config.giphyApiKey}&q=${encodeURIComponent(query)}&limit=30&rating=pg-13`);const json=await res.json();displayGifs(json.data);}catch(e){results.innerHTML='Search failed.';}}
        function displayGifs(data){const results=document.getElementById('gif-results-container');results.innerHTML='';if(!data||data.length===0){results.innerHTML='No results.';return;}data.forEach(gif=>{const img=document.createElement('img');img.src=gif.images.fixed_height_small.url;img.dataset.fullUrl=gif.images.original.url;img.alt=gif.title;results.appendChild(img);});}
        function sendGifToChat(db,uid,url){const name=localStorage.getItem("slitherChatNickname");const color=localStorage.getItem("slitherChatNameColor")||"#FFD700";const payload={uid,name,text:url,time:firebase.database.ServerValue.TIMESTAMP,chatNameColor:color};db.ref("slitherChat").push(payload);db.ref("discordBridge").push(payload);document.getElementById('gif-picker-modal').style.display='none';}

        function setupEventListeners(db, uid) {
            document.getElementById('chat-tab-main').addEventListener('click', () => { document.getElementById('chat-body').style.display = 'flex'; document.getElementById('online-users').style.display = 'none'; document.getElementById('chat-tab-main').style.background = 'rgba(76, 175, 80, 0.25)'; document.getElementById('chat-tab-users').style.background = 'transparent'; });
            document.getElementById('chat-tab-users').addEventListener('click', () => { document.getElementById('chat-body').style.display = 'none'; document.getElementById('online-users').style.display = 'flex'; document.getElementById('chat-tab-users').style.background = 'rgba(76, 175, 80, 0.25)'; document.getElementById('chat-tab-main').style.background = 'transparent'; });
            document.getElementById('chat-input').addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); e.stopPropagation(); const text = e.target.value.trim(); if (text) { const payload = { uid, name: localStorage.getItem("slitherChatNickname"), text, time: firebase.database.ServerValue.TIMESTAMP, chatNameColor: localStorage.getItem("slitherChatNameColor") || "#FFD700" }; db.ref("slitherChat").push(payload); db.ref("discordBridge").push(payload); 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 = document.getElementById('settings-name-color-hex').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');
            const cPicker = document.getElementById('settings-name-color'), hInput = document.getElementById('settings-name-color-hex');
            cPicker.addEventListener('input', () => hInput.value = cPicker.value); hInput.addEventListener('input', () => { if (/^#[0-9A-F]{6}$/i.test(hInput.value)) cPicker.value = hInput.value; });
            document.getElementById('settings-save-btn').addEventListener('click', () => { const newData = { name: document.getElementById('settings-nickname').value.trim().slice(0, 20) || 'Anon', chatNameColor: cPicker.value, profileAvatar: document.getElementById('settings-avatar').value.trim(), profileMotto: document.getElementById('settings-motto').value.trim() }; 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('promo-okay-btn').addEventListener('click', () => document.getElementById('promo-modal-overlay').style.display = 'none');
            document.getElementById('chat-hide-btn').addEventListener('click', toggleChatVisibility);
            showChatButton.addEventListener('click', toggleChatVisibility);
            document.getElementById('gif-btn').addEventListener('click', openGifPicker);
            document.getElementById('gif-search-input').addEventListener('keydown', (e) => { if (e.key === 'Enter') searchGifs(); });
            document.getElementById('gif-results-container').addEventListener('click', (e) => { if (e.target.tagName === 'IMG') sendGifToChat(db, uid, e.target.dataset.fullUrl); });
            document.body.addEventListener('click', e => {
                if (e.target.closest('.chat-username')) { showUserProfile(db, e.target.closest('.chat-username').dataset.uid); }
                if (e.target.id === 'get-full-mod-btn') { document.getElementById('promo-modal-overlay').style.display = 'flex'; }
                if (!document.getElementById('gif-picker-modal').contains(e.target) && e.target.id !== 'gif-btn') { document.getElementById('gif-picker-modal').style.display = 'none'; }
            });
            document.addEventListener('keydown', (e) => {
                const activeEl = document.activeElement;
                if (e.key.toLowerCase() === 'c' && activeEl.tagName !== 'INPUT' && activeEl.tagName !== 'TEXTAREA') { toggleChatVisibility(); }
            });
        }

        loadFirebaseAndInit();
    }
})();