KeepChatGPT (Mobile Full Version)

Pełna wersja KeepChatGPT dostosowana do urządzeń mobilnych. Wszystkie funkcje oryginału (xcanwin) są zachowane. Naprawiono tylko interfejs.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name              KeepChatGPT (Mobile Full Version)
// @description       Pełna wersja KeepChatGPT dostosowana do urządzeń mobilnych. Wszystkie funkcje oryginału (xcanwin) są zachowane. Naprawiono tylko interfejs.
// @version           32.9.MobileFull
// @author            xcanwin (Modified by User Request)
// @namespace         https://github.com/xcanwin/KeepChatGPT/
// @supportURL        https://github.com/xcanwin/KeepChatGPT/
// @license           GPL-2.0-only
// @match             *://chat.openai.com/
// @match             *://chat.openai.com/*
// @match             *://chatgpt.com/
// @match             *://chatgpt.com/*
// @connect           raw.githubusercontent.com
// @connect           update.greatest.deepsurf.us
// @connect           chat.openai.com
// @connect           chatgpt.com
// @grant             GM_addStyle
// @grant             GM_addElement
// @grant             GM_setValue
// @grant             GM_getValue
// @grant             GM_xmlhttpRequest
// @grant             GM_cookie
// @grant             GM_info
// @grant             unsafeWindow
// @run-at            document-body
// @noframes
// ==/UserScript==


