OG Kick Advanced Tools — Chat Spammer + Rate Limit Bypass

Advanced chat spammer for Kick.com

// ==UserScript==
// @name         OG Kick Advanced Tools — Chat Spammer + Rate Limit Bypass
// @namespace    http://tampermonkey.net/
// @version      1.7
// @description  Advanced chat spammer for Kick.com
// @author       Robert
// @icon         https://www.google.com/s2/favicons?sz=64&domain=kick.com
// @match        https://kick.com/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
'use strict';

const originalFetch = window.fetch;
let initialTokenLogged = false;

// Removed attemptToSniffTokenViaActivationMessage function

window.fetch = async function(...args) {
    let [resource, config] = args;
    let url = '';
    let requestHeaders = {};
    if (typeof resource === 'string') { url = resource; }
    else if (resource instanceof Request) { url = resource.url; }

    if (config && config.headers) {
        if (config.headers instanceof Headers) { config.headers.forEach((v, k) => { requestHeaders[k.toLowerCase()] = v; }); }
        else { for (const k in config.headers) { requestHeaders[k.toLowerCase()] = config.headers[k]; } }
    } else if (resource instanceof Request && resource.headers) {
         resource.headers.forEach((v, k) => { requestHeaders[k.toLowerCase()] = v; });
    }
    const authHeaderValue = requestHeaders['authorization'];
    if (authHeaderValue && typeof authHeaderValue === 'string' && authHeaderValue.startsWith('Bearer ')) {
        const token = authHeaderValue.substring(7);
        if (token) {
            const currentStoredToken = localStorage.getItem('bearerToken');
            if (token !== currentStoredToken) {
                localStorage.setItem('bearerToken', token);
                if (typeof window.bearerTokenGlobal !== 'undefined') window.bearerTokenGlobal = token;
                if (!initialTokenLogged) {
                    console.log('[Kick Tools] Bearer token detected/updated and stored.');
                    addLog(`INFO: Bearer token updated from network: ${token.substring(0,10)}...`);
                    initialTokenLogged = true;
                }
            }
        }
    }
    return originalFetch.apply(this, args);
};

window.bearerTokenGlobal = localStorage.getItem('bearerToken') || null;
let chatroomId = null;
let logs = [];
let spamStatus = 'unknown';
const randomEmotes = [
    '[emote:39286:YOUTried]', '[emote:37239:WeSmart]', '[emote:37240:WeirdChamp]', '[emote:39284:vibePlz]',
    '[emote:37237:TriKool]', '[emote:39283:ToXiC]', '[emote:37236:ThisIsFine]', '[emote:37235:SUSSY]',
    '[emote:28633:SenpaiWhoo]', '[emote:39282:saltyTrain]', '[emote:37248:ratJAM]', '[emote:37234:Prayge]',
    '[emote:39279:PPJedi]', '[emote:39277:politeCat]', '[emote:37230:POLICE]', '[emote:37233:PogU]',
    '[emote:39275:peepoShyy]', '[emote:37246:peepoRiot]', '[emote:37245:peepoDJ]', '[emote:37232:PeepoClap]',
    '[emote:37231:PatrickBoo]', '[emote:28632:OuttaPocke]', '[emote:37229:OOOO]', '[emote:28631:NugTime]',
    '[emote:37228:NODDERS]', '[emote:39273:MuteD]', '[emote:37244:modCheck]', '[emote:43404:mericKat]',
    '[emote:37227:LULW]', '[emote:39272:LetMeIn]', '[emote:39261:kkHuh]', '[emote:55886:kickSadge]',
    '[emote:37226:KEKW]', '[emote:37225:KEKLEO]', '[emote:39269:KEKByebye]', '[emote:39256:KatKiss]',
    '[emote:305040:KappA]', '[emote:39268:HYPERCLAPH]', '[emote:39267:HaHaaHaHaa]', '[emote:37224:GIGACHAD]',
    '[emote:37243:gachiGASM]', '[emote:39402:Flowie]', '[emote:37221:EZ]', '[emote:39265:EDMusiC]',
    '[emote:39262:duckPlz]', '[emote:37220:DonoWall]', '[emote:39260:DanceDance]', '[emote:39258:coffinPls]',
    '[emote:37218:Clap]', '[emote:37242:catblobDan]', '[emote:39254:CaptFail]', '[emote:37217:Bwop]',
    '[emote:39251:beeBobble]', '[emote:39250:BBooomer]', '[emote:37215:AYAYA]'
];

