Legend Delta agar.io

Agar.io PC addon for Delta (deltio / delt.io) and LegendMod. Discord webhook invites, Play auto-post, rich embeds, server join URLs, SNEZ broadcaster, server history & instant rejoin, cloud save/load, themes, skins, custom skin URLs, clan tags, macro & feed coordination, party play, bot reduction helpers, UI enhancements. Works with Legend, Ogario style play, Agar.io desktop, Discord, YouTube.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Legend Delta agar.io
// @namespace    Legend addon for Delta mod agar.io
// @version      1.0
// @description  Agar.io PC addon for Delta (deltio / delt.io) and LegendMod. Discord webhook invites, Play auto-post, rich embeds, server join URLs, SNEZ broadcaster, server history & instant rejoin, cloud save/load, themes, skins, custom skin URLs, clan tags, macro & feed coordination, party play, bot reduction helpers, UI enhancements. Works with Legend, Ogario style play, Agar.io desktop, Discord, YouTube.
// @author       Jimboy3100
// @icon         https://www.legendmod.ml/banners/icon48.png
// @match        https://agar.io/*
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @connect      lmsettings.snez.org
// @connect      agar.snez.org
// @connect      discord.com
// @connect      discordapp.com
// @connect      ipapi.co
// @run-at       document-start
// ==/UserScript==

const win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
win.LOG_TAG = "[LM-OVERLORD] ";
win.FALLBACK_ICON = "https://deltav4.gitlab.io/v7/assets/map-logo-old.png";
win.SNEZ_WS_URL = "wss://agar.snez.org:63051/";
win.FLAG_CSS_LIB = "https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.2.1/css/flag-icon.min.css";
// 1. Initializer (Put this near the top of your script)
win
    .LM_Session = {
    current: { nick: "", tag: "", server: "", region: "", mode: "" },
    history: JSON.parse(localStorage.getItem("LM_Server_History") || "[]")
};
win.REMOVE_CHAT_MESSAGE_AFTER_PARSE = true;
// 2. Listener (Put this inside your listeners section)
document.addEventListener('click', (e) => {
    const target = e.target.closest('.btn-layer');
    if (target && target.innerText.trim() === "Play") {
        const sipVal = document.querySelector('input[name="serverToken"]')?.value;
        const tagVal = document.querySelector('input[name="clantag"]')?.value || "";
        const nickVal = document.querySelector('input[name="nickA"]')?.value || "Guest";
        const regVal = document.querySelector('select[name="region"]')?.value || "Unknown";
        const modVal = document.querySelector('select[name="gamemode"]')?.value || ":ffa";

        // Logic: Same server only updates current data. New server pushes old current to history.
        if (win
            .LM_Session.current.server === sipVal) {
            win
                .LM_Session.current.nick = nickVal;
            win
                .LM_Session.current.tag = tagVal;
        } else {
            if (win
                .LM_Session.current.server) {
                win
                    .LM_Session.history.unshift({...win
                        .LM_Session.current});
                if (win
                    .LM_Session.history.length > 10) win
                    .LM_Session.history.pop();
            }
            win
                .LM_Session.current = { nick: nickVal, tag: tagVal, server: sipVal, region: regVal, mode: modVal, time: new Date().toLocaleString() };
        }

        localStorage.setItem("LM_Server_History", JSON.stringify(win
            .LM_Session.history));

        if (typeof LM_MASTER !== 'undefined' && LM_MASTER.discord.autoSend) {
            win
                .sendServerToDiscord();
        }
    }
}, true);

