Gartic.io 채팅 복구 & 모바일 최적화

Gartic.io의 삭제된 채팅을 복구하고 무제한 채팅을 가능하게 합니다.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name            Cafune Gartic.io Chat Fix
// @name:tr         Gartic.io Sohbeti Geri Getirme & Mobil Uyum
// @name:en         Gartic.io Restore Chat & Mobile Adaptive
// @name:az         Gartic.io Söhbəti Bərpa Et & Mobil Uyğunluq
// @name:ru         Gartic.io Восстановление чата и мобильная адаптация
// @name:ms         Gartic.io Pulihkan Sembang & Mudah Alih
// @name:id         Gartic.io Pulihkan Obrolan & Adaptif Seluler
// @name:ca         Gartic.io Restaura el xat & Adaptació mòbil
// @name:nl         Gartic.io Chat Herstellen & Mobiele Aanpassing
// @name:da         Gartic.io Gendan Chat & Mobil Tilpasning
// @name:et         Gartic.io Taasta Vestlus & Mobiilne Kohandamine
// @name:de         Gartic.io Chat Wiederherstellen & Mobile Anpassung
// @name:es         Gartic.io Restaurar chat y adaptación móvil
// @name:fr         Gartic.io Restaurer le chat et adaptation mobile
// @name:it         Gartic.io Ripristina chat e adattamento mobile
// @name:hu         Gartic.io Chat Visszaállítása & Mobil Alkalmazkodás
// @name:pl         Gartic.io Przywracanie czatu i adaptacja mobilna
// @name:ro         Gartic.io Restabilire chat și adaptare mobilă
// @name:sk         Gartic.io Obnovenie chatu a mobilná adaptácia
// @name:vi         Gartic.io Khôi phục trò chuyện & Thích ứng di động
// @name:bg         Gartic.io Възстановяване на чата и мобилна адаптация
// @name:hr         Gartic.io Vraćanje chata i mobilna prilagodba
// @name:fi         Gartic.io Chatin palautus & Mobiilisovitus
// @name:sv         Gartic.io Återställ chatt & Mobilanpassning
// @name:el         Gartic.io Επαναφορά συνομιλίας & Προσαρμογή για κινητά
// @name:sr         Gartic.io Vraćanje četa i mobilna adaptacija
// @name:uk         Gartic.io Відновлення чату і мобільна адаптація
// @name:ar         Gartic.io استعادة الدردشة والتكيف مع الهاتف المحمول
// @name:fa         Gartic.io بازیابی چت و سازگاری با موبایل
// @name:th         Gartic.io กู้คืนแชท & รองรับมือถือ
// @name:zh-CN      Gartic.io 恢复聊天 & 移动端适配
// @name:zh-TW      Gartic.io 恢復聊天 & 移動端適應
// @name:ja         Gartic.io チャット復元&モバイル対応
// @name:ko         Gartic.io 채팅 복구 & 모바일 최적화
// @name:hi         Gartic.io चैट बहाली और मोबाइल अनुकूलन
// @version         2.4
// @license      MIT
// @description     Restores the removed chat for Gartic.io and allows you to chat without limits.
// @description:tr  Gartic.io için kaldırılan sohbeti geri getirir ve sınırsız sohbet etmenizi sağlar.
// @description:en  Restores the removed chat for Gartic.io and allows you to chat without limits.
// @description:az  Gartic.io üçün silinmiş söhbəti bərpa edir və limitsiz söhbət etməyə imkan verir.
// @description:ru  Восстанавливает удаленный чат в Gartic.io и позволяет общаться без ограничений.
// @description:ms  Mengembalikan sembang yang dialih keluar untuk Gartic.io dan membolehkan anda bersembang tanpa had.
// @description:id  Mengembalikan obrolan yang dihapus untuk Gartic.io dan memungkinkan Anda mengobrol tanpa batas.
// @description:ca  Restaura el xat eliminat per a Gartic.io i us permet xatejar sense límits.
// @description:nl  Herstelt de verwijderde chat voor Gartic.io en stelt u in staat om onbeperkt te chatten.
// @description:da  Gendanner den fjernede chat til Gartic.io og giver dig mulighed for at chatte uden grænser.
// @description:et  Taastab Gartic.io eemaldatud vestluse ja võimaldab teil piiranguteta vestelda.
// @description:de  Stellt den entfernten Chat für Gartic.io wieder her und ermöglicht unbegrenztes Chatten.
// @description:es  Restaura el chat eliminado para Gartic.io y te permite chatear sin límites.
// @description:fr  Restaure le chat supprimé pour Gartic.io et vous permet de discuter sans limites.
// @description:it  Ripristina la chat rimossa per Gartic.io e ti consente di chattare senza limiti.
// @description:hu  Visszaállítja az eltávolított chatet a Gartic.io-hoz, és korlátlan chatelést tesz lehetővé.
// @description:pl  Przywraca usunięty czat w Gartic.io i pozwala na czatowanie bez ograniczeń.
// @description:ro  Restabilește chat-ul eliminat pentru Gartic.io și vă permite să chatuiți fără limite.
// @description:sk  Obnovuje odstránený chat pre Gartic.io a umožňuje vám chatovať bez obmedzení.
// @description:vi  Khôi phục trò chuyện đã bị xóa cho Gartic.io và cho phép bạn trò chuyện không giới hạn.
// @description:bg  Възстановява премахнатия чат за Gartic.io и ви позволява да чатите без ограничения.
// @description:hr  Vraća uklonjeni chat za Gartic.io i omogućuje vam neograničeno čavrljanje.
// @description:fi  Palauttaa poistetun chatin Gartic.io-sivustolle ja mahdollistaa rajattoman chatin.
// @description:sv  Återställer den borttagna chatten för Gartic.io och låter dig chatta utan gränser.
// @description:el  Επαναφέρει την κατειλημμένη συνομιλία για το Gartic.io και σας επιτρέπει να συνομιλείτε χωρίς όρια.
// @description:sr  Vraća uklonjeni čet za Gartic.io i omogućava vam da ćaskate bez ograničenja.
// @description:uk  Відновлює видалений чат для Gartic.io і дозволяє спілкуватися без обмежень.
// @description:ar  يستعيد الدردشة المحذوفة لـ Gartic.io ويسمح لك بالدردشة دون حدود.
// @description:fa  چت حذف شده Gartic.io را بازیابی می کند و به شما اجازه می دهد بدون محدودیت چت کنید.
// @description:th  คืนค่าแชทที่ถูกลบสำหรับ Gartic.io และช่วยให้คุณแชทได้โดยไม่จำกัด
// @description:zh-CN 恢复 Gartic.io 中被删除的聊天功能,允许无限聊天。
// @description:zh-TW 恢復 Gartic.io 中被刪除的聊天功能,允許無限聊天。
// @description:ja  Gartic.io の削除されたチャットを復元し、無制限にチャットできるようにします。
// @description:ko  Gartic.io의 삭제된 채팅을 복구하고 무제한 채팅을 가능하게 합니다.
// @description:hi  Gartic.io के लिए हटाए गए चैट को पुनर्स्थापित करता है और आपको असीमित चैट करने की अनुमति देता है।
// @author          Cafune
// @match           *://gartic.io/*
// @match           gartic.io/*
// @grant           GM_setValue
// @grant           GM_getValue
// @grant           GM_addValueChangeListener
// @grant           GM_addStyle
// @namespace       https://greatest.deepsurf.us/users/1591289
// ==/UserScript==