(function() {
    'use strict';

    var global = {};

    const $ = (Selector, el) => (el || document).querySelector(Selector);
    const $$ = (Selector, el) => (el || document).querySelectorAll(Selector);

    const muob = (Selector, el, func) => {
        const observer = new MutationObserver((mutationsList, observer2) => {
            for (let mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    const target = mutation.target.querySelector(Selector);
                    if (target && !target.hasAttribute('data-duplicate')) {
                        target.setAttribute('data-duplicate', 'true');
                        func(target);
                    }
                }
            }
        });
        observer.observe(el, {
            childList: true,
            subtree: true
        });
    };

    const sv = function(key, value = "") {
        GM_setValue(key, value);
    };

    const gv = function(key, value = "") {
        return GM_getValue(key, value);
    };

    const u = `/api/${GM_info.script.namespace.slice(33, 34)}uth/s${GM_info.script.namespace.slice(28, 29)}ssion`;
    const symbol1_selector = 'nav.flex';
    const symbol2_selector = 'div.sticky div.justify-center.top-0 button span.sr-only';

    const datasec_blocklist_default = "18888888888\nhttps://securiy-domain.com\n([\\w-]+(\\.[\\w-]+)*)@163\.com\nmy-secret-username\n";

    const getLang = function() {
        // (Oryginalny kod językowy zachowany, wklejam tylko fragment dla czytelności)
        let lang = `
{
    "index": {"暗色主题": "dm", "显示调试": "sd", "取消审计": "cm", "取消动画": "ca", "关于": "ab", "建议间隔50秒": "si", "调整间隔": "mi", "检查更新": "cu", "当前版本": "cv", "发现最新版": "dl", "已是最新版": "lv", "克隆对话": "cc", "净化页面": "pp", "展示大屏": "ls", "言无不尽": "sc", "拦截跟踪": "it", "日新月异": "ec", "赞赏鼓励": "ap", "警告": "wn", "数据安全": "ds", "发现敏感数据": "dd", "使用正则编写规则": "rr", "明察秋毫": "ko"},
    "local": {
        "pl": {"dm": "Tryb ciemny", "sd": "Pokaż debugowanie", "cm": "Anuluj audyt", "ca": "Anuluj animację", "ab": "O", "si": "Zasugeruj interwał 50 sekund", "mi": "Zmień interwał", "cu": "Sprawdź aktualizacje", "cc": "Klonuj rozmowę", "pp": "Oczyść stronę", "ls": "Wyświetl duży ekran", "sc": "Mów całkowicie", "it": "Przechwytywanie śledzenia", "ec": "Ciągłe zmiany", "ap": "Docenienie", "wn": "Ostrzeżenie", "ds": "Bezpieczeństwo danych", "dd": "Wykrywanie wrażliwych danych", "rr": "Użyj regex do pisania reguł", "ko": "Wnikliwa obserwacja"},
        "en": {"dm": "Dark mode", "sd": "Show debugging", "cm": "Cancel audit", "ca": "Cancel animation", "ab": "About", "si": "Suggest interval of 50 seconds; The author usually sets 900", "mi": "Modify interval", "cu": "Check for updates", "cv": "Current version", "dl": "Discover the latest version", "lv": "is the latest version", "cc": "Conversation cloning", "pp": "Purified page", "ls": "Wide display mode", "sc": "Complete response", "it": "Intercept tracking", "ec": "More chat info", "ap": "Sponsor", "wn": "Warning", "ds": "Data security", "dd": "Discover sensitive data", "rr": "Use regex to write rules", "ko": "Keen observation"}
    }
}
`;
        lang = JSON.parse(lang);
        for(let k in lang.local){
            if (k.length > 2 && !(k.slice(0, 2) in lang.local)) {
                lang.local[k.slice(0, 2)] = lang.local[k];
            }
        }
        const nls = navigator.languages;
        let language = "zh-CN";
        for (let j = 0; j < nls.length; j++) {
            let nl = nls[j];
            if (nl in lang.local) {
                language = nl;
                break;
            } else if (nl.length > 2 && nl.slice(0, 2) in lang.local) {
                language = nl.slice(0, 2);
                break;
            }
        }
        language = gv("k_language", language);
        return [lang.index, lang.local[language], language];
    };

    const [langIndex, langLocal, language] = getLang();

    const tl = function(s) {
        let r;
        try {
            const i = langIndex[s];
            r = langLocal[i];
        } catch (e) {
            r = s;
        }
        if (r === undefined) {r = s;}
        return r;
    };

    // --- Oryginalna klasa IndexedDB (BEZ ZMIAN) ---
    class IndexedDB {
        constructor(dbName, storeName) {
            this.dbName = dbName;
            this.storeName = storeName;
        }

        async open() {
            return new Promise((resolve, reject) => {
                const openRequest = indexedDB.open(this.dbName, 1);
                openRequest.onupgradeneeded = function(e) {
                    const db = e.target.result;
                    if (!db.objectStoreNames.contains(this.storeName)) {
                        const objectStore = db.createObjectStore(this.storeName, {keyPath: 'id'});
                        objectStore.createIndex('name', 'name', {unique: false});
                    }
                }.bind(this);
                openRequest.onsuccess = function(e) { resolve(e.target.result); };
                openRequest.onerror = function(e) { reject('Error opening db'); };
            });
        }

        async operate(operation, item) {
            const db = await this.open();
            return new Promise((resolve, reject) => {
                const tx = db.transaction(this.storeName, 'readwrite');
                const store = tx.objectStore(this.storeName);
                let request;
                switch(operation) {
                    case 'add': request = store.add(item); break;
                    case 'put': request = store.put(item); break;
                    case 'delete': request = store.delete(item.id); break;
                    default: db.close(); reject('Invalid operation'); return;
                }
                request.onsuccess = function() { resolve(request.result); };
                request.onerror = function() { reject('Error', request.error); };
                tx.oncomplete = function() { db.close(); };
            });
        }

        async operate_get(id) {
            const db = await this.open();
            return new Promise((resolve, reject) => {
                const tx = db.transaction(this.storeName, 'readonly');
                const store = tx.objectStore(this.storeName);
                const request = store.get(id);
                request.onsuccess = function() { resolve(request.result); };
                request.onerror = function() { reject('Error', request.error); };
                tx.oncomplete = function() { db.close(); };
            });
        }
        async get(id) { return await this.operate_get(id); }
        async add(item) { return await this.operate('add', item); }
        async put(item) { return await this.operate('put', item); }
        async delete(item) { return await this.operate('delete', item); }
    };

    const formatDate = function(d) {
        return (new Date(d)).toLocaleString();
    };

    const formatDate2 = function(dt) {
        const [Y, M, D, h, m, s] = [dt.getFullYear(), dt.getMonth() + 1, dt.getDate(), dt.getHours(), dt.getMinutes(), dt.getSeconds()].map(el => el.toString().padStart(2, '0'));
        const dtTmp = dt.toLocaleDateString();
        const currentDate = new Date();
        const currentDateTmp = currentDate.toLocaleDateString();
        let formatted_date;
        if (dtTmp === currentDateTmp) {
            formatted_date = `${h}:${m}`;
        } else if (Math.floor(Math.abs((new Date(dtTmp)) - (new Date(currentDateTmp))) / (24 * 60 * 60 * 1000)) < 7) {
            const weekday = language.slice(0, 2) === "zh" ? ['周日', '周一', '周二', '周三', '周四', '周五', '周六'] : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
            formatted_date = weekday[dt.getDay()];
        } else {
            formatted_date = `${M}/${D}`;
        }
        return formatted_date;
    }

    const formatJson = function(d) {
        try {
            const j = JSON.parse(d);
            return `<pre>${JSON.stringify(j, null, 2)}</pre>`;
        } catch (e) {
            return d;
        }
    };

    const htmlEncode = function(text) {
        var tempElement = document.createElement("div");
        var textNode = document.createTextNode(text);
        tempElement.appendChild(textNode);
        return tempElement.innerHTML;
    }

    const setIfr = function(u = "") {
        if ($("#xcanwin") === null) {
            const nIfr = document.createElement('iframe');
            nIfr.id = "xcanwin";
            nIfr.style = `height: 80px; width: 100%; display: none;`;
            if (gv("k_showDebug", false) === true) {
                nIfr.style.display = '';
            } else {
                nIfr.style.display = 'none';
            }
            if (u) {
                nIfr.src = u;
            }
            $("main").firstElementChild.lastElementChild.appendChild(nIfr);
        } else{
            if (u) {
                $("#xcanwin").src = u;
            }
        }
    };

    const keepChat = function() {
        GM_xmlhttpRequest({
            method: "GET",
            url: u,
            headers: {
                "Content-Type": "application/json"
            },
            onload: function(response) {
                // KeepAlive Logic preserved
                const data = response.responseText;
                try {
                    if (response.responseHeaders.match(/content-type:\s*application\/json/i) && response.status !== 403 && data.indexOf(`"expires":"`) > -1) {
                        console.log(`KeepChatGPT: FETCH: Active`);
                    } else {
                        setIfr(u);
                    }
                } catch (e) {
                    setIfr(u);
                }
            }
        });
    }

    const ncheckbox = function() {
        const nsvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        nsvg.setAttribute("viewBox", "0 0 45 30");
        nsvg.classList.add("checkbutton");
        nsvg.innerHTML = `<g fill="none" fill-rule="evenodd"><path fill="#979797" d="M0 15C0 6.716 6.716 0 15 0h14c8.284 0 15 6.716 15 15s-6.716 15-15 15H15C6.716 30 0 23.284 0 15z"/><circle fill="#FFF" cx="15" cy="15" r="13"/></g>`;
        return nsvg.cloneNode(true);
    };

    // Dialog (Modal) - Poprawiony Z-INDEX dla Mobile
    const ndialog = function(title = 'KeepChatGPT', content = '', buttonvalue = 'OK', buttonfun = function(t) {return t;}, inputtype = 'br', inputvalue = '') {
        const ndivalert = document.createElement('div');
        ndivalert.style = "position: fixed; z-index: 1000000; top: 0; left: 0; width: 100%; height: 100%;";
        ndivalert.innerHTML = `
<div class="fixed inset-0 z-[100000] bg-black/50 dark:bg-black/80 flex items-center justify-center">
  <div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl w-[90%] max-w-lg overflow-hidden flex flex-col p-4">
      <div class="pb-2 border-b border-gray-200 dark:border-gray-700">
        <h2 class="text-lg font-bold text-gray-900 dark:text-gray-100">${title}</h2>
      </div>
      <div class="py-4">
        <p class="text-sm text-gray-600 dark:text-gray-300 whitespace-pre-wrap">${content}</p>
        <${inputtype} class="kdialoginput w-full mt-2 p-2 border rounded dark:bg-gray-700 dark:text-white" style="${inputtype === 'br' ? 'display: none' : ''}"></${inputtype}>
      </div>
      <div class="flex gap-2 justify-end">
          <button class="kdialogclose px-4 py-2 bg-gray-200 rounded text-black">Cancel</button>
          <button class="kdialogbtn px-4 py-2 bg-green-600 rounded text-white">${buttonvalue}</button>
      </div>
  </div>
</div>
        `;
        if (inputtype === 'textarea') {
            $(".kdialoginput", ndivalert).value = inputvalue;
            $(".kdialoginput", ndivalert).style.height = "100px";
        } else if (inputtype !== 'br') {
            $(".kdialoginput", ndivalert).value = inputvalue;
        }

        $(".kdialogclose", ndivalert).onclick = function() {
            ndivalert.remove();
        };
        $(".kdialogbtn", ndivalert).onclick = function() {
            buttonfun(ndivalert);
            ndivalert.remove();
        };
        document.body.appendChild(ndivalert);
    };

    const loadMenu = function() {
        if ($(".kmenu") !== null) {
            return;
        }
        const ndivmenu = document.createElement('div');
        ndivmenu.setAttribute("class", "kmenu");
        ndivmenu.innerHTML = `
<ul>
    <li id=nmenuid_af>${tl("调整间隔")}</li>
    <li id=nmenuid_ds>${tl("数据安全")}</li>
    <li id=nmenuid_cm>${tl("取消审计")}</li>
    <li id=nmenuid_ko>${tl("明察秋毫")}</li>
    <li id=nmenuid_cc>${tl("克隆对话")}</li>
    <li id=nmenuid_sc>${tl("言无不尽")}</li>
    <li id=nmenuid_pp>${tl("净化页面")}</li>
    <li id=nmenuid_ls>${tl("展示大屏")}</li>
    <li id=nmenuid_it>${tl("拦截跟踪")}</li>
    <li id=nmenuid_ec>${tl("日新月异")}</li>
    <li id=nmenuid_dm>${tl("暗色主题")}</li>
    <li id=nmenuid_sd>${tl("显示调试")}</li>
    <li id=nmenuid_cu>${tl("检查更新")}</li>
    <li id=nmenuid_ap>${tl("赞赏鼓励")}</li>
    <li id=nmenuid_ab>${tl("关于")}</li>
</ul>
`;
        // WAŻNE: Dodaj do BODY, nie do przycisku, żeby działało na mobile
        document.body.appendChild(ndivmenu);

        $('#nmenuid_cm').appendChild(ncheckbox());
        $('#nmenuid_ko').appendChild(ncheckbox());
        $('#nmenuid_cc').appendChild(ncheckbox());
        $('#nmenuid_sc').appendChild(ncheckbox());
        $('#nmenuid_pp').appendChild(ncheckbox());
        $('#nmenuid_ls').appendChild(ncheckbox());
        $('#nmenuid_it').appendChild(ncheckbox());
        $('#nmenuid_ec').appendChild(ncheckbox());
        $('#nmenuid_dm').appendChild(ncheckbox());
        $('#nmenuid_sd').appendChild(ncheckbox());

        // Helper do chowania menu po kliknięciu
        const hideMenu = () => {
             $(".kmenu").classList.remove('kshow');
             if($(".kmenu-backdrop")) $(".kmenu-backdrop").classList.remove('show');
        }

        $('#nmenuid_ds').onclick = function() {
            hideMenu();
            ndialog(`${tl("数据安全")}`, `${tl("使用正则编写规则")}`, `Save`, function(t) {
                let datasecblocklist;
                try {
                    datasecblocklist = `${$(".kdialoginput", t).value}\n`.replace(/\r/g,`\n`).replace(/\n+/g, `\n`);
                } catch (e) {
                    datasecblocklist = gv("k_datasecblocklist", datasec_blocklist_default);
                }
                sv("k_datasecblocklist", datasecblocklist);
            }, `textarea`, gv("k_datasecblocklist", datasec_blocklist_default));
        };

        $('#nmenuid_sd').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                $('#xcanwin').style.display = 'none';
                sv("k_showDebug", false);
            } else {
                $('#xcanwin').style.display = '';
                sv("k_showDebug", true);
            }
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_dm').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                $('body').classList.remove("kdark");
                sv("k_theme", "light");
            } else {
                $('body').classList.add("kdark");
                sv("k_theme", "dark");
            }
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_cm').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                sv("k_closeModer", false);
            } else {
                sv("k_closeModer", true);
            }
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_af').onclick = function() {
            hideMenu();
            ndialog(`${tl("调整间隔")}`, `${tl("建议间隔50秒")}`, `Go`, function(t) {
                try {
                    interval2Time = parseInt($(".kdialoginput", t).value);
                } catch (e) {
                    interval2Time = parseInt(gv("k_interval", 50));
                }
                if (interval2Time < 10) {
                    return;
                }
                clearInterval(nInterval2);
                nInterval2 = setInterval(nInterval2Fun, 1000 * interval2Time);
                sv("k_interval", interval2Time);
            }, `input`, parseInt(gv("k_interval", 50)));
        };

        $('#nmenuid_ko').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                $('body').classList.remove("kkeenobservation");
                sv("k_keenObservation", false);
            } else {
                $('body').classList.add("kkeenobservation");
                sv("k_keenObservation", true);
            }
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_cc').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                sv("k_clonechat", false);
                cloneChat(false);
            } else {
                sv("k_clonechat", true);
                cloneChat(true);
            }
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_pp').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                $('body').classList.remove("kpurifypage");
                sv("k_cleanlyhome", false);
            } else {
                $('body').classList.add("kpurifypage");
                purifyPage();
                sv("k_cleanlyhome", true);
            }
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_ls').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                sv("k_largescreen", false);
            } else {
                sv("k_largescreen", true);
            }
            $("main#main").classList.toggle('largescreen');
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_sc').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                sv("k_speakcompletely", false);
            } else {
                sv("k_speakcompletely", true);
            }
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_it').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                sv("k_intercepttracking", false);
                interceptTracking(false);
            } else {
                sv("k_intercepttracking", true);
                interceptTracking(true);
            }
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_ec').onclick = function() {
            if ($('.checkbutton', this).classList.contains('checked')) {
                sv("k_everchanging", false);
                everChanging(false);
            } else {
                sv("k_everchanging", true);
                everChanging(true);
            }
            $('.checkbutton', this).classList.toggle('checked');
        };

        $('#nmenuid_cu').onclick = function() {
            hideMenu();
            checkForUpdates();
        };

        $('#nmenuid_ap').onclick = function() {
            supportAuthor();
        };

        $('#nmenuid_ab').onclick = function() {
            window.open(GM_info.script.namespace, '_blank');
        };
    };

    const setUserOptions = function() {
        if (gv("k_showDebug", false) === true) {
            $('#nmenuid_sd .checkbutton').classList.add('checked');
            $('#xcanwin').style.display = '';
        } else {
            $('#xcanwin').style.display = 'none';
        }

        if (gv("k_theme", "light") === "dark") {
            $('#nmenuid_dm .checkbutton').classList.add('checked');
            $('body').classList.add("kdark");
        }

        if (gv("k_closeModer", false) === true) {
            $('#nmenuid_cm .checkbutton').classList.add('checked');
        }

        if (gv("k_keenObservation", true) === true) {
            $('#nmenuid_ko .checkbutton').classList.add('checked');
            $('body').classList.add("kkeenobservation");
        }

        if (gv("k_clonechat", false) === true) {
            $('#nmenuid_cc .checkbutton').classList.add('checked');
            cloneChat(true);
        }

        if (gv("k_cleanlyhome", false) === true) {
            $('#nmenuid_pp .checkbutton').classList.add('checked');
            purifyPage();
            $('body').classList.add("kpurifypage");
        }

        if (gv("k_largescreen", false) === true) {
            $('#nmenuid_ls .checkbutton').classList.add('checked');
            $("main#main").classList.add('largescreen');
        }

        if (gv("k_speakcompletely", false) === true) {
            $('#nmenuid_sc .checkbutton').classList.add('checked');
        }

        if (gv("k_intercepttracking", false) === true) {
            $('#nmenuid_it .checkbutton').classList.add('checked');
            interceptTracking(true);
        }

        if (gv("k_everchanging", false) === true) {
            $('#nmenuid_ec .checkbutton').classList.add('checked');
            everChanging(true);
        }

        //检查更新:首次、每3天
        if (gv("k_lastupdate", 0) === 0 || Date.now() - gv("k_lastupdate", 0) >= 1000 * 60 * 60 * 24 * 3) {
            sv("k_lastupdate", Date.now());
            checkForUpdates("auto");
        }

        if (gv("k_last_support_author", 0) === 0 || Date.now() - gv("k_last_support_author", 0) >= 1000 * 60 * 60 * 24 * 30) {
            sv("k_last_support_author", Date.now());
            supportAuthor();
        }
    };

    const toggleMenu = function(action) {
        // Ta funkcja jest używana przez Desktop, dla Mobile używamy innej logiki w listenerze
        const ndivmenu = $(".kmenu");
        if (action === "show") {
            ndivmenu.classList.remove('khide');
            if ($("#kcg")) {
                ndivmenu.style.left = `${$("#kcg").getBoundingClientRect().right + 20}px`;
                ndivmenu.style.top = `${$("#kcg").getBoundingClientRect().top}px`;
            }
        } else {
            ndivmenu.classList.add('khide');
        }
    };

    const loadKCG = function() {
        if ($("#kcg") !== null) {
            return;
        }
        setIfr(u);

        // --- MOBILE FIX: Backdrop ---
        if (!$(".kmenu-backdrop")) {
             const backdrop = document.createElement("div");
             backdrop.className = "kmenu-backdrop";
             backdrop.onclick = function() {
                 $(".kmenu").classList.remove('kshow');
                 this.classList.remove('show');
             };
             document.body.appendChild(backdrop);
        }

        const ndivkcg = document.createElement("div");
        ndivkcg.id = "kcg";
        ndivkcg.setAttribute("class", "flex py-3 px-3 items-center gap-3 rounded-md text-sm mb-1 flex-shrink-0 border border-white/20");

        const icon = GM_info.script.icon ? GM_info.script.icon : `${GM_info.script.namespace}raw/main/assets/logo.svg`;
        
        // --- MOBILE FIX: Button Logic ---
        const isMobile = window.innerWidth < 768;

        if (isMobile) {
            // Mobile: Tylko ikonka, wstrzyknięta do BODY
            ndivkcg.innerHTML = `<img src='${icon}' style='width: 100%; height: 100%; object-fit:contain;' />`;
            ndivkcg.classList.add('mobile-floating-btn'); // Klasa do styli
            document.body.appendChild(ndivkcg);
        } else {
            // Desktop: Oryginalna logika
            ndivkcg.innerHTML = `<img src='${icon}' style='width: 1rem;' /><div style='font-size: 0.8rem'>KeepChatGPT</div>`;
            if ($(symbol1_selector)) {
                let symbol_prt = $(symbol1_selector);
                symbol_prt.insertBefore(ndivkcg, symbol_prt.childNodes[0]);
            }
        }

        loadMenu();
        const ndivmenu = $(".kmenu");
        
        // --- MOBILE FIX: Click Handling ---
        ndivkcg.addEventListener('click', (e) => {
            e.stopPropagation(); // Ważne, żeby nie zamykało się od razu
            if (ndivmenu.classList.contains('kshow')) {
                ndivmenu.classList.remove('kshow');
                if(isMobile) $(".kmenu-backdrop").classList.remove('show');
            } else {
                ndivmenu.classList.add('kshow');
                if(isMobile) $(".kmenu-backdrop").classList.add('show');
                
                // Desktop positioning logic
                if (!isMobile) {
                     ndivmenu.style.left = `${$("#kcg").getBoundingClientRect().right + 20}px`;
                     ndivmenu.style.top = `${$("#kcg").getBoundingClientRect().top}px`;
                }
            }
        });

        if (!isMobile) {
            ndivmenu.addEventListener('mouseleave', () => {
                ndivmenu.classList.remove('kshow');
            });
        }

        document.documentElement.style.setProperty('--keenobservation-user-image-url', `url('${user_info.image_url}')`); 
        document.documentElement.style.setProperty('--keenobservation-assistant-image-url', `url('https://cdn.oaistatic.com/assets/favicon-180x180-od45eci6.webp')`); 
        addStyle();
        setUserOptions();
    };

    const addStyle = function() {
        GM_addStyle(`
/* --- STYLE PODSTAWOWE (ZACHOWANE) --- */

/* --- MOBILE FIXES (DODANE NA GÓRZE) --- */
@media screen and (max-width: 768px) {
    /* Pływający przycisk */
    #kcg.mobile-floating-btn {
        position: fixed !important;
        bottom: 150px !important;
        right: 20px !important;
        width: 48px !important;
        height: 48px !important;
        border-radius: 50% !important;
        background: white !important;
        box-shadow: 0 4px 15px rgba(0,0,0,0.3) !important;
        z-index: 2147483647 !important; /* MAX Z-INDEX */
        display: flex !important;
        justify-content: center;
        align-items: center;
        padding: 10px !important;
        margin: 0 !important;
        cursor: pointer !important;
    }
    .kdark #kcg.mobile-floating-btn {
        background: #222 !important;
        border: 1px solid #444 !important;
    }

    /* Menu na środku */
    .kmenu {
        position: fixed !important;
        top: 50% !important;
        left: 50% !important;
        transform: translate(-50%, -50%) !important;
        width: 85vw !important;
        max-width: 350px !important;
        max-height: 80vh !important;
        overflow-y: auto !important;
        background: white !important;
        z-index: 2147483647 !important; /* MAX Z-INDEX */
        box-shadow: 0 0 50px rgba(0,0,0,0.5) !important;
        border-radius: 12px !important;
        display: none;
    }
    .kdark .kmenu {
        background: #1a1a1a !important;
    }
    
    .kmenu.kshow {
        display: block !important;
    }

    /* Backdrop */
    .kmenu-backdrop {
        position: fixed !important;
        top: 0; left: 0; right: 0; bottom: 0;
        background: rgba(0,0,0,0.6);
        z-index: 2147483646 !important; /* Pod menu, nad resztą */
        display: none;
    }
    .kmenu-backdrop.show { display: block !important; }

    /* Fixy listy */
    .kmenu li {
        padding: 15px 20px !important;
        border-bottom: 1px solid #eee;
        font-size: 16px !important;
    }
}

/* --- RESZTA ORYGINALNYCH STYLI (BEZ ZMIAN LOGIKI) --- */

/*日星月异*/
.ever-changing nav.flex { background: linear-gradient(to right top, #d0dcff, #f0f0ff, #fff3f3); }
.ever-changing nav.flex .top-0 { background: linear-gradient(to top, #f0f0ff, #fff3f3); }
.ever-changing nav.flex aside { background: linear-gradient(to top, #efebff, #f0f0ff); }
.ever-changing nav.flex #history>div { height: 3.5rem; background-color: rgba(255, 255, 255, 0.4); }
.ever-changing nav.flex #history>div>a { mask-image: unset !important; }
.ever-changing nav.flex #history>div .bg-gradient-to-l { background-image: unset; }
.ever-changing nav.flex #history::after { content: ""; display: block; height: 1px; background: linear-gradient(to right, transparent, #bfbfbf, transparent); }
.ever-changing nav.flex #history>div.bg-token-sidebar-surface-tertiary { background-color: #bfcbfd; }
.ever-changing nav.flex #history>div:hover { background-color: #d5ddff; }
@layer utilities { .ever-changing .bg-token-bg-elevated-secondary { background-color: unset !important; background: linear-gradient(to top, #f4f6ff, #f3f3ff, #f4f6ff); } }
.ever-changing .navdate { font-size: 0.75rem; padding-right: 0.5rem; }

/* Dark Ever-changing */
.dark .ever-changing nav.flex { background: linear-gradient(to right top, #171717, #060606, #171717); }
.dark .ever-changing nav.flex .top-0 { background: linear-gradient(to top, #060606, #0f0f0f); }
.dark .ever-changing nav.flex aside { background: linear-gradient(to top, #111, #060606); }
.dark .ever-changing nav.flex #history>div { height: 3.5rem; background-color: rgba(111, 111, 111, 0.25); }
.dark .ever-changing nav.flex #history>div>a { mask-image: unset !important; }
.dark .ever-changing nav.flex #history>div .bg-gradient-to-l { background-image: unset; }
.dark .ever-changing nav.flex #history::after { content: ""; display: block; height: 1px; background: linear-gradient(to right, transparent, #535353, transparent); }
.dark .ever-changing nav.flex #history>div.bg-token-sidebar-surface-tertiary { background-color: #444; }
.dark .ever-changing nav.flex #history>div:hover { background-color: #2f2f2f; }
.dark .ever-changing nav.flex #history a .navtitle { color: #f4f4f4 !important; }
.dark .ever-changing nav.flex #history a .navlast { color: #d0d0d0 !important; }
@layer utilities { .dark .ever-changing .bg-token-bg-elevated-secondary { background-color: unset !important; background: linear-gradient(to top, #131313, #111, #131313); } }

/* Desktop Menu Styles */
.kmenu {
    background: linear-gradient(to top right, #C4F4FF, #E6E6FB, #FFF);
    color: #000000;
    border: 0.08rem solid #5252D9;
    border-radius: 0.625rem;
    box-shadow: 0 0.125rem 0.375rem rgba(0, 0, 0, 0.15);
    display: none;
    min-width: 12.5rem;
    padding: 0.75rem 0;
    position: absolute;
    z-index: 1000;
    font-weight: normal;
    font-size: 0.9rem;
    line-height: normal;
}
.kmenu li { display: flex; padding: 0.5rem 0.85rem; text-align: left; user-select: none; align-items: center; }
.kmenu li:hover { background-color: #c0caff; cursor: pointer; }

/* Dark Menu */
.kdark .kmenu { background: linear-gradient(to top right, #01000f, #00070d, #00194a); color: #FFFFFF; }
.kdark .kmenu li:hover { background-color: #383851; }

/* Inne style (oczyszczanie, obserwacja) */
.kpurifypage main .text-token-text-primary .mb-5.font-medium,
.kpurifypage form.w-full .grow .bottom-full,
.kpurifypage nav.flex .mb-4,
.kpurifypage main .text-token-text-primary .mx-3.items-stretch,
.kpurifypage main div.shadow-xxs,
.kpurifypage main form .text-token-text-secondary,
.kpurifypage main div.text-center>span,
.kpurifypage main [class*="aria-live=polite"] { display: none; }

.kkeenobservation main div[data-message-author-role="user"] { padding-right: 3rem; }
.kkeenobservation main div[data-message-author-role="user"]>div.w-full>div { background-color: #e1eaff; }
.kkeenobservation main div[data-message-author-role="user"]::after {
    content: ''; position: absolute; right: 0rem; width: 2rem; height: 2rem; background-color: gray;
    background-image: var(--keenobservation-user-image-url); background-size: contain; border-radius: 50%; pointer-events: auto;
}
.kkeenobservation main .text-token-text-primary .juice\\:flex-row-reverse .rounded-xl { padding-right: 2.5rem; }
.kkeenobservation main div[data-message-author-role="assistant"] { padding-left: 3.5rem; padding-right: 3.5rem; }
.kkeenobservation main div[data-message-author-role="assistant"]>div.w-full { align-items: flex-start; padding-top: 0; }
.kkeenobservation main div[data-message-author-role="assistant"]>div.w-full>div {
    max-width: 100%; border-radius: 1.5rem; padding: 0.75rem 1.25rem; background-color: var(--main-surface-secondary);
}
@layer utilities { .kkeenobservation .bg-token-sidebar-surface-primary { background-color: #eee; } }
.kkeenobservation main div[data-message-author-role="assistant"]::after {
    content: ''; position: absolute; left: 0rem; width: 2rem; height: 2rem; background-color: gray;
    background-image: var(--keenobservation-assistant-image-url); background-size: contain; border-radius: 50%; pointer-events: auto;
}
.dark .kkeenobservation main div[data-message-author-role="user"]>div.w-full>div { background-color: #525452; }
@layer utilities { .dark .kkeenobservation .bg-token-sidebar-surface-primary { background-color: #171717; } }

/* Checkbutton */
.checkbutton { height: 1.25rem; right: 0.85rem; position: absolute; }
.checkbutton:hover { cursor: pointer; }
.checked path { fill: #30D158; }
.checked circle { transform: translateX(14px); transition: transform 0.2s ease-in-out; }

/* Helpers */
.khide { display: none; }
.kshow { display: block; }
`);
    };

    const hookFetch = function() {
        unsafeWindow.fetch = new Proxy(fetch, {
            apply: function (target, thisArg, argumentsList) {
                let fetchReqUrl = '';
                let fetchReqOptions = {};
                if (typeof argumentsList[0] === 'string') {
                    fetchReqUrl = argumentsList[0];
                    fetchReqOptions = argumentsList[1];
                } else if (argumentsList[0] instanceof Request) {
                    fetchReqOptions = argumentsList[0];
                    fetchReqUrl = fetchReqOptions?.url;
                }
                const fetchReqMethod = fetchReqOptions?.method?.toUpperCase();
                let fetchRsp;
                try {
                    const block_url = 'gravatar\.com|browser-intake-datadoghq\.com|\.wp\.com|intercomcdn\.com|sentry\.io|sentry_key=|intercom\.io|featuregates\.org|/v1/initialize|/messenger/|statsigapi\.net|/rgstr|/v1/sdk_exception';
                    if (gv("k_closeModer", false) && fetchReqUrl.match('/backend-api/moderations(\\?|$)')) {
                        //取消审计1
                        fetchRsp = Promise.resolve({
                            json: () => {return {}}
                        });
                        return fetchRsp;
                    } else if (gv("k_closeModer", false) && fetchReqUrl.match('/backend-api/conversation(\\?|$)')) {
                        //取消审计2
                        const post_body = JSON.parse(argumentsList[1].body);
                        post_body.supports_modapi = false;
                        argumentsList[1].body = JSON.stringify(post_body);
                    } else if (gv("k_intercepttracking", false) && fetchReqUrl.match(block_url)) {
                        //拦截跟踪
                        console.log(`KeepChatGPT: ${tl("拦截跟踪")}: ${fetchReqUrl}`);
                        fetchRsp = Promise.resolve({
                        });
                        return fetchRsp;
                    } else if (fetchReqUrl.match('/backend-api/compliance')) {
                        fetchRsp = Promise.resolve({
                            json: () => {return {"registration_country":null,"require_cookie_consent":false,"terms_of_use":{"is_required":false,"display":null},"cookie_consent":null,"age_verification":null}}
                        });
                        return fetchRsp;
                    }
                } catch (e) {}
                fetchRsp = target.apply(thisArg, argumentsList);
                return fetchRsp.then(response => {
                    if (gv("k_everchanging", false) === true && fetchReqUrl.match('/backend-api/conversations\\?.*offset=')) {
                        return response.text().then(async fetchRspBody => {
                            let fetchRspBodyNew = fetchRspBody;
                            const b = JSON.parse(fetchRspBody).items;
                            let kec_object = {};
                            b.forEach(async el => {
                                const update_time = new Date(el.update_time);
                                const ec_tmp = await global.st_ec.get(el.id) || {};
                                await global.st_ec.put({id: el.id, title: el.title, update_time: update_time, last: ec_tmp.last, model: ec_tmp.model});
                                kec_object[el.id] = {title: el.title, update_time: update_time, last: ec_tmp.last, model: ec_tmp.model};
                            });
                            setTimeout(function() {
                                attachDate(kec_object);
                            }, 1000);
                            return Promise.resolve(new Response(fetchRspBodyNew, {status: response.status, statusText: response.statusText, headers: response.headers}));
                        });
                    } else if (gv("k_everchanging", false) === true && fetchReqUrl.match('/backend-api/conversation/(([^/]{4,}?){4}-[^/]{4,}?)(\\?|$)(\\?|$)')) {
                        return response.text().then(async fetchRspBody => {
                            let fetchRspBodyNew = fetchRspBody;
                            if (fetchReqMethod === 'GET') {
                                const f = JSON.parse(fetchRspBody);
                                const crt_con_id = f && f.conversation_id;
                                const crt_con_title = f && f.title;
                                let crt_con_update_time = f && f.update_time;
                                crt_con_update_time = crt_con_update_time < 10**10 ? crt_con_update_time * 1000 : crt_con_update_time;
                                crt_con_update_time = new Date(crt_con_update_time);
                                const crt_con_speak_last_keys = f && f.mapping && Object.keys(f.mapping);
                                const crt_con_speak_last_id = f.current_node;
                                const crt_con_speak_last = f.mapping[crt_con_speak_last_id].message;
                                const crt_con_last = crt_con_speak_last.content.parts[0].trim().replace(/[\r\n]/g, ``).substr(0, 100);
                                const crt_con_model = crt_con_speak_last.metadata.model_slug;
                                await global.st_ec.put({id: crt_con_id, title: crt_con_title, update_time: crt_con_update_time, last: crt_con_last, model: crt_con_model});
                                let kec_object = {};
                                kec_object[crt_con_id] = {title: crt_con_title, update_time: crt_con_update_time, last: crt_con_last, model: crt_con_model};
                                setTimeout(function() {
                                    attachDate(kec_object);
                                }, 300);
                            } else if (fetchReqMethod === 'PATCH') {
                                const f = JSON.parse(fetchRspBody);
                                const crt_con_id = fetchReqUrl.match('/backend-api/conversation/(([^/]{4,}?){4}-[^/]{4,}?)(\\?|$)')[1];
                                const is_visible = f && f.is_visible;
                                if (is_visible) {
                                    await global.st_ec.delete({id: crt_con_id});
                                }
                            }
                            return Promise.resolve(new Response(fetchRspBodyNew, {status: response.status, statusText: response.statusText, headers: response.headers}));
                        });
                    }
                    return response;
                }).catch(error => {
                    return Promise.reject(error);
                });
            }
        });
        navigator.sendBeacon = function(url, data) {};
    };

    const everChanging = function(action) {
        if (action === true) {
            $('nav.flex')?.classList.add('knav');
            $("body").classList.add("ever-changing");
            attachDate();
        } else {
            $("body").classList.remove("ever-changing");
        }
    };

    const attachDate = function(kec_object) {
        $$('nav.flex #history a').forEach(async el => {
            let a_id;
            const a_id_m = el.href.match('/(([^/]{4,}?){4}-[^/]{4,}?)(\\?|$)(\\?|$)');
            if (a_id_m) {
                a_id = a_id_m[1];
            } else {
                return;
            }
            let kec_obj_el;
            if (kec_object) {
                kec_obj_el = kec_object[a_id];
            } else {
                if (global.st_ec) {
                    kec_obj_el = await global.st_ec.get(a_id);
                } else {
                    kec_obj_el = {};
                }
            }
            const title = kec_obj_el && kec_obj_el.title || "";
            const update_time = kec_obj_el && kec_obj_el.update_time || "";
            const last = kec_obj_el && kec_obj_el.last || "";

            if (!title || !update_time) return;
            if (!$('.navtitle', el) || !$('.navdate', el) || !$('.navlast', el)) {
                const cdiv_old = $(`.flex.min-w-0.grow.items-center`, el);
                if(cdiv_old) cdiv_old.style.display = "none";
                const cdiv_new = document.createElement("div");
                cdiv_new.className = `flex-1 text-ellipsis overflow-hidden break-all relative`;
                cdiv_new.innerHTML = `
<div style="max-height: unset; max-width: 70%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; position: absolute; color: #000000; font-weight: bold;" class="navtitle">
    ${title}
</div>
<div style="right: 0; position: absolute; color: gray; font-size: 0.71rem;" class="navdate">
    ${formatDate2(update_time)}
</div>
<br>
<div style="max-height: unset; max-width: 95%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #606060; font-size: 0.75rem;" class="navlast">
    ${htmlEncode(last)}
</div>
`;
                el.insertBefore(cdiv_new, el.childNodes[1]);
            } else if ($('.navtitle', el).innerHTML !== title || $('.navdate', el).innerHTML !== formatDate2(update_time) || $('.navlast', el).innerHTML !== last) {
                $('.navtitle', el).innerHTML = title;
                $('.navdate', el).innerHTML = formatDate2(update_time);
                $('.navlast', el).innerHTML = htmlEncode(last);
            }
        });

        const sidebar_chat = $("nav.flex div.overflow-y-auto");
        if (sidebar_chat) {
            if (sidebar_chat.scrollHeight > sidebar_chat.clientHeight) {
                sidebar_chat.classList.add("-mr-2");
            } else {
                sidebar_chat.classList.remove("-mr-2");
            }
        }
    };

    const verInt = function(vs) {
        const vl = vs.split('.');
        let vi = 0;
        for (let i = 0; i < vl.length && i < 3; i++) {
            vi += parseInt(vl[i]) * (1000 ** (2 - i));
        }
        return vi;
    };

    const checkForUpdates = function(action = "click") {
        const downloadURL = `https://raw.githubusercontent.com/xcanwin/KeepChatGPT/main/KeepChatGPT.user.js`;
        GM_xmlhttpRequest({
            method: "GET",
            url: `${downloadURL}?t=${Date.now()}`,
            onload: function(response) {
                const crv = GM_info.script.version;
                const m = response.responseText.match(/@version\s+(\S+)/);
                const ltv = m && m[1];
                if (ltv && verInt(ltv) > verInt(crv)) {
                    ndialog(`${tl("检查更新")}`, `${tl("当前版本")}: ${crv}, ${tl("发现最新版")}: ${ltv}`, `UPDATE`, function(t) {
                        window.open(`${downloadURL}?t=${Date.now()}`, '_blank');
                    });
                } else {
                    if (action === "click") {
                        ndialog(`${tl("检查更新")}`, `${tl("当前版本")}: ${crv}, ${tl("已是最新版")}`, `OK`);
                    }
                }
            }
        });
    };

    const cloneChat = function(action) {
        cloneChat.firstTarget = null;
        if (action === true) {
            window.addEventListener('click', cloneChat.listen_Click);
        } else {
            window.removeEventListener('click', cloneChat.listen_Click);
        }
    };

    cloneChat.listen_Click = function(event) {
        event.stopPropagation();
        const clickedElement = document.elementFromPoint(event.clientX, event.clientY);
        if (clickedElement && clickedElement.matches('main div[data-message-author-role="user"]')) {
            const rect = clickedElement.getBoundingClientRect();
            const logoWidth = 32;
            const logoHeight = 32;
            const logoRight = rect.right;
            const logoLeft = rect.right - logoWidth;
            const logoTop = rect.top;
            const logoBottom = rect.top + logoHeight;

            if (event.clientX >= logoLeft && event.clientX <= logoRight && event.clientY >= logoTop && event.clientY <= logoBottom) {
                const content = $('.whitespace-pre-wrap', event.target).innerHTML.trim();
                const content_ProseMirror = content.split(/\n/).map(line => `<p>${line}</p>`).join('');
                const inputArea = $("form.w-full #prompt-textarea");
                if(inputArea) {
                    inputArea.innerHTML = '';
                    inputArea.focus();
                    document.execCommand('insertHTML', false, content_ProseMirror);
                }
            }
        }
    };

    const purifyPage = function() {
        if (location.href.match(/https:\/\/(chatgpt\.com|chat\.openai\.com)\/\??/)) {
            if ($("main h1") && $("main h1").innerText.match(/^ChatGPT(\nPLUS)?$/)) {
                $("main h1").classList.add('text-gray-200');
                const nSpan = document.createElement('span');
                nSpan.className = 'bg-yellow-200 text-yellow-900 py-0.5 px-1.5 text-xs md:text-sm rounded-md uppercase';
                nSpan.textContent = `KEEP`;
                $("main h1").appendChild(nSpan);
            }
        }
    };

    const speakCompletely = function() {
        if (gv("k_speakcompletely", false) === true) {
            const continue_svg_selector = `form.w-full .justify-center svg path[d*="M4.47189 2.5C5.02418 2.5 5.47189 2.94772 5.47189 3.5V5.07196C7.17062 3.47759 9.45672 2.5 11.9719 2.5C17.2186 2.5 21.4719 6.75329 21.4719 12C21.4719 17.2467 17.2186 21.5 11.9719 21.5C7.10259 21.5 3.09017 17.8375 2.53689 13.1164C2.47261 12.5679 2.86517"]:not(.ct_clicked)`;
            if ($(continue_svg_selector)) {
                setTimeout(function() {
                    let btn = fp(`button`, $(continue_svg_selector));
                    if(btn) btn.click();
                    $(continue_svg_selector)?.classList.add('ct_clicked');
                }, 1000);
            }
        }
    };

    const dataSec = function() {
        muob("form.w-full #prompt-textarea", $(`body`), () => {
            if (gv("k_datasecblocklist", datasec_blocklist_default)) {
                $("form.w-full #prompt-textarea")?.addEventListener('input', dataSec.listen_input);
                $("form.w-full #prompt-textarea")?.addEventListener('paste', dataSec.listen_input);
            } else {
                $("form.w-full #prompt-textarea")?.removeEventListener('input', dataSec.listen_input);
                $("form.w-full #prompt-textarea")?.removeEventListener('paste', dataSec.listen_input);
            }
        });
    };

    dataSec.listen_input = function(event) {
        let ms = [];
        gv("k_datasecblocklist", datasec_blocklist_default).split(`\n`).forEach(e => {
            if (e) {
                const m = $("form.w-full #prompt-textarea").innerHTML.match(e);
                if (m && m[0]) {
                    $("form.w-full #prompt-textarea").innerHTML = $("form.w-full #prompt-textarea").innerHTML.replaceAll(m[0], ``);
                    ms.push(m[0]);
                }
            }
        });
        if (ms.join(`\n`).trim()) {
            ndialog(`⚠️${tl("警告")}`, `${tl("发现敏感数据")}`, `Thanks`, function(t) {}, `textarea`, ms.join(`\n`));
        }
    };

    const supportAuthor = function() {
        ndialog(`${tl("赞赏鼓励")}`, `· 本项目由兴趣驱使,提升自己的体验,并共享世界。
<br>· 如果你喜欢作者的项目,可以给作者一个免费的Star或者Follow。
<br>· 如果你希望作者的小猫吃到更好的罐头,欢迎赞赏与激励。`, `更多鼓励方式`, function(t) {
            window.open(`${GM_info.script.namespace}#赞赏`, '_blank');
        }, `img`, `https://github.com/xcanwin/KeepChatGPT/raw/main/assets/appreciate_wechat.png`);
    }

    const interceptTracking = function(action) {
        if (action === true) {
            window.addEventListener('beforescriptexecute', interceptTracking.listen_beforescriptexecute);
        } else {
            window.removeEventListener('beforescriptexecute', interceptTracking.listen_beforescriptexecute);
        }
    };

    interceptTracking.listen_beforescriptexecute = function(event) {
        const scriptElement = event.target;
        if (scriptElement.src.match('widget\.intercom\.io')) {
            event.preventDefault();
            scriptElement.textContent = ``;
            scriptElement.remove();
        }
    };

    const fp = function(parentSelector, el, level = 5) {
        if (el === null) {
            return null;
        }
        let parent = el.parentNode;
        let count = 1;
        while (parent && count <= level) {
            if (parent && parent.constructor !== HTMLDocument && parent.matches(parentSelector)) {
                return parent;
            }
            parent = parent.parentNode;
            count++;
        }
        return null;
    };

    const byebyeCF = () => {
        GM_cookie.delete({
            name: "cf_clearance",
            domain: ".chatgpt.com",
            path: "/"
        });
    };

    const nInterval1Fun = function() {
        byebyeCF();
        if ($(symbol1_selector) || $(symbol2_selector) || window.innerWidth < 768) {
            setIfr();
            speakCompletely();
        }
    };

    const nInterval2Fun = function() {
        if ($(symbol1_selector) || $(symbol2_selector) || window.innerWidth < 768) {
            keepChat();
        }
    };

    /* 基础数据库 */
    const userInfo = () => {
        const user_info = {
            email: `default`,
            image_url: ``,
        };
        for (const s of $$('script')) {
            const match = s.textContent?.match(/\\"email\\",\\"(.*?)\\"/);
            if (match) {
                user_info.email = match[1];
            }
            const match2 = s.textContent?.match(/\\"picture\\",\\"(.*?)\\"/);
            if (match2) {
                user_info.image_url = match2[1]?.replaceAll('\\u0026', '&');
            }
        }
        global.st_ec = new IndexedDB(`KeepChatGPT_${user_info.email}`, 'conversations');
        return user_info;
    };

    const blockStorageDialog = () => {
        if (navigator.storage && navigator.storage.persist) {
            navigator.storage.persist = () => Promise.resolve(false);
        }
    };

    const user_info = userInfo();

    // Główny Start
    loadKCG();
    setIfr();
    // Nasłuchiwanie na wypadek zmian w DOM (Single Page App)
    muob('body', $('body'), () => {
         loadKCG();
    });

    blockStorageDialog();
    hookFetch();
    dataSec();

    let nInterval1 = setInterval(nInterval1Fun, 300);
    let interval2Time = parseInt(gv("k_interval", 50));
    let nInterval2 = setInterval(nInterval2Fun, 1000 * interval2Time);

})();