win.rejoinLastServer = function() {
    // Check if Delta app exists and get the real connected token, otherwise empty string
    const realConnectedToken = win.app && win.app.server ? win.app.server.serverToken : "";

    // Check against our stored session (Corrected structure to LM_Session.current.server)
    if (realConnectedToken !== win.LM_Session.current.server) {
        const history = win.LM_Session.history;

        if (history && history.length > 0) {
            const last = history[0]; // Get the first element of history

            console.log(win.LOG_TAG + "Restoring Session from History: " + last.server);

            // 1. Make the history item the 'current' session
            win.LM_Session.current = {
                nick: last.nick,
                tag: last.tag,
                server: last.server,
                region: last.region,
                mode: last.mode,
                time: new Date().toLocaleString()
            };

            // 2. Visually update Nickname and Tag inputs (since startSovereignJoin doesn't handle these)
            // We use the hijackUI helper function defined in your script
            if (typeof hijackUI === 'function') {
                hijackUI('input[name="nickA"]', last.nick);
                hijackUI('input[name="clantag"]', last.tag);
            }

            // 3. Connect using the Region, Mode, and Server from history
            if (typeof win.startSovereignJoin === 'function') {
                win.startSovereignJoin(last.region, last.mode, last.server);
            } else if (typeof startSovereignJoin === 'function') {
                startSovereignJoin(last.region, last.mode, last.server);
            }

            if (win.toastr) win.toastr.success("[LM] Session restored from history.");
        } else {
            if (win.toastr) win.toastr.warning("[LM] No history found to restore.");
        }
    } else {
        // Fallback: If tokens match, just rejoin the last history item normally
        const history = win.LM_Session.history;
        if (history && history.length > 0) {
            const last = history[0];
            if (typeof win.startSovereignJoin === 'function') {
                win.startSovereignJoin(last.region, last.mode, last.server);
            }
        }
    }
};
win.injectHistoryButton = function() {
    const serverInput = document.querySelector('input[name="serverToken"]');
    if (!serverInput || document.getElementById('lm-rejoin-btn')) return;

    // Direct path: input -> input-box -> column (w-8/12)
    const inputCol = serverInput.parentElement.parentElement;
    const btnCol = inputCol?.nextElementSibling; // column (w-4/12)

    if (!inputCol || !btnCol) return;

    // Force widths using style.width to stay inside the border
    inputCol.style.setProperty('width', '58%', 'important');
    btnCol.style.setProperty('width', '42%', 'important');
    btnCol.style.display = 'flex';
    btnCol.style.gap = '4px';

    const buttons = Array.from(btnCol.querySelectorAll('button'));

    const connectBtn = buttons.find(b =>
        (b.textContent || '').trim().toLowerCase() === 'connect'
    );

    if (connectBtn) {
        // replace label with icon
        connectBtn.innerHTML = '<i class="fas fa-plug"></i>';
        connectBtn.title = 'Connect';

        // keep layout tight
        connectBtn.style.flex = "1 1 0";
        connectBtn.style.minWidth = "32px";
        connectBtn.style.padding = "0";
        connectBtn.style.boxSizing = "border-box";
    }


    const rejoinBtn = document.createElement('button');
    rejoinBtn.id = 'lm-rejoin-btn';
    rejoinBtn.className = connectBtn ? connectBtn.className : "btn";
    rejoinBtn.style.flex = "0 0 36px";
    rejoinBtn.style.minWidth = "36px";
    rejoinBtn.style.padding = "0";
    rejoinBtn.style.boxSizing = "border-box";
    rejoinBtn.style.minWidth = "35px";
    rejoinBtn.style.cursor = "pointer";
    rejoinBtn.style.pointerEvents = "auto"; // Fixes "unclickable" issue
    rejoinBtn.innerHTML = '<i class="fas fa-history"></i>';
    rejoinBtn.title = "Rejoin Last Played Server";

    // Re-linking the click event properly
    rejoinBtn.onclick = function(e) {
        e.preventDefault();
        e.stopPropagation();
        win.rejoinLastServer();
    };

    btnCol.appendChild(rejoinBtn);
};
(function() {
    'use strict';

    // ==========================================================================
    // [1. GLOBAL CONSTANTS & PALETTE]
    // ==========================================================================



    const THEME = {
        cyan: "#01d9cc",
        dark: "rgba(0, 10, 18, 0.98)",
        gold: "#f1c40f",
        red: "#ff3e3e",
        discord: "#7289da",
        border: "1px solid #01d9cc"
    };

    // ==========================================================================
    // [2. THE IMMUTABLE VAULT - CAPTURES URL BEFORE DELTA CLEANS IT]
    // ==========================================================================
    const START_URL = window.location.href;
    if (START_URL.includes('sip=')) {
        sessionStorage.setItem('LM_OVERLORD_VAULT_FINAL', START_URL);
    }
    const URL_VAULT = sessionStorage.getItem('LM_OVERLORD_VAULT_FINAL') || START_URL;

    win
        .originalURL = URL_VAULT;
    try {
        Object.defineProperty(win
            , 'url', { get: () => URL_VAULT, configurable: false });
        Object.defineProperty(win
            , 'originalURL', { get: () => URL_VAULT, configurable: false });
    } catch (e) { win
        .url = URL_VAULT; }

    // ==========================================================================
    // [3. MASTER STATE]
    // ==========================================================================
    win
        .LM_MASTER = {
        socket: null,
        playerCache: [],
        lastBroadcast: "",
        searching: false,
        myCountryCode: "un",
        onlineCount: 0,
        joinInProgress: false,
        discord: {
            webhook1: localStorage.getItem("discwebhook1") || "",
            webhook2: localStorage.getItem("discwebhook2") || "",
            autoSend: localStorage.getItem("lmAutoDiscord") === "true"
        }
    };

    // ==========================================================================
    // [4. DISCORD & FLAG UTILS]
    // ==========================================================================
    function isValidWebhook(url) { return /^(https?):\/\/(discord|discordapp)\.com\/api\/webhooks\/[^\s]+$/.test(url); }

    win.sendServerToDiscord = function(customToken = null, customRegion = null, customMode = null) {
        const sip = customToken || document.querySelector('input[name="serverToken"]')?.value;
        const tag = document.querySelector('input[name="clantag"]')?.value || "";
        const nick = document.querySelector('input[name="nickA"]')?.value || "Guest";
        const region = customRegion || document.querySelector('select[name="region"]')?.value || "Unknown";
        const mode = customMode || document.querySelector('select[name="gamemode"]')?.value || ":ffa";

        const avatarIcon = document.querySelector('img.avatar')?.src || win.FALLBACK_ICON;

        const manualSkin =
            document.querySelector('input[name="skinA"]')?.value ||
            document.querySelector('input[name="skinB"]')?.value;

        const thumbnailIcon = (manualSkin && manualSkin.length > 10)
            ? manualSkin
            : avatarIcon;

        if (!sip) return;

        const lmSip = sip.includes(".") ? sip : `live-arena-${sip}.agar.io`;
        const joinLink =
            `${win.location.origin}${win.location.pathname}` +
            `?sip=${lmSip}&pass=${tag}&?r=${region}&?m=${mode}`;

        // format HH:MM (local time)
        const now = new Date();
        const time =
            now.getHours().toString().padStart(2, "0") +
            ":" +
            now.getMinutes().toString().padStart(2, "0");

        const fields = [
            { name: "🖥️", value: `[\`${sip}\`](${joinLink})`, inline: true },
            { name: "🌍", value: `\`${region}\``, inline: true },
            { name: "🎮", value: `\`${mode}\``, inline: true }
        ];

        if (tag) {
            fields.push(
                { name: "🏷️", value: `\`${tag}\``, inline: true },
                { name: "⠀", value: "⠀", inline: true }
            );
        }

        const payload = {
            embeds: [{
                author: {
                    name: `${nick} joined – ${time}`,
                    icon_url: avatarIcon
                },
                color: 5763719,
                thumbnail: {
                    url: thumbnailIcon
                },
                fields
                // ❌ timestamp REMOVED → no "Today at ..."
            }]
        };

        [LM_MASTER.discord.webhook1, LM_MASTER.discord.webhook2].forEach(wh => {
            if (isValidWebhook(wh)) {
                fetch(wh, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(payload)
                });
            }
        });
    };


    function getISO(code) {
        if (!code) return "un";
        let c = code.toLowerCase().trim();
        const map = { "turkey": "tr", "tk-turkey": "tr", "brazil": "br", "br-brazil": "br", "london": "gb", "eu-london": "gb", "atlanta": "us", "us-atlanta": "us", "russia": "ru", "ru-russia": "ru", "tokyo": "jp", "jp-tokyo": "jp", "china": "cn", "cn-china": "cn", "singapore": "sg", "sg-singapore": "sg" };
        if (map[c]) return map[c];
        if (c.length === 2) return c;
        if (c.includes('-')) return c.split('-')[0];
        return "un";
    }

    async function runGeo() {
        let nav = navigator.languages ? navigator.languages[0] : navigator.language;
        LM_MASTER.myCountryCode = (nav.includes('-') ? nav.split('-')[1] : nav).toLowerCase();
        try {
            const res = await fetch('https://ipapi.co/json/');
            const data = await res.json();
            if (data && data.country_code) LM_MASTER.myCountryCode = data.country_code.toLowerCase();
        } catch (err) { }
    }

    // ==========================================================================
    // [5. SOVEREIGN JOIN PIPELINE]
    // ==========================================================================
    function hijackUI(selector, value) {
        const el = document.querySelector(selector);
        if (!el) return false;
        const isSelect = el instanceof HTMLSelectElement;
        const setter = Object.getOwnPropertyDescriptor(isSelect ? win
            .HTMLSelectElement.prototype : win
            .HTMLInputElement.prototype, "value").set;
        setter.call(el, value);
        el.dispatchEvent(new Event('input', { bubbles: true }));
        el.dispatchEvent(new Event('change', { bubbles: true }));
        if (isSelect) { for (let i = 0; i < el.options.length; i++) { if (el.options[i].value === value) { el.selectedIndex = i; break; } } }
        return true;
    }

    win
        .startSovereignJoin = async function(region, mode, token) {
        if (LM_MASTER.joinInProgress) return;
        LM_MASTER.joinInProgress = true;
        hijackUI('select[name="region"]', region);
        hijackUI('select[name="gamemode"]', mode);
        await new Promise(r => setTimeout(r, 1200));
        hijackUI('input[name="serverToken"]', token);
        await new Promise(r => setTimeout(r, 200));
        hijackUI('input[name="serverToken"]', token);
        setTimeout(() => {
            const btn = document.querySelector('input[name="serverToken"]')?.closest('.flex.items-stretch')?.querySelector('button') ||
                Array.from(document.querySelectorAll('button')).find(b => b.innerText.includes('Connect'));
            if (btn) btn.click();
            LM_MASTER.joinInProgress = false;
        }, 500);
    }

    // ==========================================================================
    // [6. THE OVERLORD COMPACT HUD]
    // ==========================================================================
    function injectHUD() {
        if (document.getElementById('lm-over-root')) return;
        const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = win.FLAG_CSS_LIB; document.head.appendChild(link);

        const css = `
            <style>
                #lm-over-root { font-family: 'Ubuntu', sans-serif; pointer-events: none; font-size: 13px; }
                .lm-win { background: ${THEME.dark}; border: 1px solid ${THEME.border}; color: #fff; pointer-events: auto; border-radius: 8px; position: fixed; z-index: 10020; box-shadow: 0 0 40px #000; overflow: hidden; width: 600px; }
                .lm-bar { background: rgba(1, 217, 204, 0.1); padding: 10px; cursor: move; text-align: center; font-weight: bold; border-bottom: 1px solid #222; position: relative; }
                .lm-stats { background: #000; font-size: 10px; color: ${THEME.cyan}; padding: 3px; text-align: center; }
                .lm-main { padding: 15px; }
                .lm-search-box { display: flex; gap: 8px; margin-bottom: 12px; }
                .lm-in-pro { flex-grow: 1; background: #000; border: 1px solid #444; color: #fff; padding: 8px; text-align: center; font-size: 14px; border-radius: 4px; outline: none; }
                .lm-in-pro:focus { border-color: ${THEME.cyan}; }
                .lm-btn-s { background: ${THEME.cyan}; color: #000; border: none; font-weight: bold; border-radius: 4px; padding: 0 15px; cursor: pointer; height: 38px; }
                .lm-log { height: 320px; overflow-y: scroll; background: rgba(0,0,0,0.3); border: 1px solid #111; border-radius: 4px; }
                .lm-row { padding: 8px 12px; border-bottom: 1px solid #222; cursor: pointer; display: flex; justify-content: space-between; align-items: center; }
                .lm-row:hover { background: rgba(1, 217, 204, 0.1); }
                .lm-ds-icon { color: ${THEME.discord}; font-size: 18px; cursor: pointer; margin-left: 8px; }

                .lm-footer { padding: 12px 15px; border-top: 1px solid #222; background: #050505; display: flex; flex-direction: column; gap: 8px; }
                .lm-wh-row { display: flex; align-items: center; gap: 10px; width: 100%; }
                .lm-wh-in { flex-grow: 1; background: #000; border: 1px solid #333; color: ${THEME.discord}; padding: 6px; font-size: 11px; border-radius: 3px; outline: none; }
                .lm-wh-in:focus { border-color: ${THEME.discord}; }
                .lm-help-btn { color: ${THEME.gold}; font-weight: bold; font-size: 18px; cursor: pointer; user-select: none; width: 20px; text-align: center; }

                .lm-x { position: absolute; right: 10px; top: 8px; cursor: pointer; color: ${THEME.red}; font-size: 20px; }
.lm-h-panel { position: absolute; top: 40px; left: 10px; right: 10px; bottom: 10px; background: ${THEME.dark}; border: 1px solid ${THEME.gold}; padding: 15px; z-index: 10025; display: none; overflow-y: auto; text-shadow:none; font-size: 13px; }
</style>
        `;

        const html = `
            <div id="lm-over-root">
                <div id="lm-shade" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:10019;"></div>
                <div id="lm-main-win" class="lm-win" style="display:none; top: 15%; left: 50%; transform: translateX(-50%);">
                    <div class="lm-bar" id="lm-drag">
                        <span class="lm-x" id="lm-quit">×</span>
                        Search Players Across the Network
                    </div>
                    <div class="lm-stats" id="lm-st-online">Network Online...</div>
                    <div class="lm-main">
                        <div class="lm-search-box">
                            <input id="lm-search-in" class="lm-in-pro" placeholder="Player, Tag, or Token...">
                            <button id="lm-search-btn" class="lm-btn-s">SEARCH</button>
                        </div>
                        <div id="lm-log" class="lm-log">
                            <div style="text-align:center; color:#333; padding-top:140px;">Network active. Start typing.</div>
                        </div>
                        <div id="lm-h-panel" class="lm-help-panel">
                            <h4 style="color:${THEME.gold}; margin-top:0;">Discord Webhook Guide</h4>
                            <p><b>1.</b> Open Discord, go to <b>Server Settings</b>.</p>
                            <p><b>2.</b> Click <b>Integrations</b> then <b>Webhooks</b>.</p>
                            <p><b>3.</b> Click <b>New Webhook</b> and <b>Copy Webhook URL</b>.</p>
                            <p><b>4.</b> Paste the URL into the boxes below.</p>
                            <p><b>5.</b> Use the Discord icon <i class="fa fa-discord"></i> next to players to share them!</p>
                            <button id="lm-h-close" class="lm-btn-s" style="width:100%;">Got it!</button>
                        </div>
                    </div>
                    <div class="lm-footer">
                        <div class="lm-wh-row">
                            <span class="lm-help-btn" id="lm-help">?</span>
                            <input id="lm-wh1" class="lm-wh-in" placeholder="Discord Webhook URL 1" value="${LM_MASTER.discord.webhook1}">
                        </div>
                        <input id="lm-wh2" class="lm-wh-in" style="margin-left: 30px; width: calc(100% - 30px);" placeholder="Discord Webhook URL 2" value="${LM_MASTER.discord.webhook2}">
                        <label style="font-size:10px;color:#9aa0a6;margin-left:30px;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;cursor:pointer;"><input type="checkbox" id="lm-auto-d" ${LM_MASTER.discord.autoSend ? 'checked' : ''} style="margin:0;width:13px;height:13px;accent-color:#5865F2;"> Auto-post to Discord on "Play"</label>
                    </div>
                </div>
            </div>
        `;

        document.body.insertAdjacentHTML('beforeend', css + html);

        // Bind events
        document.getElementById('lm-search-in').oninput = () => {
            LM_MASTER.searching = true; refreshHUD();
            if (LM_MASTER.socket?.readyState === 1) LM_MASTER.socket.send(JSON.stringify({ type: "get_players" }));
        };
        document.getElementById('lm-help').onclick = () => document.getElementById('lm-h-panel').style.display = 'block';
        document.getElementById('lm-h-close').onclick = () => document.getElementById('lm-h-panel').style.display = 'none';
        document.getElementById('lm-quit').onclick = hideUI;

        ['lm-wh1', 'lm-wh2'].forEach((id, i) => {
            document.getElementById(id).onblur = (e) => {
                localStorage.setItem(`discwebhook${i+1}`, e.target.value);
                LM_MASTER.discord[`webhook${i+1}`] = e.target.value;
            };
        });
        document.getElementById('lm-auto-d').onchange = (e) => {
            localStorage.setItem("lmAutoDiscord", e.target.checked);
            LM_MASTER.discord.autoSend = e.target.checked;
        };

        setupDragging(document.getElementById('lm-main-win'), document.getElementById('lm-drag'));
    }

    function refreshHUD() {
        const query = document.getElementById('lm-search-in').value.toLowerCase();
        const log = document.getElementById('lm-log');
        if (!query) { log.innerHTML = '<div style="text-align:center; color:#333; padding-top:140px;">Network active. Start typing.</div>'; return; }
        log.innerHTML = "";
        let found = 0;

        LM_MASTER.playerCache.forEach(p => {
            if ((p.nickname || "").toLowerCase().includes(query) || (p.tag || "").toLowerCase().includes(query) || (p.server || "").includes(query)) {
                found++;
                const srv = p.server || "";
                const token = srv.split('live-arena-').pop().split('.agar.io')[0];
                const region = (new RegExp('[\\?&]\\??r=([^&#]*)').exec(srv)?.[1]) || "Unknown";
                const mode = (new RegExp('[\\?&]\\??m=([^&#]*)').exec(srv)?.[1]) || ":ffa";
                const iso = getISO(p.country || p.c || p.loc || "un");

                const row = document.createElement('div');
                row.className = 'lm-row';
                row.innerHTML = `
                    <div style="display:flex; align-items:center;">
                        <span class="flag-icon flag-icon-${iso}" style="font-size:20px; margin-right:12px;"></span>
                        <div><b>${p.nickname}</b><br><small style="color:#666;">${region} • ${mode}</small></div>
                    </div>
                    <div style="text-align:right; display:flex; align-items:center;">
                        <div><b style="color:${THEME.gold}; font-family:monospace; font-size:15px;">${token}</b><br><small style="color:#444;">Lvl ${p.agarioLEVEL || '?'}</small></div>
                        <i class="fa fa-discord lm-ds-icon"></i>
                    </div>
                `;
                row.querySelector('.lm-ds-icon').onclick = (e) => { e.stopPropagation(); win
                    .sendServerToDiscord(token, region, mode); };
                row.onclick = () => { hideUI(); startSovereignJoin(region, mode, token); };
                log.appendChild(row);
            }
        });
    }

    function hideUI() { document.getElementById('lm-shade').style.display = 'none'; document.getElementById('lm-main-win').style.display = 'none'; LM_MASTER.searching = false; }
    function setupDragging(el, h) { let x1=0,y1=0,x2=0,y2=0; h.onmousedown=(e)=>{ e.preventDefault(); x2=e.clientX; y2=e.clientY; document.onmouseup=()=>{document.onmouseup=null;document.onmousemove=null;}; document.onmousemove=(e)=>{e.preventDefault(); x1=x2-e.clientX; y1=y2-e.clientY; x2=e.clientX; y2=e.clientY; el.style.top=(el.offsetTop-y1)+"px"; el.style.left=(el.offsetLeft-x1)+"px"; el.style.transform="none";};}; }

    // ==========================================================================
    // [7. NETWORK & BROADCASTER]
    // ==========================================================================
    function initNetwork() {
        if (LM_MASTER.socket?.readyState === 1) return;
        LM_MASTER.socket = new WebSocket(win.SNEZ_WS_URL);
        LM_MASTER.socket.onopen = () => {
            setInterval(() => {
                const sip = document.querySelector('input[name="serverToken"]')?.value;
                if (!sip) return;
                const payload = { type: "update_details", data: {
                        nickname: (document.querySelector('input[name="nickA"]')?.value || "Unnamed") + " (DM)",
                        server: `live-arena-${sip}.agar.io&?r=${document.querySelector('select[name="region"]')?.value}&?m=${document.querySelector('select[name="gamemode"]')?.value}`,
                        tag: document.querySelector('input[name="clantag"]')?.value || "",
                        agarioLEVEL: win
                            .agarioLEVEL || 0, country: LM_MASTER.myCountryCode.toUpperCase()
                    }};
                LM_MASTER.socket.send(JSON.stringify(payload));
            }, 10000);
            LM_MASTER.socket.send(JSON.stringify({ type: "get_players" }));
        };
        LM_MASTER.socket.onmessage = (msg) => {
            const data = JSON.parse(msg.data);
            if (data.type === "players_list") {
                LM_MASTER.playerCache = JSON.parse(data.data);
                const countLabel = document.getElementById('lm-st-online');
                if (countLabel) countLabel.innerText = `Network Online: ${LM_MASTER.playerCache.length} players`;
                if (LM_MASTER.searching) refreshHUD();
            }
        };
        LM_MASTER.socket.onclose = () => setTimeout(initNetwork, 5000);
    }

    // ==========================================================================
    // [8. STARTUP & ADRES SYNC]
    // ==========================================================================
    win
        .syncAdres = function() {
        const t = document.querySelector('input[name="serverToken"]')?.value;
        const g = document.querySelector('input[name="clantag"]')?.value;
        const r = document.querySelector('select[name="region"]')?.value;
        const m = document.querySelector('select[name="gamemode"]')?.value;
        if (!t || t.length < 3 || LM_MASTER.joinInProgress) return;
        const u = win
            .location.origin + win
            .location.pathname + "?sip=live-arena-" + t + ".agar.io" + (g?"&pass="+g:"") + (r?"&?r="+r:"") + (m?"&?m="+m:"");
        if (win
            .location.href !== u) win
            .history.replaceState(null, "", u);
    };

    const boot = setInterval(() => {

        if (document.querySelector('select[name="region"]') && document.querySelector('input[name="serverToken"]')) {
            clearInterval(boot);
            injectHUD();
            win
                .injectHistoryButton();
            runGeo().then(() => {
                setTimeout(() => {
                    const sip = (new RegExp('[\\?&]\\??sip=([^&#]*)').exec(URL_VAULT)?.[1]) || "";
                    const reg = (new RegExp('[\\?&]\\??r=([^&#]*)').exec(URL_VAULT)?.[1]) || "";
                    const mod = (new RegExp('[\\?&]\\??m=([^&#]*)').exec(URL_VAULT)?.[1]) || "";
                    const pss = (new RegExp('[\\?&]\\??pass=([^&#]*)').exec(URL_VAULT)?.[1]) || "";
                    if (sip && reg && mod) startSovereignJoin(reg, mod, sip.replace("live-arena-", "").replace(".agar.io", ""));
                    if (pss) hijackUI('input[name="clantag"]', pss);
                    initNetwork();

                    // --- LISTENERS ---
                    document.addEventListener('keydown', (e) => {
                        if (e.keyCode === 8 && !['INPUT','TEXTAREA'].includes(document.activeElement.tagName)) {
                            e.preventDefault(); document.getElementById('lm-shade').style.display = 'block'; document.getElementById('lm-main-win').style.display = 'block'; document.getElementById('lm-search-in').focus();
                        }
                    });

                    // THE PLAY BUTTON TRIGGER
                    document.addEventListener('mousedown', (e) => {
                        const playBtn = e.target.closest('.btn-layer');
                        if (playBtn && playBtn.innerText.trim() === "Play") {

                            // Helper to find the input that actually has text (handles Delta hidden inputs)
                            const getVal = (name) => {
                                const inputs = Array.from(document.querySelectorAll(`input[name="${name}"]`));
                                const found = inputs.find(i => i.value.length > 0);
                                return found ? found.value : (document.querySelector(`input[name="${name}"]`)?.value || "");
                            };

                            let s = getVal("serverToken");
                            if (!s) s = document.querySelector('.list-style .active')?.innerText.split(' ').pop();
                            if (!s) return;

                            const n = getVal("nickA") || "Guest";
                            const t = getVal("clantag") || "";
                            const r = document.querySelector('select[name="region"]')?.value || "Unknown";
                            const m = document.querySelector('select[name="gamemode"]')?.value || ":ffa";

                            const session = win.LM_Session;

                            // Same server: just update identity. New server: push to history.
                            if (session.current.server === s) {
                                session.current.nick = n;
                                session.current.tag = t;
                            } else {
                                if (session.current.server && session.current.server !== "") {
                                    session.history.unshift({...session.current});
                                    if (session.history.length > 10) session.history.pop();
                                }
                                session.current = { nick: n, tag: t, server: s, region: r, mode: m, time: new Date().toLocaleString() };
                            }

                            localStorage.setItem("LM_Server_History", JSON.stringify(session.history));
                            console.log(win.LOG_TAG + "Session Object Updated.");

                            if (LM_MASTER.discord.autoSend) {
                                console.log(win.LOG_TAG + "Triggering Discord...");
                                win
                                    .sendServerToDiscord();
                            }
                        }
                    }, true);

                    document.addEventListener('change', (e) => { if (['region', 'gamemode', 'clantag', 'serverToken'].includes(e.target.name)) setTimeout(win
                        .syncAdres, 100); }, true);
                    setInterval(win
                        .syncAdres, 3500);
                }, 1200);
            });

        }
    }, 500);
})();
// ==========================================================================
// [SNEZ CLOUD] Delta full bundle Export/Import + UI buttons
// Upload/Download must talk to SNEZ server (NO file pickers)
// Endpoint: https://lmsettings.snez.org/
// Auth headers: username=<uid>, password=LMSettings
// ==========================================================================