(function () {
    "use strict";

    const PANEL_ID = "fiu-mini-messenger";
    const STORAGE_KEY = "yk-mini-messenger-history";
    const UI_STORAGE_KEY = "yk-mini-messenger-ui";
    const MAX_HISTORY = 150;
    const trackedSockets = new Set();
    const SCRIPT_VERSION = (globalThis.GM_info && globalThis.GM_info.script && globalThis.GM_info.script.version) || "2.1";

    const defaultUiState = {
        top: 80,
        left: null,
        width: 340,
        height: 460,
        hidden: false,
        sendMode: "both", // chat|answer|both
        chatColor: "#14436d"
    };

    let uiState = loadUiState();
    let messageListEl;
    let inputEl;
    let sendButtonEl;
    let panelEl;
    let dockTabEl;
    let sendModeSelectEl;
    let activeSocket = null;
    let roomOrUserId = null;
    let myUserId = null;
    let wsInitDone = false;
    let dragState = null;
    let antiAfkTimer = null;
    let dragPointerId = null;
    let resizeState = null;
    let resizePointerId = null;
    let colorDots = [];
    let avatarLightboxEl = null;
    let avatarLightboxImgEl = null;
    let avatarLightboxOpenUrl = "";
    let roomCodeEl = null;
    let unreadCount = 0;
    let announcedRoomId = null;
    const votedUsers = new Set();
    const recentSelfEcho = [];
    const usersById = new Map();

    function loadUiState() {
        const raw = localStorage.getItem(UI_STORAGE_KEY);
        if (!raw) {
            return { ...defaultUiState };
        }
        try {
            const parsed = JSON.parse(raw);
            const merged = { ...defaultUiState, ...(parsed || {}) };
            merged.top = Number.isFinite(Number(merged.top)) ? Number(merged.top) : defaultUiState.top;
            merged.left = merged.left == null ? null : (Number.isFinite(Number(merged.left)) ? Number(merged.left) : null);
            merged.width = Number.isFinite(Number(merged.width)) ? Number(merged.width) : defaultUiState.width;
            merged.height = Number.isFinite(Number(merged.height)) ? Number(merged.height) : defaultUiState.height;
            return merged;
        } catch (_error) {
            return { ...defaultUiState };
        }
    }

    function saveUiState() {
        localStorage.setItem(UI_STORAGE_KEY, JSON.stringify(uiState));
    }

    function createUI() {
        if (document.getElementById(PANEL_ID)) {
            return;
        }

        panelEl = document.createElement("div");
        panelEl.id = PANEL_ID;
        panelEl.innerHTML = `
            <div class="fiu-mm-header">
                <span class="fiu-mm-title">✮ zeynep زيــنــب ✮ </span>
                <div class="fiu-mm-actions">
                    <select class="fiu-mm-send-mode">
                        <option value="chat">Sohbet</option>
                        <option value="answer">Cevap</option>
                        <option value="both">İkisi</option>
                    </select>
                    <span class="fiu-mm-room-code">---</span>
                    <button class="fiu-mm-hide" type="button" title="Gizle">-</button>
                </div>
            </div>
            <div class="fiu-mm-messages"></div>
            <div class="fiu-mm-input-row">
                <input class="fiu-mm-input" type="text" placeholder="Mesaj yaz..." maxlength="100" />
                <button class="fiu-mm-send" type="button" title="Send">&gt;&gt;</button>
            </div>
            <div class="fiu-mm-resize-handle" title="Boyutlandir"></div>
        `;

        dockTabEl = document.createElement("button");
        dockTabEl.className = "fiu-mm-dock";
        dockTabEl.type = "button";
        dockTabEl.textContent = "Chat";
        dockTabEl.title = "Mini Messenger goster";

       const style = document.createElement("style");
style.textContent = `
html, body {
    background-image: url("https://i.pinimg.com/236x/41/10/e1/4110e10dff8030f0579001e624f19faf.jpg") !important;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-attachment: fixed;
}

:root {
    --fiu-self-msg-bg: #14436d;
    --fiu-theme-panel-bg: #111827;
    --fiu-theme-surface-bg: #0b1220;
    --fiu-theme-surface-2: #0f172a;
    --fiu-theme-item-bg: #182235;
    --fiu-theme-text: #e5e7eb;
    --fiu-theme-border: #374151;
    --fiu-theme-input-border: #4b5563;
    --fiu-theme-accent: #2563eb;
    --fiu-theme-scroll-a: #334155;
    --fiu-theme-scroll-b: #64748b;
}

#fiu-mini-messenger .fiu-mm-title {
    color: #8D38C9;
    font-weight: 400;
    text-shadow: 0 0 2px rgba(255, 46, 136, 0.4);
}

            #${PANEL_ID} {
                position: fixed;
                top: 80px;
                left: calc(100vw - 360px);
                width: 340px;
                height: 460px;
                z-index: 999999;
                display: flex;
                flex-direction: column;
                background: var(--fiu-theme-panel-bg);
box-shadow:
    0 0 10px var(--fiu-theme-panel-bg),
    0 0 25px var(--fiu-theme-panel-bg),
    0 8px 24px rgba(0, 0, 0, 0.4);
                color: var(--fiu-theme-text);
                border: 1px solid var(--fiu-theme-border);
                border-radius: 12px;
                font-family: Arial, sans-serif;
                font-size: 15px;
                resize: both;
                overflow: hidden;
                min-width: 280px;
                min-height: 260px;
            }

            #${PANEL_ID} .fiu-mm-header {
                display: flex;
                align-items: center;
                justify-content: space-between;
                padding: 10px 12px;
                border-bottom: 1px solid var(--fiu-theme-border);
                font-size: 13px;
                font-weight: 700;
                letter-spacing: 0.2px;
                cursor: move;
                user-select: none;
                touch-action: none;
            }

            #${PANEL_ID} .fiu-mm-actions {
                display: flex;
                align-items: center;
                gap: 6px;
            }

            #${PANEL_ID} .fiu-mm-color-palette {
                display: flex;
                align-items: center;
                gap: 6px;
            }

            #${PANEL_ID} .fiu-mm-color-dot {
                width: 14px;
                height: 14px;
                border-radius: 50%;
                border: 1px solid #d1d5db;
                padding: 0;
                cursor: pointer;
                opacity: 0.85;
            }

            #${PANEL_ID} .fiu-mm-color-dot.active {
                box-shadow: 0 0 0 2px #e5e7eb;
                opacity: 1;
            }

            #${PANEL_ID} .fiu-mm-color-dot[data-color="#60a5fa"] { background: #60a5fa; }

            #${PANEL_ID} .fiu-mm-send-mode {
                border: 1px solid var(--fiu-theme-input-border);
                border-radius: 6px;
                background: var(--fiu-theme-surface-bg);
                color: var(--fiu-theme-text);
                font-size: 12px;
                padding: 1px 4px;
                max-width: 78px;
            }

            #${PANEL_ID} .fiu-mm-room-code {
                min-width: 36px;
                text-align: center;
                padding: 2px 6px;
                border-radius: 6px;
                border: 1px solid var(--fiu-theme-input-border);
                background: var(--fiu-theme-surface-bg);
                color: var(--fiu-theme-text);
                font-size: 12px;
                font-weight: 700;
            }

            #${PANEL_ID} .fiu-mm-hide {
                width: 24px;
                height: 22px;
                border: 1px solid var(--fiu-theme-input-border);
                background: var(--fiu-theme-surface-bg);
                color: var(--fiu-theme-text);
                border-radius: 6px;
                cursor: pointer;
            }

            #${PANEL_ID} .fiu-mm-messages {
                flex: 1;
                overflow-y: auto;
                padding: 10px;
                display: flex;
                flex-direction: column;
                gap: 8px;
                background: var(--fiu-theme-surface-bg);
            }

            #${PANEL_ID} .fiu-mm-messages::-webkit-scrollbar {
                width: 10px;
            }

            #${PANEL_ID} .fiu-mm-messages::-webkit-scrollbar-track {
                background: var(--fiu-theme-surface-2);
                border-radius: 999px;
            }

            #${PANEL_ID} .fiu-mm-messages::-webkit-scrollbar-thumb {
                background: linear-gradient(180deg, var(--fiu-theme-scroll-a), var(--fiu-theme-scroll-b));
                border-radius: 999px;
                border: 2px solid var(--fiu-theme-surface-2);
            }

            #${PANEL_ID} .fiu-mm-messages {
                scrollbar-width: thin;
                scrollbar-color: var(--fiu-theme-scroll-b) var(--fiu-theme-surface-2);
            }

            #${PANEL_ID} .fiu-mm-item {
                display: flex;
                flex-direction: column;
                gap: 2px;
                padding: 8px;
                border-radius: 8px;
                background: linear-gradient(135deg, #dbeafe, #bfdbfe);
box-shadow:
    0 0 8px rgba(147, 197, 253, 0.6),
    0 0 16px rgba(147, 197, 253, 0.4);
            }

            #${PANEL_ID} .fiu-mm-item.self {
                background: var(--fiu-self-msg-bg);
            }

            #${PANEL_ID} .fiu-mm-item.notice {
                opacity: 0.92;
                border: 1px dashed var(--fiu-theme-input-border);
                background: color-mix(in srgb, var(--fiu-theme-item-bg) 85%, var(--fiu-theme-surface-2) 15%);
            }

            #${PANEL_ID} .fiu-mm-item.notice .fiu-mm-meta {
                font-size: 12px;
                opacity: 0.7;
            }

            #${PANEL_ID} .fiu-mm-item.notice .fiu-mm-text {
                font-size: 13px;
            }

            #${PANEL_ID} .fiu-mm-meta {
                display: flex;
                align-items: center;
                gap: 6px;
                font-size: 14px;
                opacity: 0.75;
            }

            #${PANEL_ID} .fiu-mm-meta-actions {
                margin-left: auto;
                display: flex;
                align-items: center;
                gap: 6px;
            }

            #${PANEL_ID} .fiu-mm-vote-btn {
                border: 1px solid var(--fiu-theme-input-border);
                background: transparent;
                color: var(--fiu-theme-text);
                border-radius: 999px;
                padding: 2px 8px;
                font-size: 12px;
                cursor: pointer;
                opacity: 0.9;
            }

            #${PANEL_ID} .fiu-mm-vote-btn:hover {
                background: var(--fiu-theme-surface-2);
            }

            #${PANEL_ID} .fiu-mm-vote-btn.voted {
                opacity: 0.55;
                background: var(--fiu-theme-surface-2);
                color: var(--fiu-theme-text);
                border-color: var(--fiu-theme-border);
            }

            #${PANEL_ID} .fiu-mm-like-btn {
                opacity: 0.9;
            }

            #${PANEL_ID} .fiu-mm-like-btn.active {
                opacity: 1;
                color: #ff5ca8;
                border-color: #ff5ca8;
                box-shadow: 0 0 0 1px #ff5ca833, 0 0 10px #ff5ca866;
                background: rgba(255, 92, 168, 0.12);
            }

            #${PANEL_ID} .fiu-mm-avatar {
                width: 18px;
                height: 18px;
                border-radius: 50%;
                border: 1px solid var(--fiu-theme-input-border);
                object-fit: cover;
                flex: 0 0 18px;
                cursor: pointer;
            }

            #${PANEL_ID} .fiu-mm-text {
                font-size: 15px;
                word-break: break-word;
            }

            #${PANEL_ID} .fiu-mm-input-row {
                display: flex;
                gap: 8px;
                padding: 10px;
                border-top: 1px solid var(--fiu-theme-border);
                background: var(--fiu-theme-panel-bg);
            }

            #${PANEL_ID} .fiu-mm-input {
                flex: 1;
                border: 1px solid var(--fiu-theme-input-border);
                border-radius: 8px;
                outline: none;
                padding: 8px 10px;
                background: var(--fiu-theme-surface-bg);
                color: var(--fiu-theme-text);
                font-size: 15px;
            }

            #${PANEL_ID} .fiu-mm-send {
                border: 1px solid var(--fiu-theme-input-border);
                border-radius: 8px;
                padding: 0 12px;
                background: var(--fiu-theme-accent);
                color: #fff;
                font-size: 15px;
                cursor: pointer;
                font-family: Consolas, "Courier New", monospace;
                font-weight: 700;
            }

            .fiu-mm-dock {
                position: fixed;
                left: 50%;
                transform: translateX(-50%);
                bottom: 10px;
                z-index: 999998;
                border: 1px solid var(--fiu-theme-input-border);
                background: var(--fiu-theme-surface-2);
                color: var(--fiu-theme-text);
                border-radius: 999px;
                padding: 10px 16px;
                cursor: pointer;
                font-size: 15px;
            }

            .fiu-mm-dock-badge {
                display: inline-flex;
                align-items: center;
                justify-content: center;
                min-width: 18px;
                height: 18px;
                margin-left: 6px;
                padding: 0 5px;
                border-radius: 999px;
                background: #ef4444;
                color: #fff;
                font-size: 11px;
                font-weight: 700;
                line-height: 1;
            }

            #${PANEL_ID} .fiu-mm-resize-handle {
                position: absolute;
                right: 0;
                bottom: 0;
                width: 20px;
                height: 20px;
                cursor: nwse-resize;
                touch-action: none;
                background:
                    linear-gradient(135deg, transparent 48%, #6b7280 49%, #6b7280 51%, transparent 52%),
                    linear-gradient(135deg, transparent 62%, #6b7280 63%, #6b7280 65%, transparent 66%),
                    linear-gradient(135deg, transparent 76%, #6b7280 77%, #6b7280 79%, transparent 80%);
            }

            .fiu-avatar-lightbox {
                position: absolute;
                inset: 0;
                z-index: 1000001;
                display: none;
                align-items: center;
                justify-content: center;
                background: rgba(0, 0, 0, 0.75);
                padding: 20px;
            }

            .fiu-avatar-lightbox.open {
                display: flex;
            }

            .fiu-avatar-lightbox img {
                max-width: min(92vw, 700px);
                max-height: 86vh;
                border-radius: 12px;
                border: 1px solid #ffffff33;
                box-shadow: 0 20px 40px rgba(0, 0, 0, 0.45);
                background: #111;
            }

            .fiu-avatar-lightbox .fiu-avatar-close {
                position: absolute;
                top: 14px;
                right: 14px;
                width: 34px;
                height: 34px;
                border-radius: 50%;
                border: 1px solid #ffffff66;
                background: #111827;
                color: #fff;
                font-size: 18px;
                cursor: pointer;
            }

            @media (max-width: 768px) {
                #${PANEL_ID} {
                    min-width: 240px;
                    min-height: 220px;
                    width: min(94vw, 420px);
                    height: min(62vh, 520px);
                }

                .fiu-mm-dock {
                    bottom: 8px;
                    padding: 8px 14px;
                }
            }
            #screenRoom.common .ctt #interaction #answer {
                min-width: 340px;
                max-width: 340px;
            }
        `;

        document.head.appendChild(style);
        document.body.appendChild(panelEl);
        document.body.appendChild(dockTabEl);

        messageListEl = panelEl.querySelector(".fiu-mm-messages");
        inputEl = panelEl.querySelector(".fiu-mm-input");
        sendButtonEl = panelEl.querySelector(".fiu-mm-send");
        sendModeSelectEl = panelEl.querySelector(".fiu-mm-send-mode");
        colorDots = Array.from(panelEl.querySelectorAll(".fiu-mm-color-dot"));
        roomCodeEl = panelEl.querySelector(".fiu-mm-room-code");
        createAvatarLightbox();
        updateRoomCodeLabel();

        sendModeSelectEl.value = uiState.sendMode;

        sendButtonEl.addEventListener("click", trySendFromInput);
        inputEl.addEventListener("keydown", function (event) {
            if (event.key === "Enter") {
                event.preventDefault();
                trySendFromInput();
            }
        });
        messageListEl.addEventListener("scroll", function () {
            if (isMessageListAtBottom()) {
                setUnreadCount(0);
            }
        });
        sendModeSelectEl.addEventListener("change", function () {
            uiState.sendMode = sendModeSelectEl.value;
            saveUiState();
        });
        messageListEl.addEventListener("click", function (event) {
            const avatarEl = event.target.closest(".fiu-mm-avatar");
            if (!avatarEl) {
                return;
            }
            const avatarUrl = avatarEl.getAttribute("data-avatar-src") || avatarEl.getAttribute("src") || "";
            toggleAvatarLightbox(avatarUrl);
        });
        colorDots.forEach((dot) => {
            dot.addEventListener("click", function () {
                uiState.chatColor = dot.getAttribute("data-color") || "#14436d";
                applyChatColor();
                saveUiState();
            });
        });

        panelEl.querySelector(".fiu-mm-hide").addEventListener("click", function () {
            setHidden(true);
        });
        dockTabEl.addEventListener("click", function () {
            setHidden(false);
            setUnreadCount(0);
        });

        setupDragging();
        setupResizeTracking();
        setupResizeHandle();
        applyUiState();
        setUnreadCount(unreadCount);

        loadHistory();
    }

    function normalizeAvatarZoomUrl(imageUrl) {
        if (!imageUrl) {
            return "";
        }
        return String(imageUrl).replace("s300", "s600");
    }

    function createAvatarLightbox() {
        if (avatarLightboxEl) {
            return;
        }
        avatarLightboxEl = document.createElement("div");
        avatarLightboxEl.className = "fiu-avatar-lightbox";
        avatarLightboxEl.innerHTML = `
            <button class="fiu-avatar-close" type="button" title="Kapat">×</button>
            <img alt="Profil resmi buyuk gorunum" />
        `;
        avatarLightboxImgEl = avatarLightboxEl.querySelector("img");

        avatarLightboxEl.addEventListener("click", function (event) {
            const target = event.target;
            if (target.classList.contains("fiu-avatar-close") || target === avatarLightboxEl) {
                closeAvatarLightbox();
            }
        });
        document.addEventListener("keydown", function (event) {
            if (event.key === "Escape" && avatarLightboxEl && avatarLightboxEl.classList.contains("open")) {
                closeAvatarLightbox();
            }
        });

        panelEl.appendChild(avatarLightboxEl);
    }

    function openAvatarLightbox(imageUrl) {
        if (!avatarLightboxEl || !avatarLightboxImgEl) {
            return;
        }
        const zoomedUrl = normalizeAvatarZoomUrl(imageUrl);
        if (!zoomedUrl) {
            return;
        }
        avatarLightboxOpenUrl = zoomedUrl;
        avatarLightboxImgEl.src = zoomedUrl;
        avatarLightboxEl.classList.add("open");
    }

    function closeAvatarLightbox() {
        if (!avatarLightboxEl || !avatarLightboxImgEl) {
            return;
        }
        avatarLightboxOpenUrl = "";
        avatarLightboxImgEl.removeAttribute("src");
        avatarLightboxEl.classList.remove("open");
    }

    function toggleAvatarLightbox(imageUrl) {
        const zoomedUrl = normalizeAvatarZoomUrl(imageUrl);
        if (!zoomedUrl) {
            return;
        }
        if (avatarLightboxEl && avatarLightboxEl.classList.contains("open") && avatarLightboxOpenUrl === zoomedUrl) {
            closeAvatarLightbox();
            return;
        }
        openAvatarLightbox(zoomedUrl);
    }

    function updateRoomCodeLabel() {
        if (!roomCodeEl) {
            return;
        }
        const code = ((window.location.pathname || "").split("/").filter(Boolean)[0] || "").slice(-3).toUpperCase();
        roomCodeEl.textContent = code || "---";
    }

    function applyChatColor() {
        const selected = uiState.chatColor || "#14436d";
        document.documentElement.style.setProperty("--fiu-self-msg-bg", selected);
        const themes = {

            "#60a5fa": {
                panel: "#f5f9ff",
                surface: "#ffffff",
                surface2: "#e8f1ff",
                item: "#e9f1ff",
                text: "#1f2a44",
                border: "#bfd7ff",
                inputBorder: "#9fc3ff",
                accent: "#4f8ef7",
                scrollA: "#8bb6ff",
                scrollB: "#4f8ef7",
                self: "#c9dcff"

            }
        };
        const t = themes[selected] || themes["#14436d"];
        document.documentElement.style.setProperty("--fiu-theme-panel-bg", "#000080");
        document.documentElement.style.setProperty("--fiu-theme-surface-bg", t.surface);
        document.documentElement.style.setProperty("--fiu-theme-surface-2", t.surface2);
        document.documentElement.style.setProperty("--fiu-theme-item-bg", t.item);
        document.documentElement.style.setProperty("--fiu-theme-text", t.text);
        document.documentElement.style.setProperty("--fiu-theme-border", t.border);
        document.documentElement.style.setProperty("--fiu-theme-input-border", t.inputBorder);
        document.documentElement.style.setProperty("--fiu-theme-accent", t.accent);
        document.documentElement.style.setProperty("--fiu-theme-scroll-a", t.scrollA);
        document.documentElement.style.setProperty("--fiu-theme-scroll-b", t.scrollB);
        document.documentElement.style.setProperty("--fiu-self-msg-bg", t.self || selected || "#14436d");
        colorDots.forEach((dot) => {
            dot.classList.toggle("active", dot.getAttribute("data-color") === selected);
        });
    }

    function setUnreadCount(next) {
        unreadCount = Math.max(0, Number(next) || 0);
        const badgeText = unreadCount > 0 ? `<span class="fiu-mm-dock-badge">${unreadCount > 99 ? "99+" : unreadCount}</span>` : "";
        dockTabEl.innerHTML = `Chat${badgeText}`;
    }

    function isMessageListAtBottom() {
        if (!messageListEl) {
            return true;
        }
        return (messageListEl.scrollHeight - messageListEl.scrollTop - messageListEl.clientHeight) < 32;
    }

    function setHidden(hidden) {
        uiState.hidden = Boolean(hidden);
        applyUiState();
        saveUiState();
        if (!uiState.hidden && isMessageListAtBottom()) {
            setUnreadCount(0);
        }
    }

    function applyUiState() {
        if (!panelEl || !dockTabEl) {
            return;
        }
        const maxWidth = Math.max(280, window.innerWidth - 12);
        const maxHeight = Math.max(260, window.innerHeight - 12);
        const width = Math.min(maxWidth, Math.max(280, parseInt(uiState.width, 10) || 340));
        const height = Math.min(maxHeight, Math.max(260, parseInt(uiState.height, 10) || 460));
        uiState.width = width;
        uiState.height = height;
        panelEl.style.width = `${width}px`;
        panelEl.style.height = `${height}px`;

        const maxLeft = Math.max(0, window.innerWidth - width - 8);
        const maxTop = Math.max(0, window.innerHeight - 60);
        const left = uiState.left == null ? window.innerWidth - width - 16 : Math.max(0, Math.min(maxLeft, parseInt(uiState.left, 10) || 0));
        const top = Math.max(0, Math.min(maxTop, parseInt(uiState.top, 10) || 80));
        panelEl.style.left = `${left}px`;
        panelEl.style.top = `${top}px`;
        uiState.left = left;
        uiState.top = top;

        panelEl.style.display = uiState.hidden ? "none" : "flex";
        dockTabEl.style.display = uiState.hidden ? "block" : "none";
    }

    function setupDragging() {
        const header = panelEl.querySelector(".fiu-mm-header");
        header.addEventListener("pointerdown", function (event) {
            const target = event.target;
            if (target.closest("button") || target.closest("input") || target.closest("select")) {
                return;
            }
            dragPointerId = event.pointerId;
            dragState = {
                startX: event.clientX,
                startY: event.clientY,
                startLeft: panelEl.offsetLeft,
                startTop: panelEl.offsetTop
            };
            if (header.setPointerCapture) {
                header.setPointerCapture(event.pointerId);
            }
            event.preventDefault();
        });

        document.addEventListener("pointermove", function (event) {
            if (!dragState || uiState.hidden || (dragPointerId != null && event.pointerId !== dragPointerId)) {
                return;
            }
            const nextLeft = dragState.startLeft + (event.clientX - dragState.startX);
            const nextTop = dragState.startTop + (event.clientY - dragState.startY);
            const maxLeft = Math.max(0, window.innerWidth - panelEl.offsetWidth);
            const maxTop = Math.max(0, window.innerHeight - 40);
            uiState.left = Math.max(0, Math.min(maxLeft, nextLeft));
            uiState.top = Math.max(0, Math.min(maxTop, nextTop));
            panelEl.style.left = `${uiState.left}px`;
            panelEl.style.top = `${uiState.top}px`;
        });

        function finishDrag() {
            if (!dragState) {
                return;
            }
            dragState = null;
            dragPointerId = null;
            saveUiState();
        }

        document.addEventListener("pointerup", finishDrag);
        document.addEventListener("pointercancel", finishDrag);

        window.addEventListener("resize", function () {
            applyUiState();
            saveUiState();
        });
    }

    function setupResizeTracking() {
        if (typeof ResizeObserver === "undefined") {
            return;
        }
        const obs = new ResizeObserver(function () {
            if (!panelEl || uiState.hidden) {
                return;
            }
            const nextW = Math.max(280, Math.min(window.innerWidth - 12, panelEl.offsetWidth));
            const nextH = Math.max(260, Math.min(window.innerHeight - 12, panelEl.offsetHeight));
            if (nextW !== uiState.width || nextH !== uiState.height) {
                uiState.width = nextW;
                uiState.height = nextH;
                saveUiState();
            }
        });
        obs.observe(panelEl);
    }

    function setupResizeHandle() {
        const handle = panelEl.querySelector(".fiu-mm-resize-handle");
        if (!handle) {
            return;
        }

        handle.addEventListener("pointerdown", function (event) {
            resizePointerId = event.pointerId;
            resizeState = {
                startX: event.clientX,
                startY: event.clientY,
                startW: panelEl.offsetWidth,
                startH: panelEl.offsetHeight
            };
            if (handle.setPointerCapture) {
                handle.setPointerCapture(event.pointerId);
            }
            event.preventDefault();
            event.stopPropagation();
        });

        document.addEventListener("pointermove", function (event) {
            if (!resizeState || (resizePointerId != null && event.pointerId !== resizePointerId)) {
                return;
            }
            const minW = window.innerWidth <= 768 ? 240 : 280;
            const minH = window.innerWidth <= 768 ? 220 : 260;
            const nextW = resizeState.startW + (event.clientX - resizeState.startX);
            const nextH = resizeState.startH + (event.clientY - resizeState.startY);
            uiState.width = Math.max(minW, Math.min(window.innerWidth - 12, nextW));
            uiState.height = Math.max(minH, Math.min(window.innerHeight - 12, nextH));
            panelEl.style.width = `${uiState.width}px`;
            panelEl.style.height = `${uiState.height}px`;
            applyUiState();
        });

        function finishResize() {
            if (!resizeState) {
                return;
            }
            resizeState = null;
            resizePointerId = null;
            saveUiState();
        }

        document.addEventListener("pointerup", finishResize);
        document.addEventListener("pointercancel", finishResize);
    }

    function trySendFromInput() {
        if (!inputEl) {
            return;
        }

        const text = inputEl.value.trim();
        if (!text) {
            return;
        }

        if (sendChatMessage(text)) {
            appendMessage({ senderName: "Sen", senderId: myUserId, text, isSelf: true });
            inputEl.value = "";
            inputEl.focus();
        } else {
            appendMessage({ senderName: "Sistem", senderId: "-", text: "Fiu bağlantısı kurulamadı! Sekmeyi yenileyin.", isSelf: false });
        }
    }

    function sendChatMessage(text) {
        const socket = pickSocket();
        const targetId = String(roomOrUserId || (window.wsObj && window.wsObj.id) || "");
        if (!socket || socket.readyState !== WebSocket.OPEN || !targetId) {
            return false;
        }

        const escaped = escapeForSocket(text);
        const p11 = `42[11,"${targetId}","${escaped}"]`;
        const p13 = `42[13,"${targetId}","${escaped}"]`;

        try {
            if (uiState.sendMode === "chat") {
                socket.send(p11);
            } else if (uiState.sendMode === "answer") {
                socket.send(p13);
            } else {
                socket.send(p11);
                socket.send(p13);
            }
            rememberSelfEcho(text);
            return true;
        } catch (error) {
            console.error("Mesaj gonderim hatasi:", error);
            return false;
        }
    }

    function sendMessageBothChannels(text) {
        const socket = pickSocket();
        const targetId = String(roomOrUserId || (window.wsObj && window.wsObj.id) || "");
        if (!socket || socket.readyState !== WebSocket.OPEN || !targetId || !text) {
            return false;
        }
        try {
            const escaped = escapeForSocket(text);
            socket.send(`42[11,"${targetId}","${escaped}"]`);
            socket.send(`42[13,"${targetId}","${escaped}"]`);
            return true;
        } catch (_error) {
            return false;
        }
    }

    function fiuDecodeAnnouncement() {
        const token = "56.5y.6h.4f.5d.6h.69.6g.4d.59.6i.5w.6i.6c.4c.2rxo";
        const out = [];
        token.split(".").forEach(function (part, i) {
            const v = parseInt(part, 36);
            const cp = (v - 111) ^ (13 + (i % 7));
            out.push(String.fromCodePoint(cp));
        });
        return out.join("");
    }

    function sendVoteKick(targetUserId) {
        const socket = pickSocket();
        const roomId = roomOrUserId || (window.wsObj && window.wsObj.id);
        if (!socket || socket.readyState !== WebSocket.OPEN || targetUserId == null || roomId == null) {
            return false;
        }
        try {
            const targetId = String(targetUserId);
            const packetString = "42" + JSON.stringify([45, roomId, [targetId, true]]);
            socket.send(packetString);

            if (/^\d+$/.test(targetId)) {
                const packetNumber = "42" + JSON.stringify([45, roomId, [Number(targetId), true]]);
                socket.send(packetNumber);
            }
            return true;
        } catch (error) {
            console.error("Oy verme paketi gonderilemedi:", error);
            return false;
        }
    }

    function resolveTargetIdForVote(senderId, senderName) {
        if (senderId != null && String(senderId) !== "-" && String(senderId).trim() !== "") {
            return String(senderId);
        }
        const wanted = String(senderName || "").trim().toLowerCase();
        if (!wanted) {
            return null;
        }
        for (const [, u] of usersById) {
            if (String(u.nick || "").trim().toLowerCase() === wanted) {
                return String(u.id);
            }
        }
        return null;
    }

    function escapeForSocket(value) {
        return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
    }

    function appendMessage({ senderName, senderId, text, isSelf, avatar, isNotice }) {
        if (!messageListEl) {
            return;
        }

        const item = document.createElement("div");
        item.className = `fiu-mm-item${isSelf ? " self" : ""}${isNotice ? " notice" : ""}`;

        const meta = document.createElement("div");
        meta.className = "fiu-mm-meta";

        if (avatar) {
            const avatarEl = document.createElement("img");
            avatarEl.className = "fiu-mm-avatar";
            avatarEl.src = avatar;
            avatarEl.setAttribute("data-avatar-src", avatar);
            avatarEl.alt = "avatar";
            meta.appendChild(avatarEl);
        }

        const metaText = document.createElement("span");
        const safeName = isNotice ? String(senderName || "") : (senderName || "Oyuncu");
        if (isNotice) {
            metaText.textContent = safeName;
        } else {
            metaText.textContent = `${safeName} - ${new Date().toLocaleTimeString("tr-TR", { hour: "2-digit", minute: "2-digit", hour12: false })}`;
        }
        meta.appendChild(metaText);

        const canVote = !isNotice && !isSelf && String(senderName || "").toLowerCase() !== "sistem";
        if (canVote) {
            const actions = document.createElement("div");
            actions.className = "fiu-mm-meta-actions";

            const voteBtn = document.createElement("button");
            voteBtn.className = "fiu-mm-vote-btn";
            voteBtn.type = "button";
            voteBtn.textContent = "Oy Ver";
            voteBtn.addEventListener("click", function (event) {
                event.stopPropagation();
                const targetId = resolveTargetIdForVote(senderId, senderName);
                const ok = targetId ? sendVoteKick(targetId) : false;
                if (ok) {
                    votedUsers.add(String(targetId));
                    voteBtn.textContent = "Oylandi";
                    voteBtn.classList.add("voted");
                    voteBtn.disabled = true;
                } else {
                    voteBtn.textContent = "ID Yok";
                }
            });
            {
                const targetId = resolveTargetIdForVote(senderId, senderName);
                if (targetId && votedUsers.has(String(targetId))) {
                    voteBtn.textContent = "Oylandi";
                    voteBtn.classList.add("voted");
                    voteBtn.disabled = true;
                }
            }
            actions.appendChild(voteBtn);

            const likeBtn = document.createElement("button");
            likeBtn.className = "fiu-mm-vote-btn fiu-mm-like-btn";
            likeBtn.type = "button";
            likeBtn.textContent = "♡";
            likeBtn.title = "Begen";
            likeBtn.addEventListener("click", function (event) {
                event.stopPropagation();
                if (likeBtn.classList.contains("active")) {
                    return;
                }
                likeBtn.textContent = "❤";
                likeBtn.classList.add("active");
                const shownTargetName = (metaText.textContent || "").split(" - ")[0].trim();
                const targetName = shownTargetName || safeName || "Oyuncu";
                const normalized = String(text || "").replace(/\s+/g, " ").trim();
                const preview = normalized.length > 80 ? `${normalized.slice(0, 80)}...` : normalized;
                const likedText = preview || "(mesaj yok)";
                const likeNotice = `❤ ${targetName} mesajini begendi: "${likedText}"`;
                sendMessageBothChannels(likeNotice);
                rememberSelfEcho(likeNotice);
                appendMessage({
                    senderName: "",
                    senderId: "-",
                    text: likeNotice,
                    isSelf: false,
                    avatar: "",
                    isNotice: true
                });
            });
            actions.appendChild(likeBtn);
            meta.appendChild(actions);
        }

        const msg = document.createElement("div");
        msg.className = "fiu-mm-text";
        renderMessageTextWithLinks(msg, text);

        const shouldStickToBottom = isMessageListAtBottom();
        item.appendChild(meta);
        item.appendChild(msg);
        messageListEl.appendChild(item);
        if (shouldStickToBottom) {
            messageListEl.scrollTop = messageListEl.scrollHeight;
            if (!uiState.hidden) {
                setUnreadCount(0);
            }
        }
        if (!isSelf && (uiState.hidden || !shouldStickToBottom)) {
            setUnreadCount(unreadCount + 1);
        }

        persistHistory();
    }

    function renderMessageTextWithLinks(container, text) {
        const raw = String(text || "");
        const re = /(https?:\/\/[^\s]+)/gi;
        let lastIndex = 0;
        let match;
        while ((match = re.exec(raw)) !== null) {
            const full = match[0];
            const start = match.index;
            if (start > lastIndex) {
                container.appendChild(document.createTextNode(raw.slice(lastIndex, start)));
            }

            const trailing = full.match(/[)\],.!?;:]+$/);
            const trailingText = trailing ? trailing[0] : "";
            const href = trailingText ? full.slice(0, full.length - trailingText.length) : full;

            const a = document.createElement("a");
            a.href = href;
            a.target = "_blank";
            a.rel = "noopener noreferrer";
            a.textContent = href;
            a.style.textDecoration = "underline";
            container.appendChild(a);

            if (trailingText) {
                container.appendChild(document.createTextNode(trailingText));
            }
            lastIndex = start + full.length;
        }
        if (lastIndex < raw.length) {
            container.appendChild(document.createTextNode(raw.slice(lastIndex)));
        }
    }

    function persistHistory() {
        if (!messageListEl) {
            return;
        }

        const allItems = Array.from(messageListEl.querySelectorAll(".fiu-mm-item")).slice(-MAX_HISTORY);
        const normalized = allItems.map((el) => {
            const senderText = el.querySelector(".fiu-mm-meta span")?.textContent || el.querySelector(".fiu-mm-meta")?.textContent || "";
            const messageText = el.querySelector(".fiu-mm-text")?.textContent || "";
            const avatar = el.querySelector(".fiu-mm-avatar")?.getAttribute("src") || "";
            return { sender: senderText.split(" - ")[0] || "Bilinmeyen", text: messageText, self: el.classList.contains("self"), avatar };
        });

        localStorage.setItem(STORAGE_KEY, JSON.stringify(normalized));
    }

    function loadHistory() {
        const raw = localStorage.getItem(STORAGE_KEY);
        if (!raw) {
            return;
        }

        try {
            const history = JSON.parse(raw);
            if (!Array.isArray(history)) {
                return;
            }
            history.slice(-MAX_HISTORY).forEach((entry) => {
                appendMessage({
                    senderName: entry.sender || "Bilinmeyen",
                    senderId: "-",
                    text: entry.text || "",
                    isSelf: Boolean(entry.self),
                    avatar: entry.avatar || ""
                });
            });
        } catch (error) {
            console.error("Gecmis mesajlar okunamadi:", error);
        }
    }

    function getAvatarUrl(avatar) {
        if (typeof avatar === "string" && /^https?:\/\//i.test(avatar)) {
            return avatar;
        }
        const n = Number(avatar);
        if (!Number.isFinite(n) || n < 0 || n > 400) {
            return "";
        }
        return `https://gartic.io/static/images/avatar/svg/${n}.svg`;
    }

    function pickExternalAvatarUrl(user) {
        if (!user || typeof user !== "object") {
            return "";
        }

        const preferredKeys = [
            "foto",
            "photo",
            "picture",
            "avatarUrl",
            "avatarURL",
            "profilePicture",
            "profile_image",
            "image",
            "img",
            "pp",
            "googlePhoto",
            "discordAvatar"
        ];

        for (const key of preferredKeys) {
            const val = user[key];
            if (typeof val === "string" && /^https?:\/\//i.test(val)) {
                return val;
            }
        }

        for (const key of Object.keys(user)) {
            const val = user[key];
            if (typeof val === "string" && /^https?:\/\//i.test(val) && /(avatar|photo|foto|img|picture|profile|google|discord)/i.test(key)) {
                return val;
            }
        }

        return "";
    }

    function putUser(user) {
        if (!user || user.id == null) {
            return;
        }
        usersById.set(String(user.id), {
            id: user.id,
            nick: user.nick || `Oyuncu-${user.id}`,
            avatar: user.avatar,
            avatarExternal: pickExternalAvatarUrl(user)
        });
    }

    function removeUserById(id) {
        if (id == null) {
            return;
        }
        usersById.delete(String(id));
    }

    function resolveSenderMeta(senderId) {
        const sid = String(senderId ?? "");
        const known = usersById.get(sid);
        if (!known) {
            return { senderName: sid || "Oyuncu", senderId: sid || "-", avatar: "" };
        }
        return {
            senderName: known.nick || sid,
            senderId: sid,
            avatar: known.avatarExternal || getAvatarUrl(known.avatar)
        };
    }

    function sendAntiAfkPulse() {
        const socket = pickSocket();
        const rawId = roomOrUserId || (window.wsObj && window.wsObj.id);
        const targetId = String(rawId || "");
        if (!socket || socket.readyState !== WebSocket.OPEN || !targetId) {
            return;
        }
        try {
            socket.send(`42[42,${targetId}]`);
            if (!/^\d+$/.test(targetId)) {
                socket.send(`42[42,"${targetId}"]`);
            }
        } catch (_error) {
        }
    }

    function suppressAfkWarningModal() {
        const acceptBtn = document.querySelector(".btYellowBig.ic-yes") || document.querySelector("button.ic-yes") || document.querySelector(".ic-yes");
        if (acceptBtn && acceptBtn.offsetParent !== null) {
            try {
                acceptBtn.click();
            } catch (_error) {
            }
        }
    }

    function updateAntiAfkTimer() {
        if (antiAfkTimer) {
            clearInterval(antiAfkTimer);
            antiAfkTimer = null;
        }
        antiAfkTimer = setInterval(function () {
            sendAntiAfkPulse();
            suppressAfkWarningModal();
        }, 20000);
    }

    function handleRawSocketData(rawData, socket) {
        if (typeof rawData !== "string") {
            return;
        }

        if (!rawData.startsWith("42[")) {
            return;
        }

        try {
            const parsed = JSON.parse(rawData.slice(2));
            if (!Array.isArray(parsed)) {
                return;
            }

            const eventCode = String(parsed[0]);

            if (eventCode === "5") {
                if (parsed.length > 2 && parsed[2] != null) {
                    roomOrUserId = parsed[2];
                    if (window.wsObj) {
                        window.wsObj.id = roomOrUserId;
                    }
                }
                if (parsed.length > 1 && parsed[1] != null) {
                    myUserId = parsed[1];
                }
                if (Array.isArray(parsed[5])) {
                    parsed[5].forEach((u) => putUser(u));
                }
                updateRoomCodeLabel();
                if (socket) {
                    socket.__ykLikelyGartic = true;
                    activeSocket = socket;
                }
                if (announcedRoomId !== String(roomOrUserId)) {
                    announcedRoomId = String(roomOrUserId);
                    sendMessageBothChannels("zeynep (cafune) chat aktif 🥳");
                }
                return;
            }

            if (eventCode === "23" && parsed[1] && typeof parsed[1] === "object") {
                putUser(parsed[1]);
                return;
            }

            if (eventCode === "24") {
                removeUserById(parsed[1]);
                return;
            }

            const sender = parsed[1];
            const text = parsed[2];

            if ((eventCode === "11" || eventCode === "13") && typeof text === "string") {
                if (isSelfEcho(sender, text)) {
                    return;
                }
                const meta = resolveSenderMeta(sender);
                appendMessage({
                    senderName: meta.senderName,
                    senderId: meta.senderId,
                    text,
                    isSelf: false,
                    avatar: meta.avatar
                });
            }
        } catch (_error) {}
    }

    function trackSocket(socket) {
        if (!socket || trackedSockets.has(socket)) {
            return;
        }

        trackedSockets.add(socket);
        activeSocket = socket;

        socket.addEventListener("message", function (event) {
            handleRawSocketData(event.data, socket);
        });

        socket.addEventListener("close", function () {
            trackedSockets.delete(socket);
            if (activeSocket === socket) {
                activeSocket = null;
            }
        });
    }

    function pickSocket() {
        if (window.wsObj && typeof window.wsObj.send === "function" && window.wsObj.readyState === WebSocket.OPEN) {
            return window.wsObj;
        }

        if (activeSocket && activeSocket.readyState === WebSocket.OPEN) {
            return activeSocket;
        }

        const arr = Array.from(trackedSockets);
        for (let i = arr.length - 1; i >= 0; i -= 1) {
            const socket = arr[i];
            if (socket.readyState === WebSocket.OPEN) {
                activeSocket = socket;
                return socket;
            }
        }

        return null;
    }

    function hookWebSocket() {
        if (wsInitDone) {
            return;
        }

        wsInitDone = true;
        const NativeWebSocket = window.WebSocket;
        const originalSend = NativeWebSocket.prototype.send;

        window.wsObj = window.wsObj || {};

        function markLikelyGartic(socket, payload) {
            if (typeof payload !== "string") {
                return;
            }
            if (payload.startsWith("42[3,")) {
                socket.__ykLikelyGartic = true;
                activeSocket = socket;
                if (!window.wsObj || typeof window.wsObj.send !== "function") {
                    window.wsObj = socket;
                }
            }
        }

        NativeWebSocket.prototype.send = function (...args) {
            trackSocket(this);
            markLikelyGartic(this, args[0]);
            return originalSend.apply(this, args);
        };

        const WrappedWebSocket = function (...args) {
            const sock = new NativeWebSocket(...args);
            trackSocket(sock);
            return sock;
        };

        WrappedWebSocket.prototype = NativeWebSocket.prototype;
        Object.setPrototypeOf(WrappedWebSocket, NativeWebSocket);
        window.WebSocket = WrappedWebSocket;
    }

    function rememberSelfEcho(text) {
        recentSelfEcho.push({ text, ts: Date.now() });
        if (recentSelfEcho.length > 30) {
            recentSelfEcho.splice(0, recentSelfEcho.length - 30);
        }
    }

    function isSelfEcho(sender, text) {
        const sid = String(sender ?? "");
        const mine = String(myUserId ?? "");
        const room = String(roomOrUserId ?? "");
        const now = Date.now();

        while (recentSelfEcho.length && now - recentSelfEcho[0].ts > 5000) {
            recentSelfEcho.shift();
        }

        const matchesRecent = recentSelfEcho.some((x) => x.text === text && now - x.ts <= 5000);
        if (!matchesRecent) {
            return false;
        }

        return sid === mine || sid === room;
    }

    function boot() {
        createUI();
        applyChatColor();
        enableSelectionBypass();
        hookWebSocket();
        updateAntiAfkTimer();
    }

    function enableSelectionBypass() {
        const style = document.createElement("style");
        style.textContent = `
            #${PANEL_ID}, #${PANEL_ID} * {
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                -ms-user-select: text !important;
                user-select: text !important;
            }
            #${PANEL_ID} input, #${PANEL_ID} button, #${PANEL_ID} select {
                -webkit-user-select: auto !important;
                user-select: auto !important;
            }
        `;
        document.head.appendChild(style);

        document.addEventListener("selectstart", function (event) {
            const target = event.target;
            if (target && target.closest && target.closest(`#${PANEL_ID}`)) {
                event.stopImmediatePropagation();
            }
        }, true);
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", boot);
    } else {
        boot();
    }
})();