function createUI() {
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', buildActualUI);
    } else {
        buildActualUI();
    }
}

function buildActualUI() {
    if (document.getElementById('kick-tools-ui')) return;
    window.bearerTokenGlobal = localStorage.getItem('bearerToken') || null;

    const ui = document.createElement('div');
    ui.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        background: linear-gradient(135deg, #1e1e1e, #2a2a2a);
        padding: 0;
        border-radius: 10px;
        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
        z-index: 9999;
        color: #ffffff;
        font-family: 'Segoe UI', Arial, sans-serif;
        width: 450px;
        height: 450px;
        display: flex;
    `;
    ui.setAttribute('id', 'kick-tools-ui');
    const currentTokenForUI = window.bearerTokenGlobal || '';
    ui.innerHTML = `
        <div style="width: 120px; background: #181818; padding: 15px; border-radius: 10px 0 0 10px; display: flex; flex-direction: column;" id="dragHandle">
            <img src="https://kick.com/img/kick-logo.svg" style="width: 80px; margin-bottom: 20px; align-self: center;" alt="Kick Logo">
            <div id="tab-buttons" style="display: flex; flex-direction: column; gap: 10px; flex-grow: 1;">
                <button data-tab="chat" style="padding: 10px; background: #333; color: #fff; border: none; border-radius: 5px; cursor: pointer; text-align: left; transition: background 0.3s;">Chat Spammer</button>
                <button data-tab="settings" style="padding: 10px; background: #333; color: #fff; border: none; border-radius: 5px; cursor: pointer; text-align: left; transition: background 0.3s;">Settings</button>
            </div>
            <div id="ui-watermark" style="font-size: 10px; color: #777; text-align: center; margin-top: auto; padding-bottom: 5px;">
                Created by Robert<br>
                <a href="https://t.me/kickbot_pro" target="_blank" style="color: #5a8cd7; text-decoration: none;">t.me/kickbot_pro</a>
            </div>
        </div>
        <div style="flex: 1; padding: 20px; overflow-y: auto; position: relative;">
            <button id="minimizeButton" style="position: absolute; top: 10px; right: 10px; padding: 5px 10px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer; font-size: 12px;">-</button>
            <div id="chat-tab" class="tab-content" style="display: none;">
                <div style="font-size: 18px; font-weight: 600; margin-bottom: 15px; color: #00ff00; text-shadow: 0 0 5px rgba(0, 255, 0, 0.3);">Chat Spammer</div>
                <div style="margin-bottom: 15px; display: flex; gap: 10px;">
                    <input type="text" id="messageInput" placeholder="Enter your message" style="flex: 1; padding: 8px 12px; background: #333; color: #fff; border: 1px solid #444; border-radius: 5px; outline: none;">
                    <input type="number" id="countInput" value="1" min="1" style="width: 60px; padding: 8px; background: #333; color: #fff; border: 1px solid #444; border-radius: 5px; outline: none;">
                </div>
                <div style="margin-bottom: 15px; display: flex; gap: 5px; flex-wrap: wrap;">
                    <button id="kekwButton" title="KEKW" style="padding: 8px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer;"><img src="https://files.kick.com/emotes/37226/fullsize" style="width: 20px; vertical-align: middle;"></button>
                    <button id="patrickBooButton" title="PatrickBoo" style="padding: 8px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer;"><img src="https://files.kick.com/emotes/37231/fullsize" style="width: 20px; vertical-align: middle;"></button>
                    <button id="thisIsFineButton" title="ThisIsFine" style="padding: 8px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer;"><img src="https://files.kick.com/emotes/37236/fullsize" style="width: 20px; vertical-align: middle;"></button>
                    <button id="modCheckButton" title="modCheck" style="padding: 8px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer;"><img src="https://files.kick.com/emotes/37244/fullsize" style="width: 20px; vertical-align: middle;"></button>
                    <button id="muteDButton" title="MuteD" style="padding: 8px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer;"><img src="https://files.kick.com/emotes/39273/fullsize" style="width: 20px; vertical-align: middle;"></button>
                    <button id="weSmartButton" title="WeSmart" style="padding: 8px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer;"><img src="https://files.kick.com/emotes/37239/fullsize" style="width: 20px; vertical-align: middle;"></button>
                </div>
                <div style="margin-bottom: 15px; display: flex; gap: 10px;">
                    <button id="spamButton" style="flex: 1; padding: 10px; background: #00ff00; color: #000; border: none; border-radius: 5px; cursor: pointer; font-weight: 600;">SPAM</button>
                    <button id="saveButton" style="flex: 1; padding: 10px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer; font-weight: 600;">Save</button>
                </div>
                <div style="margin-bottom: 15px;">
                    <select id="savedMessages" style="width: 100%; padding: 8px; background: #333; color: #fff; border: 1px solid #444; border-radius: 5px; outline: none;">
                        <option value="">Select Saved Message</option>
                    </select>
                </div>
                <div style="margin-bottom: 15px; display: flex; align-items: center; gap: 15px;">
                    <label style="font-size: 14px;">Delay (ms): <input type="number" id="delayInput" value="0" min="0" style="width: 60px; padding: 6px; background: #333; color: #fff; border: 1px solid #444; border-radius: 5px; outline: none;"></label>
                    <label style="font-size: 14px;">Random Emote: <input type="checkbox" id="randomEmoteCheckbox" style="width: 16px; height: 16px;"></label>
                    <span id="spamStatus" style="margin-left: auto; font-size: 12px;"></span>
                </div>
                <div style="margin-top: 20px; display: flex; justify-content: space-between; align-items: center;">
                    <span style="font-size: 12px;">Chatroom ID: <span id="chatIdDisplay">${chatroomId || 'N/A'}</span></span>
                    <button id="clearLogsButton" style="padding: 6px 12px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer; font-size: 12px;">Clear Logs</button>
                </div>
                <div id="logArea" style="height: 100px; overflow-y: auto; background: #222; padding: 10px; border-radius: 5px; font-size: 12px; line-height: 1.4; border: 1px solid #444; margin-top: 10px;"></div>
            </div>
            <div id="settings-tab" class="tab-content" style="display: none;">
                <div style="font-size: 18px; font-weight: 600; margin-bottom: 15px; color: #00ff00; text-shadow: 0 0 5px rgba(0, 255, 0, 0.3);">Settings</div>
                <div style="margin-bottom: 15px;">
                    <input type="text" id="tokenInput" placeholder="Enter Bearer Token (or leave for auto-detect)" value="${currentTokenForUI}" style="width: 100%; padding: 8px 12px; background: #333; color: #fff; border: 1px solid #444; border-radius: 5px; outline: none;">
                </div>
                <button id="setTokenButton" style="width: 100%; padding: 10px; background: #555; color: #fff; border: none; border-radius: 5px; cursor: pointer; font-weight: 600;">Set/Update Token</button>
                <p style="font-size: 12px; color: #aaa; margin-top: 10px;">Token should be auto-detected. Use 'Set Token' to manually override or if detection fails.</p>
            </div>
        </div>
    `;
    document.body.appendChild(ui);
    addDragFunctionality(ui);
    setupEventListeners();
    populateSavedMessages();
    updateSpamStatus();
    getChatroomId();
    setInterval(getChatroomId, 2500);
    setInterval(updateSpamStatus, 1500);
}

function createMinimizedIcon() {
    if (document.getElementById('minimized-icon')) return;
    const icon = document.createElement('div');
    icon.style.cssText = `
        position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
        background: #555; padding: 10px; border-radius: 50%;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); z-index: 9999; cursor: pointer;
        width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;
    `;
    icon.setAttribute('id', 'minimized-icon');
    icon.innerHTML = `<img src="https://kick.com/img/kick-logo.svg" style="width: 30px;" title="Click to restore Kick Tools">`;
    document.body.appendChild(icon);
    icon.addEventListener('click', () => {
        const ui = document.getElementById('kick-tools-ui');
        if (ui) ui.style.display = 'flex';
        icon.remove();
    });
}

function addDragFunctionality(element) {
    let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
    const dragHandle = element.querySelector('#dragHandle');
    if (!dragHandle) return;
    dragHandle.onmousedown = dragMouseDown;
    function dragMouseDown(e) {
        if (e.target.tagName === 'BUTTON' || e.target.tagName === 'A' || e.target.closest('a') || e.target.closest('#tab-buttons')) return;
        e.preventDefault();
        pos3 = e.clientX; pos4 = e.clientY;
        document.onmouseup = closeDragElement; document.onmousemove = elementDrag;
    }
    function elementDrag(e) {
        e.preventDefault();
        pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY;
        pos3 = e.clientX; pos4 = e.clientY;
        element.style.top = (element.offsetTop - pos2) + "px";
        element.style.left = (element.offsetLeft - pos1) + "px";
    }
    function closeDragElement() {
        document.onmouseup = null; document.onmousemove = null;
    }
}

function addLog(message) {
    const timestamp = new Date().toLocaleTimeString();
    logs.push(`[${timestamp}] ${message}`);
    if (logs.length > 100) logs.shift();
    const logArea = document.getElementById('logArea');
    if (logArea) { logArea.innerHTML = logs.join('<br>'); logArea.scrollTop = logArea.scrollHeight; }
}

function getChatroomId() {
    const source = document.documentElement.innerHTML;
    const match = source.match(/"chatroomId\\":(\d+),/);
    const newId = match && match[1] ? match[1] : null;
    if (newId !== chatroomId) {
        chatroomId = newId;
        const chatIdDisplay = document.getElementById('chatIdDisplay');
        if (chatIdDisplay) {
            chatIdDisplay.textContent = chatroomId || 'N/A';
        }
        addLog(`DEBUG: Updated chatroom ID: ${chatroomId || 'N/A'}`);
    }
}

function updateSpamStatus() {
    const chatInputDiv = document.querySelector('div.chat-input');
    let chatInputField = null;
    if (chatInputDiv) chatInputField = chatInputDiv.querySelector('div[contenteditable="true"], textarea');
    if (!chatInputField) {
        const pointerEventsDiv = document.querySelector('div.pointer-events-none.absolute.left-2\\.5.top-1\\/2.-translate-y-1\\/2');
        spamStatus = pointerEventsDiv && pointerEventsDiv.textContent.includes('Send a message') ? 'able' : 'unable';
    } else {
        spamStatus = chatInputField && !chatInputField.hasAttribute('disabled') ? 'able' : 'unable';
    }
    const statusElement = document.getElementById('spamStatus');
    if (statusElement) {
        statusElement.textContent = spamStatus === 'able' ? 'Chat Available' : 'Chat Unavailable';
        statusElement.style.color = spamStatus === 'able' ? '#00ff00' : '#ff4444';
    }
}

function populateSavedMessages() {
    const savedMessages = JSON.parse(localStorage.getItem('savedMessagesKickTools')) || [];
    const select = document.getElementById('savedMessages');
    if (!select) return;
    select.innerHTML = '<option value="">Select Saved Message</option>';
    savedMessages.forEach((msg, index) => {
        const option = document.createElement('option');
        option.value = index;
        option.text = msg.substring(0, 30) + (msg.length > 30 ? '...' : '');
        select.appendChild(option);
    });
}

async function sendMessages(message, count, delay, randomEmote) {
    window.bearerTokenGlobal = localStorage.getItem('bearerToken') || null;
    if (!window.bearerTokenGlobal) {
        addLog('ERROR: No bearer token for sendMessages. Attempting active sniff...');
        // await attemptToSniffTokenViaActivationMessage(); // Call removed
        window.bearerTokenGlobal = localStorage.getItem('bearerToken') || null;
        if (!window.bearerTokenGlobal) {
            alert('Bearer Token is missing! Ensure logged in & page loaded, or set manually.');
            addLog('ERROR: Bearer token still missing after sniff attempt.');
            return;
        }
        addLog('INFO: Token acquired for sendMessages.');
    }

    if (!chatroomId) {
        addLog('ERROR: No chatroom ID. Cannot send messages.');
        alert('Chatroom ID is missing! Ensure on a Kick stream page.');
        return;
    }

    const url = `https://kick.com/api/v2/messages/send/${chatroomId}`;
    const headers = {
        "accept": "application/json",
        "content-type": "application/json",
        "Authorization": `Bearer ${window.bearerTokenGlobal}`,
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    };

    let successCount = 0, failCount = 0;

    if (delay === 0) {
        addLog(`INFO: Initiating ${count} message(s) concurrently (delay is 0).`);
        const promises = [];
        for (let i = 0; i < count; i++) {
            const currentMessage = randomEmote ? randomEmotes[Math.floor(Math.random() * randomEmotes.length)] : message || "Kick Tools!";
            const data = { "content": currentMessage, "type": "message" };

            promises.push(
                originalFetch(url, { method: 'POST', headers: headers, body: JSON.stringify(data) })
                    .then(async response => {
                        if (response.ok) {
                            return { type: 'success', status: response.status, index: i };
                        } else {
                            const errorText = await response.text();
                            return { type: 'error', status: response.status, text: errorText.substring(0, 100), index: i };
                        }
                    })
                    .catch(error => {
                        return { type: 'network_error', message: error.message, index: i };
                    })
            );
        }

        const results = await Promise.allSettled(promises);

        results.forEach(result => {
            if (result.status === 'fulfilled') {
                const outcome = result.value;
                const msgIdentifier = `Msg ${outcome.index + 1}/${count}`;

                if (outcome.type === 'success') {
                    addLog(`SUCCESS (Concurrent): ${msgIdentifier} sent. Status: ${outcome.status}`);
                    successCount++;
                } else if (outcome.type === 'error') {
                    addLog(`ERROR (Concurrent): ${msgIdentifier} failed. Status: ${outcome.status}. Resp: ${outcome.text}`);
                    failCount++;
                    if (outcome.status === 401 || outcome.status === 403) {
                        addLog("ERROR: Auth failed (401/403) during concurrent send. Clearing token.");
                        localStorage.removeItem('bearerToken'); window.bearerTokenGlobal = null;
                        initialTokenLogged = false;
                    } else if (outcome.status === 429) {
                        addLog("ERROR: Rate limited (429) during concurrent send. Some messages likely failed.");
                    }
                } else if (outcome.type === 'network_error') {
                    addLog(`ERROR (Concurrent Network): ${msgIdentifier}: ${outcome.message}`);
                    failCount++;
                }
            } else {
                addLog(`ERROR (Concurrent System): Message processing failed: ${result.reason}`);
                failCount++;
            }
        });

    } else {
        addLog(`INFO: Initiating ${count} message(s) sequentially. Target interval: ${delay}ms between send initiations.`);
        let lastSendInitiationTime = 0;

        for (let i = 0; i < count; i++) {
            const currentMessage = randomEmote ? randomEmotes[Math.floor(Math.random() * randomEmotes.length)] : message || "Kick Tools!";
            const data = { "content": currentMessage, "type": "message" };

            if (i > 0) { // For messages after the first one
                const currentTime = Date.now();
                const timeSinceLastStart = currentTime - lastSendInitiationTime;
                const waitDuration = delay - timeSinceLastStart;

                if (waitDuration > 0) {
                    await new Promise(resolve => setTimeout(resolve, waitDuration));
                }
            }

            lastSendInitiationTime = Date.now(); // Record time just before sending
            try {
                const response = await originalFetch(url, { method: 'POST', headers: headers, body: JSON.stringify(data) });

                if (response.ok) {
                    addLog(`SUCCESS (Sequential): Msg ${i + 1}/${count} sent. Status: ${response.status}`);
                    successCount++;
                } else {
                    const errorText = await response.text();
                    addLog(`ERROR (Sequential): Msg ${i + 1}/${count} failed. Status: ${response.status}. Resp: ${errorText.substring(0, 100)}`);
                    failCount++;
                    if (response.status === 401 || response.status === 403) {
                        addLog("ERROR: Auth failed (401/403). Clearing token.");
                        localStorage.removeItem('bearerToken'); window.bearerTokenGlobal = null;
                        initialTokenLogged = false;
                    } else if (response.status === 429) {
                        addLog("ERROR: Rate limited (429). Stopping further sequential messages.");
                        break;
                    }
                }
            } catch (error) {
                addLog(`ERROR (Sequential Network): Msg ${i + 1}/${count}: ${error.message}`);
                failCount++;
            }
        }
    }
    addLog(`INFO: Finished. Success: ${successCount}, Failed: ${failCount}.`);
}


function setupEventListeners() {
    const spamButton = document.getElementById('spamButton');
    const messageInput = document.getElementById('messageInput');
    const countInput = document.getElementById('countInput');
    const tokenInput = document.getElementById('tokenInput');
    const setTokenButton = document.getElementById('setTokenButton');
    const saveButton = document.getElementById('saveButton');
    const savedMessagesSelect = document.getElementById('savedMessages');
    const delayInput = document.getElementById('delayInput');
    const randomEmoteCheckbox = document.getElementById('randomEmoteCheckbox');
    const clearLogsButton = document.getElementById('clearLogsButton');
    const emoteButtons = {
        kekwButton: '[emote:37226:KEKW]', patrickBooButton: '[emote:37231:PatrickBoo]',
        thisIsFineButton: '[emote:37236:ThisIsFine]', modCheckButton: '[emote:37244:modCheck]',
        muteDButton: '[emote:39273:MuteD]', weSmartButton: '[emote:37239:WeSmart]'
    };
    const minimizeButton = document.getElementById('minimizeButton');
    const tabButtons = document.querySelectorAll('#tab-buttons button');

    tabButtons.forEach(button => {
        button.addEventListener('click', () => {
            document.querySelectorAll('.tab-content').forEach(tab => tab.style.display = 'none');
            const tabContent = document.getElementById(`${button.dataset.tab}-tab`);
            if (tabContent) tabContent.style.display = 'block';
            tabButtons.forEach(btn => btn.style.background = '#333');
            button.style.background = '#555';
            if (button.dataset.tab === 'settings') {
                const tokenField = document.getElementById('tokenInput');
                if (tokenField) tokenField.value = localStorage.getItem('bearerToken') || '';
            }
        });
    });
    if (tabButtons.length > 0 && tabButtons[0]) tabButtons[0].click();

    spamButton?.addEventListener('click', () => {
        sendMessages(messageInput.value, parseInt(countInput.value) || 1, parseInt(delayInput.value) || 0, randomEmoteCheckbox.checked);
    });
    setTokenButton?.addEventListener('click', () => {
        const manualToken = tokenInput.value.trim();
        if (manualToken) {
            window.bearerTokenGlobal = manualToken; localStorage.setItem('bearerToken', manualToken);
            addLog(`INFO: Manual token set: ${manualToken.substring(0, 10)}...`);
        } else {
            localStorage.removeItem('bearerToken'); window.bearerTokenGlobal = null;
            addLog('INFO: Manual token cleared.');
        }
    });
    saveButton?.addEventListener('click', () => {
        const msg = messageInput.value.trim(); if (!msg) return;
        const saved = JSON.parse(localStorage.getItem('savedMessagesKickTools')) || [];
        if (!saved.includes(msg)) {
            saved.push(msg); localStorage.setItem('savedMessagesKickTools', JSON.stringify(saved));
            populateSavedMessages(); addLog(`INFO: Saved msg: ${msg.substring(0, 20)}...`);
        } else { addLog(`INFO: Msg already saved: ${msg.substring(0, 20)}...`); }
    });
    savedMessagesSelect?.addEventListener('change', () => {
        const idx = savedMessagesSelect.value; if (idx === '') return;
        const saved = JSON.parse(localStorage.getItem('savedMessagesKickTools')) || [];
        messageInput.value = saved[idx]; addLog(`INFO: Loaded msg: ${saved[idx].substring(0, 20)}...`);
    });
    Object.keys(emoteButtons).forEach(id => {
        document.getElementById(id)?.addEventListener('click', () => {
            messageInput.value += ` ${emoteButtons[id]}`; messageInput.focus();
            addLog(`DEBUG: Added emote: ${emoteButtons[id]}`);
        });
    });
    clearLogsButton?.addEventListener('click', () => {
        logs = []; const logArea = document.getElementById('logArea');
        if (logArea) logArea.innerHTML = ''; addLog('INFO: Logs cleared');
    });
    minimizeButton?.addEventListener('click', () => {
        const ui = document.getElementById('kick-tools-ui');
        if (ui) ui.style.display = 'none';
        createMinimizedIcon(); addLog('INFO: UI minimized');
    });
}

if (window.bearerTokenGlobal) {
     addLog(`INFO: Loaded token from storage: ${(window.bearerTokenGlobal).substring(0, 10)}...`);
} else {
     addLog('INFO: No token in storage. Will attempt active sniff after page load.');
     // if (document.readyState === 'complete') { attemptToSniffTokenViaActivationMessage(); } // Call removed
     // else { window.addEventListener('load', attemptToSniffTokenViaActivationMessage); } // Call removed
}
createUI();


})();