(function () {
    'use strict';

    // IMPORTANT: use the same unsafeWindow-backed reference as the rest of your script
    const win = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window;

    const ENDPOINT = "https://lmsettings.snez.org/";
    const PASS = "LMSettings";

    // -------------------------
    // STRICT helper to verify login and get an ID
    // (your earlier logic: app.accountManager.tokens.all keys)
    // -------------------------
    function checkLoginAndGetID() {
        try {
            const all = win?.app?.accountManager?.tokens?.all;
            if (all && typeof all === "object") {
                const keys = Object.keys(all);
                if (keys.length > 0 && keys[0]) return keys[0] + "DM";
            }
        } catch (e) {
            console.error((win.LOG_TAG || "[LM] ") + "Token check error", e);
        }

        if (win.toastr) win.toastr.error("<b>[LM]:</b> Login Required!<br>Please login with Google/Facebook first.");
        return null;
    }
    function replaceButtonWithUidTextarea(btn) {
        const uid = checkLoginAndGetID();
        if (!uid) return;

        const ta = document.createElement("textarea");
        ta.id = "lm-uid-textarea";
        ta.value = uid;
        ta.readOnly = true;
        ta.spellcheck = false;

        // match your injected button sizing
        ta.style.width = "100%";
        ta.style.marginTop = "6px";
        ta.style.height = "28px";
        ta.style.padding = "4px 8px";
        ta.style.resize = "none";
        ta.style.borderRadius = "6px";
        ta.style.border = "1px solid rgba(255,255,255,.25)";
        ta.style.background = "rgba(0,0,0,.25)";
        ta.style.color = "#fff";
        ta.style.outline = "none";

        btn.replaceWith(ta);

        setTimeout(() => {
            ta.focus();
            ta.select();
        }, 0);
    }

    // -------------------------
    // Robust "wait for Delta API"
    // -------------------------
    function waitForDeltaApi(timeoutMs = 20000) {
        const start = Date.now();
        return new Promise((resolve, reject) => {
            const t = setInterval(() => {
                const ok =
                    !!win.keyMaster && typeof win.keyMaster.export === "function" && typeof win.keyMaster.import === "function" &&
                    !!win.profiles  && typeof win.profiles.export  === "function" && typeof win.profiles.import  === "function" &&
                    !!win.settings  && typeof win.settings.export  === "function" && typeof win.settings.import  === "function" &&
                    !!win.theme     && typeof win.theme.export     === "function" && typeof win.theme.import     === "function";

                if (ok) { clearInterval(t); resolve(true); return; }
                if (Date.now() - start > timeoutMs) { clearInterval(t); reject(new Error("Delta API not ready: keyMaster/profiles/settings/theme")); }
            }, 120);
        });
    }

    // -------------------------
    // Build / Apply bundle
    // -------------------------
    function buildDeltaBundle() {
        return {
            version: 1,
            deltaHotkeys: win.keyMaster.export(),
            ogarioPlayerProfiles: win.profiles.export(),
            settings: win.settings.export(),
            ogarioThemeSettings: win.theme.export(),
        };
    }

    function applyDeltaBundle(bundle) {
        if (!bundle || typeof bundle !== "object") throw new Error("Invalid bundle object");
        if (!bundle.deltaHotkeys || !bundle.settings || !bundle.ogarioThemeSettings) {
            throw new Error("Bundle missing required keys: deltaHotkeys/settings/ogarioThemeSettings");
        }

        win.keyMaster.import(bundle.deltaHotkeys);
        win.profiles.import(bundle.ogarioPlayerProfiles || []);
        win.settings.import(bundle.settings);
        win.theme.import(bundle.ogarioThemeSettings);

        // Do NOT invent methods. Best-effort: click APPLY/SAVE/OK if Delta UI provides one.
        try {
            const btns = Array.from(document.querySelectorAll("button"));
            const hit = btns.find(b => {
                const t = (b.innerText || "").trim().toUpperCase();
                return t === "APPLY" || t === "SAVE" || t === "OK";
            });
            if (hit) { try { hit.click(); } catch (_) {} }
        } catch (_) {}
        setTimeout(() => { clickDeltaSaveWithRetry().catch(()=>{}); }, 250);
    }

    // -------------------------
    // SNEZ requests
    // -------------------------
    function snezPost(uid, payloadObj) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "POST",
                url: ENDPOINT,
                headers: { username: uid, password: PASS },
                // Keep escape/unescape since your server already uses it
                data: escape(JSON.stringify(payloadObj)),
                onload: (res) => resolve(res),
                onerror: (err) => reject(err),
            });
        });
    }

    function snezGet(uid) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: ENDPOINT,
                headers: { username: uid, password: PASS },
                onload: (res) => resolve(res),
                onerror: (err) => reject(err),
            });
        });
    }

    // -------------------------
    // Exposed functions (your buttons call these)
    // -------------------------
    win.SNEZDeltaUpload = async function () {
        const uid = checkLoginAndGetID();
        if (!uid) return;

        try {
            await waitForDeltaApi();

            const bundle = buildDeltaBundle();

            if (win.toastr) win.toastr.info("<b>[LM]:</b> Uploading full Delta bundle...");
            const res = await snezPost(uid, bundle);

            if (res && res.status === 200) {
                if (win.toastr) win.toastr.success(`<b>[LM]:</b> Bundle saved to cloud!<br>ID: <span style="color:yellow">${uid}</span>`);
                console.log((win.LOG_TAG || "[LM] ") + "Cloud upload OK", uid);
            } else {
                if (win.toastr) win.toastr.error("<b>[LM]:</b> Cloud upload failed (server error).");
                console.error((win.LOG_TAG || "[LM] ") + "Cloud upload failed", res);
            }
        } catch (e) {
            if (win.toastr) win.toastr.error(`<b>[LM]:</b> Upload failed: ${String(e && e.message ? e.message : e)}`);
            console.error((win.LOG_TAG || "[LM] ") + "Upload failed", e);
        }
    };

    win.SNEZDeltaDownload = async function () {
        const uid = checkLoginAndGetID();
        if (!uid) return;

        try {
            await waitForDeltaApi();

            if (win.toastr) win.toastr.info("<b>[LM]:</b> Downloading full Delta bundle...");
            const res = await snezGet(uid);

            if (!res || res.status !== 200 || !res.responseText) {
                if (win.toastr) win.toastr.warning("<b>[LM]:</b> No cloud data found for this ID.");
                console.warn((win.LOG_TAG || "[LM] ") + "Cloud download: empty", res);
                return;
            }

            const raw = unescape(res.responseText);
            const bundle = JSON.parse(raw);

            applyDeltaBundle(bundle);

            if (win.toastr) win.toastr.success("<b>[LM]:</b> Cloud bundle loaded! Save theme/profile/hotkeys... you like to from their own menu tab");
            console.log((win.LOG_TAG || "[LM] ") + "Cloud download+import OK", uid);
        } catch (e) {
            if (win.toastr) win.toastr.error("<b>[LM]:</b> Corrupt cloud data or import failed.");
            console.error((win.LOG_TAG || "[LM] ") + "Cloud data parse/import failed", e);
        }
    };

    // ========================================================================
    // UI injection: add UPLOAD under EXPORT, DOWNLOAD under IMPORT
    // ========================================================================
    function injectSettingsButtons() {
        try {
            const LOG = (win.LOG_TAG || "[LM] ");

            // ------------------------------------------------------------
            // Helper: find & "technical click" the Delta Save button
            // ------------------------------------------------------------
            function findSaveButton(root = document) {
                // Fast: icon-based
                const iconBtn = root.querySelector(".btn-icon.fas.fa-save")?.closest("button");
                if (iconBtn) return iconBtn;

                // Text-based fallback
                const buttons = Array.from(root.querySelectorAll("button"));
                return buttons.find(b => {
                    const info = b.querySelector(".btn-info");
                    if (info && (info.textContent || "").trim().toLowerCase() === "save") return true;
                    const t = (b.innerText || "").trim().toLowerCase();
                    return t === "save";
                }) || null;
            }

            function technicalClick(el) {
                if (!el) return false;

                // Ensure interactable
                try { el.style.pointerEvents = "auto"; } catch (_) {}

                const opts = { bubbles: true, cancelable: true, composed: true };

                // Pointer events (some UIs prefer this)
                try {
                    el.dispatchEvent(new PointerEvent("pointerdown", { ...opts, pointerId: 1, pointerType: "mouse", isPrimary: true }));
                    el.dispatchEvent(new PointerEvent("pointerup",   { ...opts, pointerId: 1, pointerType: "mouse", isPrimary: true }));
                } catch (_) {}

                // Mouse events (React-style delegation)
                el.dispatchEvent(new MouseEvent("mousedown", opts));
                el.dispatchEvent(new MouseEvent("mouseup", opts));
                el.dispatchEvent(new MouseEvent("click", opts));

                return true;
            }

            async function clickSaveAfterDownload(retries = 10, gapMs = 250) {
                for (let i = 0; i < retries; i++) {
                    const saveBtn = findSaveButton(document);
                    if (saveBtn) {
                        const ok = technicalClick(saveBtn);

                        // Extra fallback: submit containing form if they wired it that way
                        try {
                            const form = saveBtn.closest("form");
                            if (form && typeof form.requestSubmit === "function") form.requestSubmit();
                        } catch (_) {}

                        console.log(LOG + "Triggered Save after cloud download.", { ok, attempt: i + 1 });
                        return true;
                    }
                    await new Promise(r => setTimeout(r, gapMs));
                }
                console.warn(LOG + "Save button not found to persist after cloud download.");
                return false;
            }

            // ------------------------------------------------------------
            // Locate the Export/Import settings form + existing buttons
            // ------------------------------------------------------------
            const forms = Array.from(document.querySelectorAll("form"));
            const settingsForm = forms.find(f => (f.textContent || "").includes("Export / import settings"));
            if (!settingsForm) return;
// -------------------------
// Extras row: 50% mobile devtools (left) + 50% Themes website (right)
// -------------------------
            (function addThemesButtonNextToMobileDevtools() {
                const THEMES_URL = "https://www.legendmod.ml/themes/";

                // Find the "Extras" header inside THIS settingsForm
                const extrasHeader = Array.from(settingsForm.querySelectorAll("div"))
                    .find(d => (d.textContent || "").trim() === "Extras");
                if (!extrasHeader) return;

                // The row is usually the next sibling: <div class="row"><div class="col-auto">...</div></div>
                const extrasRow = extrasHeader.nextElementSibling;
                if (!extrasRow || !extrasRow.classList.contains("row")) return;

                // Make row 2 columns
                extrasRow.style.display = "flex";
                extrasRow.style.gap = "6px";
                extrasRow.style.alignItems = "center";

                const leftCol = extrasRow.querySelector(":scope > .col-auto");
                if (!leftCol) return;

                // Left col = 50%
                leftCol.style.flex = "1 1 50%";
                leftCol.style.maxWidth = "50%";

                const mobileBtn = leftCol.querySelector("button");
                if (mobileBtn) mobileBtn.style.width = "100%";

                // Right col + button only once
                if (extrasRow.querySelector("#lm-themes-site-btn")) return;

                const rightCol = document.createElement("div");
                rightCol.className = "col-auto";
                rightCol.style.flex = "1 1 50%";
                rightCol.style.maxWidth = "50%";

                const btn = document.createElement("button");
                btn.id = "lm-themes-site-btn";
                btn.type = "button";
                btn.className = mobileBtn ? mobileBtn.className : "btn";
                btn.style.width = "100%";

                btn.innerHTML = `
    <div class="btn-layer px-3" style="justify-content: center;">
        <div class="btn-logo">
            <div class="btn-icon fas fa-palette"></div>
        </div>
        <div class="btn-info">Themes website</div>
    </div>
`;


                btn.addEventListener("click", (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    window.open(THEMES_URL, "_blank", "noreferrer");
                });

                rightCol.appendChild(btn);
                extrasRow.appendChild(rightCol);
            })();

            const buttons = Array.from(settingsForm.querySelectorAll("button"));
            const exportBtn = buttons.find(b => (b.innerText || "").trim() === "EXPORT");
            const importBtn = buttons.find(b => (b.innerText || "").trim() === "IMPORT");
            if (!exportBtn || !importBtn) return;

            const exportCol = exportBtn.closest('div[class*="w-1/3"]') || exportBtn.parentElement;
            const importCol = importBtn.closest('div[class*="w-1/3"]') || importBtn.parentElement;
            if (!exportCol || !importCol) return;

            const alreadyUpload = exportCol.querySelector("#lm-cloud-upload-btn");
            const alreadyDownload = importCol.querySelector("#lm-cloud-download-btn");
            if (alreadyUpload && alreadyDownload) return;

            const makeBtn = (id, iconClass, text, onClick) => {
                const b = document.createElement("button");
                b.id = id;
                b.className = exportBtn.className;

                b.style.width = "100%";
                b.style.marginTop = "6px";
                b.style.height = "28px";
                b.style.padding = "0 10px";
                b.style.opacity = "0.95";
                b.type = "button";

                b.innerHTML = `
                <div class="btn-layer">
                    <div class="btn-logo">
                        <div class="btn-icon fas ${iconClass}"></div>
                    </div>
                    <div class="btn-info">${text}</div>
                </div>
            `;

                b.addEventListener("click", (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    Promise.resolve()
                        .then(onClick)
                        .catch(err => console.error(LOG + "Cloud button error:", err));
                });

                return b;
            };

            if (!alreadyUpload) {
                exportCol.appendChild(
                    makeBtn("lm-cloud-upload-btn", "fa-cloud-upload-alt", "UPLOAD", () => win.SNEZDeltaUpload())
                );
            }

            if (!alreadyDownload) {
                importCol.appendChild(
                    makeBtn("lm-cloud-download-btn", "fa-cloud-download-alt", "DOWNLOAD", async () => {
                        await win.SNEZDeltaDownload();      // download + import
                        await clickSaveAfterDownload();     // now persist by clicking Save
                    })
                );
            }
// REVEAL UID should be under RESET (3rd column), not under IMPORT
            if (!document.getElementById("lm-reveal-uid-btn") && !document.getElementById("lm-uid-textarea")) {
                const resetBtn = buttons.find(b => (b.innerText || "").trim() === "RESET");
                const resetCol = resetBtn?.closest('div[class*="w-1/3"]') || resetBtn?.parentElement;
                if (resetCol) {
                    resetCol.appendChild(
                        makeBtn("lm-reveal-uid-btn", "fa-id-badge", "REVEAL UID", () => {
                            const b = document.getElementById("lm-reveal-uid-btn");
                            if (b) replaceButtonWithUidTextarea(b);
                        })
                    );
                }
            }

            const resetBtn = buttons.find(b => (b.innerText || "").trim() === "RESET");
            const resetCol = resetBtn?.closest('div[class*="w-1/3"]') || resetBtn?.parentElement;
            console.log(LOG + "Cloud buttons injected under EXPORT/IMPORT.");
        } catch (e) {
            console.error((win.LOG_TAG || "[LM] ") + "injectSettingsButtons failed:", e);
        }


    }

    const settingsObserver = new MutationObserver((mutations) => {
        for (const m of mutations) {
            if (m.addedNodes && m.addedNodes.length > 0) { injectSettingsButtons(); break; }
        }
    });
