Y-Klase

Bypasses Ģimenes Komplekts

// ==UserScript==
// @name         Y-Klase
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Bypasses Ģimenes Komplekts
// @author       ralfszeltins2
// @match        https://family.e-klase.lv/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    console.log('[Y-Klase] Made by ralfszeltins2');

    function showBlockedRequestPopup() {
        if (document.getElementById('y-klase-blocked-popup')) {
            return;
        }

        const popup = document.createElement('div');
        popup.id = 'y-klase-blocked-popup';
        popup.style.cssText = `
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background-color: #dc2626;
            color: white;
            padding: 16px 24px;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            z-index: 999999;
            font-family: Arial, sans-serif;
            font-size: 14px;
            max-width: 90%;
            text-align: center;
        `;

        popup.innerHTML = `
            <div style="position: relative;">
                <button id="y-klase-close-popup" style="position: absolute; top: -8px; right: -16px; background: none; border: none; color: white; font-size: 20px; cursor: pointer; padding: 0; width: 24px; height: 24px; line-height: 20px;">×</button>
                <div>
                    Šī funkcija diemžēl nav pieejama! Pārejat uz Z-Klasi, lejupielādējiet to
                    <a href="https://z-klase.ddns.net/download" style="color: white; text-decoration: underline; font-weight: bold;" target="_blank">šeit</a>.
                </div>
            </div>
        `;

        document.body.appendChild(popup);

        const closeBtn = document.getElementById('y-klase-close-popup');
        const closePopup = () => popup.remove();
        closeBtn.addEventListener('click', closePopup);

        const timeout = setTimeout(closePopup, 10000);

        closeBtn.addEventListener('click', () => clearTimeout(timeout), { once: true });
    }

    const updateTitle = () => {
        if (document.title !== 'Y-Klase') {
            document.title = 'Y-Klase';
        }
    };

    updateTitle();

    document.addEventListener('DOMContentLoaded', updateTitle);
    setInterval(updateTitle, 100);

    const titleObserver = new MutationObserver(() => {
        updateTitle();
    });

    if (document.querySelector('title')) {
        titleObserver.observe(document.querySelector('title'), {
            childList: true,
            characterData: true,
            subtree: true
        });
    }

    const headObserver = new MutationObserver(() => {
        const titleEl = document.querySelector('title');
        if (titleEl && !titleEl.dataset.observed) {
            titleEl.dataset.observed = 'true';
            titleObserver.observe(titleEl, {
                childList: true,
                characterData: true,
                subtree: true
            });
            updateTitle();
        }
    });

    if (document.head) {
        headObserver.observe(document.head, { childList: true });
    } else {
        document.addEventListener('DOMContentLoaded', () => {
            if (document.head) {
                headObserver.observe(document.head, { childList: true });
            }
        });
    }

    function modifyUserData(data) {
        if (!data) return data;

        if (data.premiumSubscription) {
            data.premiumSubscription.isActive = true;
            data.premiumSubscription.hasDiscount = true;
            data.premiumSubscription.isActiveTillEndOfYear = true;
            if ('expiresAt' in data.premiumSubscription) {
                data.premiumSubscription.expiresAt = '2099-12-31T23:59:59Z';
            }
        }

        if (data.userSettings) {
            data.userSettings.isCommercialNewsSubscriber = false;
            data.userSettings.isAdvertisingEnabled = false;
        }

        return data;
    }

    function modifyProfilesData(data) {
        if (!data) return data;
        return data;
    }

    function modifyLinksData(data) {
        if (!data) return data;

        const zKlaseLink = {
            "name": "=== Lejupielādēt Z-Klasi ===",
            "url": "https://z-klase.ddns.net/download",
            "isSingleSignOn": false
        };

        if (data.remoteSystemsLinks && Array.isArray(data.remoteSystemsLinks)) {
            data.remoteSystemsLinks = data.remoteSystemsLinks.filter(link =>
                link.name !== "=== Lejupielādēt Z-Klasi ==="
            );
            data.remoteSystemsLinks.unshift(zKlaseLink);
        }

        if (data.schoolLinks && Array.isArray(data.schoolLinks)) {
            data.schoolLinks = data.schoolLinks.filter(link =>
                link.name !== "=== Lejupielādēt Z-Klasi ==="
            );
            data.schoolLinks.unshift({...zKlaseLink});
        }

        return data;
    }

    const XHR = XMLHttpRequest.prototype;
    const originalOpen = XHR.open;
    const originalSend = XHR.send;

    XHR.open = function(method, url) {
        this._url = url;
        this._method = method;
        return originalOpen.apply(this, arguments);
    };

    XHR.send = function(body) {
        const xhr = this;
        const url = xhr._url || '';

        if (url.includes('/api/user-settings/ads') || url.includes('/api/user-settings/commercial-news')) {
            showBlockedRequestPopup();
            return;
        }

        const shouldIntercept = url.includes('/api/user') || url.includes('/api/links');

        if (shouldIntercept) {
            const originalResponseGetter = Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, 'response').get;
            const originalResponseTextGetter = Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, 'responseText').get;
            const originalOnReadyStateChange = xhr.onreadystatechange;

            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    try {
                        let data;
                        let originalResponse = originalResponseGetter.call(xhr);

                        if (xhr.responseType === 'arraybuffer' || originalResponse instanceof ArrayBuffer) {
                            const text = new TextDecoder().decode(originalResponse);
                            data = JSON.parse(text);

                            let modified = data;
                            if (url.includes('/api/user/profiles')) {
                                modified = modifyProfilesData(data);
                            } else if (url.includes('/api/links')) {
                                modified = modifyLinksData(data);
                            } else if (url.includes('/api/user')) {
                                modified = modifyUserData(data);
                            }

                            const modifiedText = JSON.stringify(modified);
                            const modifiedBuffer = new TextEncoder().encode(modifiedText).buffer;

                            Object.defineProperty(xhr, 'response', {
                                get: () => modifiedBuffer,
                                configurable: true
                            });
                        } else if (xhr.responseType === 'json') {
                            data = originalResponse;

                            let modified = data;
                            if (url.includes('/api/user/profiles')) {
                                modified = modifyProfilesData(data);
                            } else if (url.includes('/api/links')) {
                                modified = modifyLinksData(data);
                            } else if (url.includes('/api/user')) {
                                modified = modifyUserData(data);
                            }

                            Object.defineProperty(xhr, 'response', {
                                get: () => modified,
                                configurable: true
                            });
                        } else {
                            const text = originalResponseTextGetter.call(xhr);
                            data = JSON.parse(text);

                            let modified = data;
                            if (url.includes('/api/user/profiles')) {
                                modified = modifyProfilesData(data);
                            } else if (url.includes('/api/links')) {
                                modified = modifyLinksData(data);
                            } else if (url.includes('/api/user')) {
                                modified = modifyUserData(data);
                            }

                            const modifiedText = JSON.stringify(modified);

                            Object.defineProperty(xhr, 'responseText', {
                                get: () => modifiedText,
                                configurable: true
                            });

                            Object.defineProperty(xhr, 'response', {
                                get: () => modifiedText,
                                configurable: true
                            });
                        }
                    } catch (err) {
                    }
                }

                if (originalOnReadyStateChange) {
                    originalOnReadyStateChange.apply(this, arguments);
                }
            };
        }

        return originalSend.apply(this, arguments);
    };

    const originalFetch = window.fetch;
    window.fetch = async function(...args) {
        const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || '';

        if (url.includes('/api/user-settings/ads') || url.includes('/api/user-settings/commercial-news')) {
            showBlockedRequestPopup();
            return new Response('{}', {
                status: 200,
                statusText: 'OK',
                headers: { 'Content-Type': 'application/json' }
            });
        }

        const shouldIntercept = url.includes('/api/user') || url.includes('/api/links');

        if (shouldIntercept) {
            try {
                const response = await originalFetch.apply(this, args);
                const clone = response.clone();
                const text = await clone.text();

                try {
                    const data = JSON.parse(text);

                    let modified = data;
                    if (url.includes('/api/user/profiles')) {
                        modified = modifyProfilesData(data);
                    } else if (url.includes('/api/links')) {
                        modified = modifyLinksData(data);
                    } else if (url.includes('/api/user')) {
                        modified = modifyUserData(data);
                    }

                    const modifiedText = JSON.stringify(modified);

                    return new Response(modifiedText, {
                        status: response.status,
                        statusText: response.statusText,
                        headers: response.headers
                    });
                } catch (parseErr) {
                    return response;
                }
            } catch (err) {
                return originalFetch.apply(this, args);
            }
        }

        return originalFetch.apply(this, args);
    };
})();