// -------------------------
// Force-click Delta "Save" (the floppy icon) to persist imported settings
// -------------------------
    function findDeltaSaveButton() {
        // 1) Icon-based (best, matches your screenshot DOM)
        const byIcon = document.querySelector('.btn-icon.fas.fa-save')?.closest('button');
        if (byIcon) return byIcon;

        // 2) Text-based fallback: btn-info "Save"
        const buttons = Array.from(document.querySelectorAll('button'));
        return buttons.find(b => {
            const info = b.querySelector('.btn-info');
            if (info && (info.textContent || "").trim().toLowerCase() === "save") return true;
            const t = (b.innerText || "").trim().toLowerCase();
            return t === "save";
        }) || null;
    }

    function technicalClick(el) {
        if (!el) return false;

        // Make sure it can receive input (some Delta elements use pointer-events tricks)
        try { el.style.pointerEvents = "auto"; } catch (_) {}

        const opts = { bubbles: true, cancelable: true, composed: true };

        // Pointer events (some frameworks listen here)
        try {
            el.dispatchEvent(new PointerEvent("pointerdown", { ...opts, pointerId: 1, pointerType: "mouse", isPrimary: true }));
            el.dispatchEvent(new PointerEvent("pointerup",   { ...opts, pointerId: 1, pointerType: "mouse", isPrimary: true }));
        } catch (_) {}

        // Mouse events (React-style delegation)
        el.dispatchEvent(new MouseEvent("mousedown", opts));
        el.dispatchEvent(new MouseEvent("mouseup", opts));
        el.dispatchEvent(new MouseEvent("click", opts));

        return true;
    }

    async function clickDeltaSaveWithRetry(retries = 12, delayMs = 250) {
        for (let i = 0; i < retries; i++) {
            const saveBtn = findDeltaSaveButton();
            if (saveBtn) {
                technicalClick(saveBtn);

                // extra: if Save is wired via form submit, request it too (safe/no-op if not)
                try {
                    const form = saveBtn.closest("form");
                    if (form && typeof form.requestSubmit === "function") form.requestSubmit();
                } catch (_) {}

                return true;
            }
            await new Promise(r => setTimeout(r, delayMs));
        }
        return false;
    }
    settingsObserver.observe(document.body, { childList: true, subtree: true });
    setInterval(injectSettingsButtons, 2500);
    setTimeout(() => { clickDeltaSaveWithRetry().catch(()=>{}); }, 250);

})();

/* ============================================================================
   [LM] Delta Chat Commands (IIFE)
   - Delta chat DOM: .chat-messages-container ul.chatmessages li.message
   - Optional: also parse toastr.* content
   - Commands: [url], [tag], [yut], [img], [discord], [skype]
   - [yut] => embedded YouTube iframe (no Play button)
   - Close (✕) => removes the toast itself
   - FIXES:
     1) No double toasts (Delta re-hydrates same <li> multiple times)
     2) Works on characterData (Text node) mutations
     3) Dedup between chat parsing and toastr-hook
     4) Prevent multiple observers if IIFE runs twice
   ============================================================================ */
(function () {
    'use strict';

    // IMPORTANT: Delta addon uses unsafeWindow in many places; keep compatibility
    const win = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window;

    const LOG = (win.LOG_TAG || "[LM] ") + "ChatCmd: ";

    // Toggle: remove the original chat line after we handle a command
    // win.REMOVE_CHAT_MESSAGE_AFTER_PARSE = true;

    const TAGS = ["url","tag","yut","img","discord","skype"];
    const CMD_OPEN_RE  = new RegExp("\\[(" + TAGS.join("|") + ")\\]", "i");
    const CMD_CLOSE_RE = new RegExp("\\[\\/(" + TAGS.join("|") + ")\\]", "i");

    // --------- cross-source dedup (chat + toastr hook) ----------
    const __LM_TOAST_DEDUP__ = new Map();
    function lmToastDedupHit(key, ttlMs = 1500) {
        const now = Date.now();
        const exp = __LM_TOAST_DEDUP__.get(key) || 0;
        if (exp > now) return true;
        __LM_TOAST_DEDUP__.set(key, now + ttlMs);
        return false;
    }

    // ---------- helpers ----------
    function shouldParse(text) {
        if (!text) return false;
        return CMD_OPEN_RE.test(text) && CMD_CLOSE_RE.test(text);
    }

    function stripHtmlToText(el) {
        return (el && el.textContent) ? el.textContent : "";
    }

    function getNickFromLi(li) {
        const n = li?.querySelector?.(".nick");
        return (n?.textContent || "").trim().replace(/:\s*$/, "") || "Unknown";
    }

    function getTextEl(li) {
        return li?.querySelector?.(".text") || null;
    }

    function extractBetween(text, openTag, closeTag) {
        const low = String(text).toLowerCase();
        const a = low.indexOf(openTag);
        if (a === -1) return null;
        const b = low.indexOf(closeTag, a + openTag.length);
        if (b === -1) return null;
        return String(text).substring(a + openTag.length, b);
    }

    function normalizeUrl(u) {
        if (!u) return null;
        u = String(u).trim();
        if (!u) return null;
        if (!/^https?:\/\//i.test(u)) u = "https://" + u;
        return u;
    }

    function extractUrlPreferAnchor(textEl, fallbackText) {
        const a = textEl?.querySelector?.("a[href]");
        if (a?.href) return a.href;
        const m = String(fallbackText || "").match(/https?:\/\/[^\s\]]+/i);
        return m ? m[0] : null;
    }

    function makeToastId() {
        return "lm_toast_" + Date.now() + "_" + Math.random().toString(16).slice(2);
    }

    function toast(type, html, timeoutMs) {
        if (!win.toastr) {
            console.warn(LOG + "toastr not found; cannot show popup.");
            return null;
        }
        const fn = win.toastr[type] || win.toastr.info;
        fn.call(win.toastr, html, "", {
            timeOut: timeoutMs || 20000,
            extendedTimeOut: timeoutMs || 20000
        });
        return true;
    }

    function bindCloseForToastRoot(toastRootId) {
        // Bind once after toast inserts into DOM
        setTimeout(() => {
            const root = document.getElementById(toastRootId);
            if (!root) return;

            const closeBtn = root.querySelector('[data-lm-close="1"]');
            if (!closeBtn || closeBtn.__lm_bound) return;
            closeBtn.__lm_bound = true;

            closeBtn.addEventListener("click", (e) => {
                e.preventDefault();
                e.stopPropagation();
                const toastEl = root.closest(".toast");
                if (toastEl) toastEl.remove();
            });
        }, 0);
    }

    // ---------- youtube helpers ----------
    function extractYoutubeId(url) {
        const u = String(url || "");
        // watch?v=VIDEOID
        let m = u.match(/[?&]v=([A-Za-z0-9_-]{6,})/);
        if (m && m[1]) return m[1];
        // youtu.be/VIDEOID
        m = u.match(/youtu\.be\/([A-Za-z0-9_-]{6,})/);
        if (m && m[1]) return m[1];
        // shorts/VIDEOID
        m = u.match(/youtube\.com\/shorts\/([A-Za-z0-9_-]{6,})/);
        if (m && m[1]) return m[1];
        // embed/VIDEOID
        m = u.match(/youtube\.com\/embed\/([A-Za-z0-9_-]{6,})/);
        if (m && m[1]) return m[1];
        return null;
    }

    // ---------- command actions ----------
    function handleUrlCommand(payload, nick) {
        const url = normalizeUrl(payload);
        if (!url) return;

        const tid = makeToastId();
        toast("warning",
            `<div id="${tid}">
               <div style="display:flex;justify-content:space-between;align-items:center;gap:10px;">
                 <div><b>${nick}</b> shared a link</div>
                 <button data-lm-close="1"
                   style="cursor:pointer;background:#111;border:1px solid #444;color:#fff;border-radius:6px;padding:2px 8px;">✕</button>
               </div>
               <div style="margin-top:8px;">
                 <a href="${url}" target="_blank" rel="noreferrer" style="color:#3aa0ff">${url}</a>
               </div>
             </div>`,
            20000
        );
        bindCloseForToastRoot(tid);
    }

    function handleTagCommand(payload, nick) {
        const tag = String(payload || "").trim();
        const tid = makeToastId();

        toast("warning",
            `<div id="${tid}">
               <div style="display:flex;justify-content:space-between;align-items:center;gap:10px;">
                 <div><b>${nick}</b> shared a tag</div>
                 <button data-lm-close="1"
                   style="cursor:pointer;background:#111;border:1px solid #444;color:#fff;border-radius:6px;padding:2px 8px;">✕</button>
               </div>

               <div style="margin-top:8px;color:#f1c40f;font-family:monospace;">
                 ${tag || "(empty)"}
               </div>

               <button data-lm-apply-tag="1"
                 style="margin-top:10px;width:100%;cursor:pointer;background:#0b74ff;border:1px solid #0b74ff;color:#fff;border-radius:6px;padding:6px 10px;">
                 Apply Tag
               </button>
             </div>`,
            20000
        );

        setTimeout(() => {
            const root = document.getElementById(tid);
            if (!root) return;

            const apply = root.querySelector('[data-lm-apply-tag="1"]');
            if (apply && !apply.__lm_bound) {
                apply.__lm_bound = true;
                apply.addEventListener("click", (e) => {
                    e.preventDefault();
                    e.stopPropagation();

                    const el = document.querySelector('input[name="clantag"]') || document.getElementById("clantag");
                    if (el) {
                        el.value = tag;
                        el.dispatchEvent(new Event("input", { bubbles: true }));
                        el.dispatchEvent(new Event("change", { bubbles: true }));
                        el.style.backgroundColor = "#ff6347";
                        window.prevBg = el.style.backgroundColor;
                        el.style.backgroundColor = "#ff6347";
                        setTimeout(() => { el.style.backgroundColor = window.prevBg; }, 3000);
                    }
                });
            }
        }, 0);

        bindCloseForToastRoot(tid);
    }

    function handleDiscordCommand(payload, nick) {
        const url = normalizeUrl(payload);
        if (!url) return;

        if (!/(discord(app)?\.com\/invite|discord\.gg|discord\.com)/i.test(url)) return;

        const tid = makeToastId();
        toast("warning",
            `<div id="${tid}">
               <div style="display:flex;justify-content:space-between;align-items:center;gap:10px;">
                 <div><b>${nick}</b> shared Discord</div>
                 <button data-lm-close="1"
                   style="cursor:pointer;background:#111;border:1px solid #444;color:#fff;border-radius:6px;padding:2px 8px;">✕</button>
               </div>
               <div style="margin-top:8px;">
                 <a href="${url}" target="_blank" rel="noreferrer" style="color:#7289da">${url}</a>
               </div>
             </div>`,
            20000
        );
        bindCloseForToastRoot(tid);
    }

    function handleSkypeCommand(payload, nick) {
        const url = normalizeUrl(payload);
        if (!url) return;

        if (!/join\.skype\.com\//i.test(url)) return;

        const tid = makeToastId();
        toast("warning",
            `<div id="${tid}">
               <div style="display:flex;justify-content:space-between;align-items:center;gap:10px;">
                 <div><b>${nick}</b> shared Skype</div>
                 <button data-lm-close="1"
                   style="cursor:pointer;background:#111;border:1px solid #444;color:#fff;border-radius:6px;padding:2px 8px;">✕</button>
               </div>
               <div style="margin-top:8px;">
                 <a href="${url}" target="_blank" rel="noreferrer" style="color:#3aa0ff">${url}</a>
               </div>
             </div>`,
            20000
        );
        bindCloseForToastRoot(tid);
    }

    function handleImgCommand(payload, nick) {
        const url = normalizeUrl(payload);
        if (!url) return;

        const tid = makeToastId();
        const p = previewImageUrl(url);

        toast("warning",
            `<div id="${tid}">
           <div style="display:flex;justify-content:space-between;align-items:center;gap:10px;">
             <div><b>${nick}</b> shared image</div>
             <button data-lm-close="1"
               style="cursor:pointer;background:#111;border:1px solid #444;color:#fff;border-radius:6px;padding:2px 8px;">✕</button>
           </div>

           <center><img
             src="${p.direct}"
             referrerpolicy="no-referrer"
             loading="lazy"
             decoding="async"
             style="max-width:100%;margin-top:8px;border:1px solid #222;border-radius:8px;"
             onerror="this.onerror=null; this.src='${p.proxy}';"
           ></center>
         </div>`,
            20000
        );

        bindCloseForToastRoot(tid);
    }


    // [yut] => embed instead of Play button
    function handleYoutubeCommand(payload, nick) {
        const url = normalizeUrl(payload);
        if (!url) return;

        const vid = extractYoutubeId(url);
        if (!vid) {
            // not a recognizable youtube link; just show as url
            handleUrlCommand(url, nick);
            return;
        }

        const tid = makeToastId();
        toast("warning",
            `<div id="${tid}">
               <div style="display:flex;justify-content:space-between;align-items:center;gap:10px;">
                 <div><b>${nick}</b> shared YouTube</div>
                 <button data-lm-close="1"
                   style="cursor:pointer;background:#111;border:1px solid #444;color:#fff;border-radius:6px;padding:2px 8px;">✕</button>
               </div>

               <div style="margin-top:8px;">
                 <iframe
                   width="100%"
                   style="aspect-ratio:16/9;border:0;border-radius:8px;overflow:hidden;"
                   src="https://www.youtube.com/embed/${vid}?autoplay=1&mute=0&vq=tiny"
                   title="YouTube video player"
                   allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
                   allowfullscreen></iframe>
               </div>

               <div style="margin-top:8px;font-size:12px;">
                 <a href="${url}" target="_blank" rel="noreferrer" style="color:#e74c3c">${url}</a>
               </div>
             </div>`,
            20000
        );
        bindCloseForToastRoot(tid);
    }

    // ---------- main parser ----------
    function LM_ParseChatCommand(rawText, nick, textEl, liToOptionallyRemove) {
        const text = String(rawText || "");
        if (!shouldParse(text)) return false;

        // [url]
        if (/\[url\]/i.test(text) && /\[\/url\]/i.test(text)) {
            const payload = extractBetween(text, "[url]", "[/url]");
            const url = extractUrlPreferAnchor(textEl, payload || "");
            handleUrlCommand(url || payload, nick);
            if (win.REMOVE_CHAT_MESSAGE_AFTER_PARSE && liToOptionallyRemove) try { liToOptionallyRemove.remove(); } catch (_) {}
            return true;
        }

        // [tag]
        if (/\[tag\]/i.test(text) && /\[\/tag\]/i.test(text)) {
            const payload = extractBetween(text, "[tag]", "[/tag]");
            handleTagCommand(payload, nick);
            if (win.REMOVE_CHAT_MESSAGE_AFTER_PARSE && liToOptionallyRemove) try { liToOptionallyRemove.remove(); } catch (_) {}
            return true;
        }

        // [yut]
        if (/\[yut\]/i.test(text) && /\[\/yut\]/i.test(text)) {
            const payload = extractBetween(text, "[yut]", "[/yut]");
            const url = extractUrlPreferAnchor(textEl, payload || "");
            handleYoutubeCommand(url || payload, nick);
            if (win.REMOVE_CHAT_MESSAGE_AFTER_PARSE && liToOptionallyRemove) try { liToOptionallyRemove.remove(); } catch (_) {}
            return true;
        }

        // [img]
        if (/\[img\]/i.test(text) && /\[\/img\]/i.test(text)) {
            const payload = extractBetween(text, "[img]", "[/img]");
            const url = extractUrlPreferAnchor(textEl, payload || "");
            handleImgCommand(url || payload, nick);
            if (win.REMOVE_CHAT_MESSAGE_AFTER_PARSE && liToOptionallyRemove) try { liToOptionallyRemove.remove(); } catch (_) {}
            return true;
        }

        // [skype]
        if (/\[skype\]/i.test(text) && /\[\/skype\]/i.test(text)) {
            const payload = extractBetween(text, "[skype]", "[/skype]");
            const url = extractUrlPreferAnchor(textEl, payload || "");
            handleSkypeCommand(url || payload, nick);
            if (win.REMOVE_CHAT_MESSAGE_AFTER_PARSE && liToOptionallyRemove) try { liToOptionallyRemove.remove(); } catch (_) {}
            return true;
        }

        // [discord]
        if (/\[discord\]/i.test(text) && /\[\/discord\]/i.test(text)) {
            const payload = extractBetween(text, "[discord]", "[/discord]");
            const url = extractUrlPreferAnchor(textEl, payload || "");
            handleDiscordCommand(url || payload, nick);
            if (win.REMOVE_CHAT_MESSAGE_AFTER_PARSE && liToOptionallyRemove) try { liToOptionallyRemove.remove(); } catch (_) {}
            return true;
        }

        return false;
    }

    // ---------- Delta chat observer ----------
    function attachDeltaChatObserver() {
        const container = document.querySelector(".chat-messages-container ul.chatmessages");
        if (!container) return false;

        const parseMessageLi = (li) => {
            if (!li || !(li instanceof HTMLElement)) return;
            if (!li.classList.contains("message")) return;

            const textEl = getTextEl(li);
            if (!textEl) return;

            const nick = getNickFromLi(li);
            const text = stripHtmlToText(textEl);

            // Delta hydrates: same li gets updated multiple times.
            // Prevent duplicate handling for the same final text.
            const sig = nick + "||" + text;
            if (li.dataset && li.dataset.lmCmdSig === sig) return;
            if (li.dataset) li.dataset.lmCmdSig = sig;

            // Dedup between chat parsing + toastr-hook (short TTL)
            if (lmToastDedupHit("chat::" + sig)) return;

            if (shouldParse(text)) {
                LM_ParseChatCommand(text, nick, textEl, li);
            }
        };

        // Parse existing
        Array.from(container.querySelectorAll("li.message")).forEach(parseMessageLi);

        const obs = new MutationObserver((mutations) => {
            for (const m of mutations) {
                // A) new nodes added
                if (m.addedNodes && m.addedNodes.length) {
                    m.addedNodes.forEach((n) => {
                        if (!(n instanceof HTMLElement)) return;

                        if (n.matches?.("li.message")) parseMessageLi(n);
                        n.querySelectorAll?.("li.message")?.forEach(parseMessageLi);

                        // Sometimes only .text gets replaced, not the li
                        const li = n.closest?.("li.message");
                        if (li) parseMessageLi(li);
                    });
                }

                // B) text/html updates inside existing nodes
                // Delta often mutates Text nodes => m.target is NOT HTMLElement
                if (m.type === "characterData") {
                    const p = m.target && m.target.parentElement ? m.target.parentElement : null;
                    const li = p && p.closest ? p.closest("li.message") : null;
                    if (li) parseMessageLi(li);
                } else if (m.target && m.target instanceof HTMLElement) {
                    const li = m.target.closest?.("li.message");
                    if (li) parseMessageLi(li);
                }
            }
        });

        obs.observe(container, {
            childList: true,
            subtree: true,
            characterData: true
        });

        console.log(LOG + "Delta chat observer attached.");
        return true;
    }
    function previewImageUrl(url) {
        const u = String(url || "").trim();

        // If user pasted an imgur PAGE link, convert to direct CDN where possible
        // (best effort; if it already is i.imgur.com, keep it)
        let direct = u;
        if (/^https?:\/\/imgur\.com\/\w+/i.test(u) && !/\/gallery\//i.test(u)) {
            const id = u.split("/").pop().split(/[?#]/)[0];
            direct = "https://i.imgur.com/" + id + ".png";
        }

        // Proxy fallback (bypasses referrer/hotlink blocks)
        // weserv expects URL without protocol
        const noProto = direct.replace(/^https?:\/\//i, "");
        return {
            direct,
            proxy: "https://images.weserv.nl/?url=" + encodeURIComponent(noProto)
        };
    }
    // ---------- Optional: Hook toastr so commands inside toast messages also work ----------
    function hookToastrIfPresent() {
        if (!win.toastr || win.toastr.__LM_CHATCMD_HOOKED__) return;
        win.toastr.__LM_CHATCMD_HOOKED__ = true;

        ["info", "warning", "error", "success"].forEach((type) => {
            const orig = win.toastr[type];
            if (typeof orig !== "function") return;

            win.toastr[type] = function (msg, title, opts) {
                try {
                    if (typeof msg === "string" && shouldParse(msg)) {
                        const t = String(msg);
                        const sig = ((title || "").trim() || "System") + "||" + t;

                        // If chat already processed it very recently, skip.
                        if (!lmToastDedupHit("toastr::" + sig)) {
                            LM_ParseChatCommand(t, (title || "").trim() || "System", null, null);
                        }
                    }
                } catch (_) {}
                return orig.call(this, msg, title, opts);
            };
        });

        console.log(LOG + "toastr hook attached.");
    }

    // ---------- boot ----------
    const wait = setInterval(() => {
        // If this IIFE runs twice (hot reload / duplicate injection), do not attach twice.
        if (win.__LM_DELTA_CHAT_OBS__) return;

        const ok = attachDeltaChatObserver();
        if (ok) {
            win.__LM_DELTA_CHAT_OBS__ = true;
            clearInterval(wait);
            hookToastrIfPresent();
        }
    }, 300);
})();


(function () {
    'use strict';

    const INTERVAL = setInterval(function () {
        try {
            // 1) Find the existing "Hide" button
            const hideBtn = Array.from(document.querySelectorAll("button"))
                .find(function (b) { return (b.textContent || "").trim() === "Hide"; });

            if (!hideBtn) return;
            if (hideBtn.dataset.lmHideIcon === "1") { clearInterval(INTERVAL); return; }

            const parent = hideBtn.parentElement;
            if (!parent) return;

            // 2) Prevent double patching
            hideBtn.dataset.lmHideIcon = "1";

            // 3) Ensure parent is horizontal layout
            parent.style.display = "flex";
            parent.style.gap = "4px";
            parent.style.alignItems = "stretch";

            // 4) Force 50/50 width
            hideBtn.style.flex = "1 1 50%";
            hideBtn.style.width = "50%";

            // 5) Turn existing Hide button into ICON button (keep its click handler)
            hideBtn.innerHTML = ""; // replace the text
            const hideLayer = document.createElement("div");
            hideLayer.className = "btn-layer";
            hideLayer.style.justifyContent = "center";
            hideLayer.style.width = "100%";

            // Choose an icon you want for hide (eye-slash is typical)
            const hideIcon = document.createElement("i");
            hideIcon.className = "fas fa-eye-slash";
            hideLayer.appendChild(hideIcon);
            hideBtn.appendChild(hideLayer);

            // 6) Add Skins icon button on the LEFT (only once)
            let skinsBtn = parent.querySelector('button[data-lm-skins-btn="1"]');
            if (!skinsBtn) {
                skinsBtn = document.createElement("button");
                skinsBtn.dataset.lmSkinsBtn = "1";

                // Copy classes from Hide so it matches styling
                skinsBtn.className = hideBtn.className;

                skinsBtn.style.flex = "1 1 50%";
                skinsBtn.style.width = "50%";

                const skinsLayer = document.createElement("div");
                skinsLayer.className = "btn-layer";
                skinsLayer.style.justifyContent = "center";
                skinsLayer.style.width = "100%";

                // Icon for skins (palette is typical)
                const skinsIcon = document.createElement("i");
                skinsIcon.className = "fas fa-palette";
                skinsLayer.appendChild(skinsIcon);

                skinsBtn.appendChild(skinsLayer);

                skinsBtn.addEventListener("click", function (e) {
                    e.preventDefault();
                    e.stopPropagation();
                    window.open("https://www.legendmod.ml/skins", "_blank", "noopener");
                });

                // Insert Skins button BEFORE Hide button (left)
                parent.insertBefore(skinsBtn, hideBtn);
            }

            // Done
            clearInterval(INTERVAL);

        } catch (e) {
            // keep waiting; UI may still be hydrating
        }
    }, 300);
})();